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} xsHeader Header at Bootsrap Extra Small width (default for all)
8035 * @cfg {String} smHeader Header at Bootsrap Small width
8038 * @cfg {String} mdHeader Header at Bootsrap Medium width
8041 * @cfg {String} lgHeader Header at Bootsrap Large width
8044 * @cfg {String} xlHeader Header at Bootsrap extra Large width
8047 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
8048 * {@link Roo.data.Record} definition from which to draw the column's value. If not
8049 * specified, the column's index is used as an index into the Record's data Array.
8052 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
8053 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8056 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
8057 * Defaults to the value of the {@link #defaultSortable} property.
8058 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8061 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
8064 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
8067 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
8070 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
8073 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
8074 * given the cell's data value. See {@link #setRenderer}. If not specified, the
8075 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8076 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8079 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
8082 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
8085 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
8088 * @cfg {String} cursor (Optional)
8091 * @cfg {String} tooltip (Optional)
8094 * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
8097 * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
8100 * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
8103 * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
8106 * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
8109 * Returns the id of the column at the specified index.
8110 * @param {Number} index The column index
8111 * @return {String} the id
8113 getColumnId : function(index){
8114 return this.config[index].id;
8118 * Returns the column for a specified id.
8119 * @param {String} id The column id
8120 * @return {Object} the column
8122 getColumnById : function(id){
8123 return this.lookup[id];
8128 * Returns the column Object for a specified dataIndex.
8129 * @param {String} dataIndex The column dataIndex
8130 * @return {Object|Boolean} the column or false if not found
8132 getColumnByDataIndex: function(dataIndex){
8133 var index = this.findColumnIndex(dataIndex);
8134 return index > -1 ? this.config[index] : false;
8138 * Returns the index for a specified column id.
8139 * @param {String} id The column id
8140 * @return {Number} the index, or -1 if not found
8142 getIndexById : function(id){
8143 for(var i = 0, len = this.config.length; i < len; i++){
8144 if(this.config[i].id == id){
8152 * Returns the index for a specified column dataIndex.
8153 * @param {String} dataIndex The column dataIndex
8154 * @return {Number} the index, or -1 if not found
8157 findColumnIndex : function(dataIndex){
8158 for(var i = 0, len = this.config.length; i < len; i++){
8159 if(this.config[i].dataIndex == dataIndex){
8167 moveColumn : function(oldIndex, newIndex){
8168 var c = this.config[oldIndex];
8169 this.config.splice(oldIndex, 1);
8170 this.config.splice(newIndex, 0, c);
8171 this.dataMap = null;
8172 this.fireEvent("columnmoved", this, oldIndex, newIndex);
8175 isLocked : function(colIndex){
8176 return this.config[colIndex].locked === true;
8179 setLocked : function(colIndex, value, suppressEvent){
8180 if(this.isLocked(colIndex) == value){
8183 this.config[colIndex].locked = value;
8185 this.fireEvent("columnlockchange", this, colIndex, value);
8189 getTotalLockedWidth : function(){
8191 for(var i = 0; i < this.config.length; i++){
8192 if(this.isLocked(i) && !this.isHidden(i)){
8193 this.totalWidth += this.getColumnWidth(i);
8199 getLockedCount : function(){
8200 for(var i = 0, len = this.config.length; i < len; i++){
8201 if(!this.isLocked(i)){
8206 return this.config.length;
8210 * Returns the number of columns.
8213 getColumnCount : function(visibleOnly){
8214 if(visibleOnly === true){
8216 for(var i = 0, len = this.config.length; i < len; i++){
8217 if(!this.isHidden(i)){
8223 return this.config.length;
8227 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8228 * @param {Function} fn
8229 * @param {Object} scope (optional)
8230 * @return {Array} result
8232 getColumnsBy : function(fn, scope){
8234 for(var i = 0, len = this.config.length; i < len; i++){
8235 var c = this.config[i];
8236 if(fn.call(scope||this, c, i) === true){
8244 * Returns true if the specified column is sortable.
8245 * @param {Number} col The column index
8248 isSortable : function(col){
8249 if(typeof this.config[col].sortable == "undefined"){
8250 return this.defaultSortable;
8252 return this.config[col].sortable;
8256 * Returns the rendering (formatting) function defined for the column.
8257 * @param {Number} col The column index.
8258 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8260 getRenderer : function(col){
8261 if(!this.config[col].renderer){
8262 return Roo.grid.ColumnModel.defaultRenderer;
8264 return this.config[col].renderer;
8268 * Sets the rendering (formatting) function for a column.
8269 * @param {Number} col The column index
8270 * @param {Function} fn The function to use to process the cell's raw data
8271 * to return HTML markup for the grid view. The render function is called with
8272 * the following parameters:<ul>
8273 * <li>Data value.</li>
8274 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8275 * <li>css A CSS style string to apply to the table cell.</li>
8276 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8277 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8278 * <li>Row index</li>
8279 * <li>Column index</li>
8280 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8282 setRenderer : function(col, fn){
8283 this.config[col].renderer = fn;
8287 * Returns the width for the specified column.
8288 * @param {Number} col The column index
8289 * @param (optional) {String} gridSize bootstrap width size.
8292 getColumnWidth : function(col, gridSize)
8294 var cfg = this.config[col];
8296 if (typeof(gridSize) == 'undefined') {
8297 return cfg.width * 1 || this.defaultWidth;
8299 for(var i in ['xl', 'lg', 'md', 'sm', 'xs']) {
8300 if (typeof(cfg[i]) == 'undefined') {
8310 * Sets the width for a column.
8311 * @param {Number} col The column index
8312 * @param {Number} width The new width
8314 setColumnWidth : function(col, width, suppressEvent){
8315 this.config[col].width = width;
8316 this.totalWidth = null;
8318 this.fireEvent("widthchange", this, col, width);
8323 * Returns the total width of all columns.
8324 * @param {Boolean} includeHidden True to include hidden column widths
8327 getTotalWidth : function(includeHidden){
8328 if(!this.totalWidth){
8329 this.totalWidth = 0;
8330 for(var i = 0, len = this.config.length; i < len; i++){
8331 if(includeHidden || !this.isHidden(i)){
8332 this.totalWidth += this.getColumnWidth(i);
8336 return this.totalWidth;
8340 * Returns the header for the specified column.
8341 * @param {Number} col The column index
8344 getColumnHeader : function(col){
8345 return this.config[col].header;
8349 * Sets the header for a column.
8350 * @param {Number} col The column index
8351 * @param {String} header The new header
8353 setColumnHeader : function(col, header){
8354 this.config[col].header = header;
8355 this.fireEvent("headerchange", this, col, header);
8359 * Returns the tooltip for the specified column.
8360 * @param {Number} col The column index
8363 getColumnTooltip : function(col){
8364 return this.config[col].tooltip;
8367 * Sets the tooltip for a column.
8368 * @param {Number} col The column index
8369 * @param {String} tooltip The new tooltip
8371 setColumnTooltip : function(col, tooltip){
8372 this.config[col].tooltip = tooltip;
8376 * Returns the dataIndex for the specified column.
8377 * @param {Number} col The column index
8380 getDataIndex : function(col){
8381 return this.config[col].dataIndex;
8385 * Sets the dataIndex for a column.
8386 * @param {Number} col The column index
8387 * @param {Number} dataIndex The new dataIndex
8389 setDataIndex : function(col, dataIndex){
8390 this.config[col].dataIndex = dataIndex;
8396 * Returns true if the cell is editable.
8397 * @param {Number} colIndex The column index
8398 * @param {Number} rowIndex The row index - this is nto actually used..?
8401 isCellEditable : function(colIndex, rowIndex){
8402 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8406 * Returns the editor defined for the cell/column.
8407 * return false or null to disable editing.
8408 * @param {Number} colIndex The column index
8409 * @param {Number} rowIndex The row index
8412 getCellEditor : function(colIndex, rowIndex){
8413 return this.config[colIndex].editor;
8417 * Sets if a column is editable.
8418 * @param {Number} col The column index
8419 * @param {Boolean} editable True if the column is editable
8421 setEditable : function(col, editable){
8422 this.config[col].editable = editable;
8427 * Returns true if the column is hidden.
8428 * @param {Number} colIndex The column index
8431 isHidden : function(colIndex){
8432 return this.config[colIndex].hidden;
8437 * Returns true if the column width cannot be changed
8439 isFixed : function(colIndex){
8440 return this.config[colIndex].fixed;
8444 * Returns true if the column can be resized
8447 isResizable : function(colIndex){
8448 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8451 * Sets if a column is hidden.
8452 * @param {Number} colIndex The column index
8453 * @param {Boolean} hidden True if the column is hidden
8455 setHidden : function(colIndex, hidden){
8456 this.config[colIndex].hidden = hidden;
8457 this.totalWidth = null;
8458 this.fireEvent("hiddenchange", this, colIndex, hidden);
8462 * Sets the editor for a column.
8463 * @param {Number} col The column index
8464 * @param {Object} editor The editor object
8466 setEditor : function(col, editor){
8467 this.config[col].editor = editor;
8470 * Add a column (experimental...) - defaults to adding to the end..
8471 * @param {Object} config
8473 addColumn : function(c)
8476 var i = this.config.length;
8479 if(typeof c.dataIndex == "undefined"){
8482 if(typeof c.renderer == "string"){
8483 c.renderer = Roo.util.Format[c.renderer];
8485 if(typeof c.id == "undefined"){
8488 if(c.editor && c.editor.xtype){
8489 c.editor = Roo.factory(c.editor, Roo.grid);
8491 if(c.editor && c.editor.isFormField){
8492 c.editor = new Roo.grid.GridEditor(c.editor);
8494 this.lookup[c.id] = c;
8499 Roo.grid.ColumnModel.defaultRenderer = function(value)
8501 if(typeof value == "object") {
8504 if(typeof value == "string" && value.length < 1){
8508 return String.format("{0}", value);
8511 // Alias for backwards compatibility
8512 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8515 * Ext JS Library 1.1.1
8516 * Copyright(c) 2006-2007, Ext JS, LLC.
8518 * Originally Released Under LGPL - original licence link has changed is not relivant.
8521 * <script type="text/javascript">
8525 * @class Roo.LoadMask
8526 * A simple utility class for generically masking elements while loading data. If the element being masked has
8527 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8528 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
8529 * element's UpdateManager load indicator and will be destroyed after the initial load.
8531 * Create a new LoadMask
8532 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8533 * @param {Object} config The config object
8535 Roo.LoadMask = function(el, config){
8536 this.el = Roo.get(el);
8537 Roo.apply(this, config);
8539 this.store.on('beforeload', this.onBeforeLoad, this);
8540 this.store.on('load', this.onLoad, this);
8541 this.store.on('loadexception', this.onLoadException, this);
8542 this.removeMask = false;
8544 var um = this.el.getUpdateManager();
8545 um.showLoadIndicator = false; // disable the default indicator
8546 um.on('beforeupdate', this.onBeforeLoad, this);
8547 um.on('update', this.onLoad, this);
8548 um.on('failure', this.onLoad, this);
8549 this.removeMask = true;
8553 Roo.LoadMask.prototype = {
8555 * @cfg {Boolean} removeMask
8556 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
8557 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
8561 * The text to display in a centered loading message box (defaults to 'Loading...')
8565 * @cfg {String} msgCls
8566 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
8568 msgCls : 'x-mask-loading',
8571 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
8577 * Disables the mask to prevent it from being displayed
8579 disable : function(){
8580 this.disabled = true;
8584 * Enables the mask so that it can be displayed
8586 enable : function(){
8587 this.disabled = false;
8590 onLoadException : function()
8594 if (typeof(arguments[3]) != 'undefined') {
8595 Roo.MessageBox.alert("Error loading",arguments[3]);
8599 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8600 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8607 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8612 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8616 onBeforeLoad : function(){
8618 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
8623 destroy : function(){
8625 this.store.un('beforeload', this.onBeforeLoad, this);
8626 this.store.un('load', this.onLoad, this);
8627 this.store.un('loadexception', this.onLoadException, this);
8629 var um = this.el.getUpdateManager();
8630 um.un('beforeupdate', this.onBeforeLoad, this);
8631 um.un('update', this.onLoad, this);
8632 um.un('failure', this.onLoad, this);
8636 * @class Roo.bootstrap.Table
8638 * @extends Roo.bootstrap.Component
8639 * Bootstrap Table class. This class represents the primary interface of a component based grid control.
8640 * Similar to Roo.grid.Grid
8642 var table = Roo.factory({
8644 xns : Roo.bootstrap,
8645 autoSizeColumns: true,
8652 sortInfo : { direction : 'ASC', field: 'name' },
8654 xtype : 'HttpProxy',
8657 url : 'https://example.com/some.data.url.json'
8660 xtype : 'JsonReader',
8662 fields : [ 'id', 'name', whatever' ],
8669 xtype : 'ColumnModel',
8673 dataIndex : 'is_in_group',
8676 renderer : function(v, x , r) {
8678 return String.format("{0}", v)
8684 xtype : 'RowSelectionModel',
8685 xns : Roo.bootstrap.Table
8686 // you can add listeners to catch selection change here....
8692 grid.render(Roo.get("some-div"));
8695 Currently the Table uses multiple headers to try and handle XL / Medium etc... styling
8700 * @cfg {Roo.grid.RowSelectionModel|Roo.grid.CellSelectionModel} sm The selection model to use (cell selection is not supported yet)
8701 * @cfg {Roo.data.Store|Roo.data.SimpleStore} store The data store to use
8702 * @cfg {Roo.grid.ColumnModel} cm[] A column for th grid.
8704 * @cfg {String} cls table class
8707 * @cfg {boolean} striped Should the rows be alternative striped
8708 * @cfg {boolean} bordered Add borders to the table
8709 * @cfg {boolean} hover Add hover highlighting
8710 * @cfg {boolean} condensed Format condensed
8711 * @cfg {boolean} responsive Format condensed
8712 * @cfg {Boolean} loadMask (true|false) default false
8713 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
8714 * @cfg {Boolean} headerShow (true|false) generate thead, default true
8715 * @cfg {Boolean} rowSelection (true|false) default false
8716 * @cfg {Boolean} cellSelection (true|false) default false
8717 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
8718 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
8719 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
8720 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
8721 * @cfg {Boolean} enableColumnResize default true if columns can be resized (drag/drop)
8722 * @cfg {Number} minColumnWidth default 50 pixels minimum column width
8725 * Create a new Table
8726 * @param {Object} config The config object
8729 Roo.bootstrap.Table = function(config)
8731 Roo.bootstrap.Table.superclass.constructor.call(this, config);
8734 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
8735 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
8736 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
8737 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
8739 this.view = this; // compat with grid.
8741 this.sm = this.sm || {xtype: 'RowSelectionModel'};
8743 this.sm.grid = this;
8744 this.selModel = Roo.factory(this.sm, Roo.grid);
8745 this.sm = this.selModel;
8746 this.sm.xmodule = this.xmodule || false;
8749 if (this.cm && typeof(this.cm.config) == 'undefined') {
8750 this.colModel = new Roo.grid.ColumnModel(this.cm);
8751 this.cm = this.colModel;
8752 this.cm.xmodule = this.xmodule || false;
8755 this.store= Roo.factory(this.store, Roo.data);
8756 this.ds = this.store;
8757 this.ds.xmodule = this.xmodule || false;
8760 if (this.footer && this.store) {
8761 this.footer.dataSource = this.ds;
8762 this.footer = Roo.factory(this.footer);
8769 * Fires when a cell is clicked
8770 * @param {Roo.bootstrap.Table} this
8771 * @param {Roo.Element} el
8772 * @param {Number} rowIndex
8773 * @param {Number} columnIndex
8774 * @param {Roo.EventObject} e
8778 * @event celldblclick
8779 * Fires when a cell is double clicked
8780 * @param {Roo.bootstrap.Table} this
8781 * @param {Roo.Element} el
8782 * @param {Number} rowIndex
8783 * @param {Number} columnIndex
8784 * @param {Roo.EventObject} e
8786 "celldblclick" : true,
8789 * Fires when a row is clicked
8790 * @param {Roo.bootstrap.Table} this
8791 * @param {Roo.Element} el
8792 * @param {Number} rowIndex
8793 * @param {Roo.EventObject} e
8797 * @event rowdblclick
8798 * Fires when a row is double clicked
8799 * @param {Roo.bootstrap.Table} this
8800 * @param {Roo.Element} el
8801 * @param {Number} rowIndex
8802 * @param {Roo.EventObject} e
8804 "rowdblclick" : true,
8807 * Fires when a mouseover occur
8808 * @param {Roo.bootstrap.Table} this
8809 * @param {Roo.Element} el
8810 * @param {Number} rowIndex
8811 * @param {Number} columnIndex
8812 * @param {Roo.EventObject} e
8817 * Fires when a mouseout occur
8818 * @param {Roo.bootstrap.Table} this
8819 * @param {Roo.Element} el
8820 * @param {Number} rowIndex
8821 * @param {Number} columnIndex
8822 * @param {Roo.EventObject} e
8827 * Fires when a row is rendered, so you can change add a style to it.
8828 * @param {Roo.bootstrap.Table} this
8829 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
8833 * @event rowsrendered
8834 * Fires when all the rows have been rendered
8835 * @param {Roo.bootstrap.Table} this
8837 'rowsrendered' : true,
8839 * @event contextmenu
8840 * The raw contextmenu event for the entire grid.
8841 * @param {Roo.EventObject} e
8843 "contextmenu" : true,
8845 * @event rowcontextmenu
8846 * Fires when a row is right clicked
8847 * @param {Roo.bootstrap.Table} this
8848 * @param {Number} rowIndex
8849 * @param {Roo.EventObject} e
8851 "rowcontextmenu" : true,
8853 * @event cellcontextmenu
8854 * Fires when a cell is right clicked
8855 * @param {Roo.bootstrap.Table} this
8856 * @param {Number} rowIndex
8857 * @param {Number} cellIndex
8858 * @param {Roo.EventObject} e
8860 "cellcontextmenu" : true,
8862 * @event headercontextmenu
8863 * Fires when a header is right clicked
8864 * @param {Roo.bootstrap.Table} this
8865 * @param {Number} columnIndex
8866 * @param {Roo.EventObject} e
8868 "headercontextmenu" : true,
8871 * The raw mousedown event for the entire grid.
8872 * @param {Roo.EventObject} e
8879 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
8895 enableColumnResize: true,
8897 rowSelection : false,
8898 cellSelection : false,
8901 minColumnWidth : 50,
8903 // Roo.Element - the tbody
8904 bodyEl: false, // <tbody> Roo.Element - thead element
8905 headEl: false, // <thead> Roo.Element - thead element
8906 resizeProxy : false, // proxy element for dragging?
8910 container: false, // used by gridpanel...
8916 auto_hide_footer : false,
8918 view: false, // actually points to this..
8920 getAutoCreate : function()
8922 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8929 // this get's auto added by panel.Grid
8930 if (this.scrollBody) {
8931 cfg.cls += ' table-body-fixed';
8934 cfg.cls += ' table-striped';
8938 cfg.cls += ' table-hover';
8940 if (this.bordered) {
8941 cfg.cls += ' table-bordered';
8943 if (this.condensed) {
8944 cfg.cls += ' table-condensed';
8947 if (this.responsive) {
8948 cfg.cls += ' table-responsive';
8952 cfg.cls+= ' ' +this.cls;
8958 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8961 if(this.store || this.cm){
8962 if(this.headerShow){
8963 cfg.cn.push(this.renderHeader());
8966 cfg.cn.push(this.renderBody());
8968 if(this.footerShow){
8969 cfg.cn.push(this.renderFooter());
8971 // where does this come from?
8972 //cfg.cls+= ' TableGrid';
8975 return { cn : [ cfg ] };
8978 initEvents : function()
8980 if(!this.store || !this.cm){
8983 if (this.selModel) {
8984 this.selModel.initEvents();
8988 //Roo.log('initEvents with ds!!!!');
8990 this.bodyEl = this.el.select('tbody', true).first();
8991 this.headEl = this.el.select('thead', true).first();
8992 this.mainFoot = this.el.select('tfoot', true).first();
8997 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8998 e.on('click', this.sort, this);
9002 // why is this done????? = it breaks dialogs??
9003 //this.parent().el.setStyle('position', 'relative');
9007 this.footer.parentId = this.id;
9008 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9011 this.el.select('tfoot tr td').first().addClass('hide');
9016 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9019 this.store.on('load', this.onLoad, this);
9020 this.store.on('beforeload', this.onBeforeLoad, this);
9021 this.store.on('update', this.onUpdate, this);
9022 this.store.on('add', this.onAdd, this);
9023 this.store.on("clear", this.clear, this);
9025 this.el.on("contextmenu", this.onContextMenu, this);
9028 this.cm.on("headerchange", this.onHeaderChange, this);
9029 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9031 //?? does bodyEl get replaced on render?
9032 this.bodyEl.on("click", this.onClick, this);
9033 this.bodyEl.on("dblclick", this.onDblClick, this);
9034 this.bodyEl.on('scroll', this.onBodyScroll, this);
9036 // guessing mainbody will work - this relays usually caught by selmodel at present.
9037 this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9040 this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: ' ' });
9043 if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9044 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9049 // Compatibility with grid - we implement all the view features at present.
9050 getView : function()
9055 initCSS : function()
9059 var cm = this.cm, styles = [];
9060 this.CSS.removeStyleSheet(this.id + '-cssrules');
9062 // we can honour xs/sm/md/xl as widths...
9063 // we first have to decide what widht we are currently at...
9064 var sz = Roo.getGridSize();
9068 var cols = []; // visable cols.
9069 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9070 var w = cm.getColumnWidth(i, sz);
9079 var unitWidth = Math.floor(this.bodyEl.dom.clientWidth / total);
9080 var rem = this.bodyEl.dom.clientWidth - (unitWidth * total);
9083 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9085 var hidden = 'display:none;';
9086 var width = 'width:0px;';
9087 if(!cm.isHidden(i)){
9090 // we can honour xs/sm/md/xl ?
9091 var w = cols[i] * unitWidth;
9093 hidden = 'display:none;';
9095 // width should return a small number...
9097 w+=rem; // add the remaining with..
9100 width = "width:" + w+ "px;";
9104 '#' , this.id , ' .x-col-' , i, " {\n", cm.config[i].css, width, hidden, "\n}\n",
9105 '#' , this.id , ' .x-hcol-' , i, " {\n", width, hidden,"}\n"
9108 Roo.log(styles.join(''));
9109 this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9115 onContextMenu : function(e, t)
9117 this.processEvent("contextmenu", e);
9120 processEvent : function(name, e)
9122 if (name != 'touchstart' ) {
9123 this.fireEvent(name, e);
9126 var t = e.getTarget();
9128 var cell = Roo.get(t);
9134 if(cell.findParent('tfoot', false, true)){
9138 if(cell.findParent('thead', false, true)){
9140 if(e.getTarget().nodeName.toLowerCase() != 'th'){
9141 cell = Roo.get(t).findParent('th', false, true);
9143 Roo.log("failed to find th in thead?");
9144 Roo.log(e.getTarget());
9149 var cellIndex = cell.dom.cellIndex;
9151 var ename = name == 'touchstart' ? 'click' : name;
9152 this.fireEvent("header" + ename, this, cellIndex, e);
9157 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9158 cell = Roo.get(t).findParent('td', false, true);
9160 Roo.log("failed to find th in tbody?");
9161 Roo.log(e.getTarget());
9166 var row = cell.findParent('tr', false, true);
9167 var cellIndex = cell.dom.cellIndex;
9168 var rowIndex = row.dom.rowIndex - 1;
9172 this.fireEvent("row" + name, this, rowIndex, e);
9176 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9182 onMouseover : function(e, el)
9184 var cell = Roo.get(el);
9190 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9191 cell = cell.findParent('td', false, true);
9194 var row = cell.findParent('tr', false, true);
9195 var cellIndex = cell.dom.cellIndex;
9196 var rowIndex = row.dom.rowIndex - 1; // start from 0
9198 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9202 onMouseout : function(e, el)
9204 var cell = Roo.get(el);
9210 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9211 cell = cell.findParent('td', false, true);
9214 var row = cell.findParent('tr', false, true);
9215 var cellIndex = cell.dom.cellIndex;
9216 var rowIndex = row.dom.rowIndex - 1; // start from 0
9218 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9222 onClick : function(e, el)
9224 var cell = Roo.get(el);
9226 if(!cell || (!this.cellSelection && !this.rowSelection)){
9230 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9231 cell = cell.findParent('td', false, true);
9234 if(!cell || typeof(cell) == 'undefined'){
9238 var row = cell.findParent('tr', false, true);
9240 if(!row || typeof(row) == 'undefined'){
9244 var cellIndex = cell.dom.cellIndex;
9245 var rowIndex = this.getRowIndex(row);
9247 // why??? - should these not be based on SelectionModel?
9248 //if(this.cellSelection){
9249 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9252 //if(this.rowSelection){
9253 this.fireEvent('rowclick', this, row, rowIndex, e);
9258 onDblClick : function(e,el)
9260 var cell = Roo.get(el);
9262 if(!cell || (!this.cellSelection && !this.rowSelection)){
9266 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9267 cell = cell.findParent('td', false, true);
9270 if(!cell || typeof(cell) == 'undefined'){
9274 var row = cell.findParent('tr', false, true);
9276 if(!row || typeof(row) == 'undefined'){
9280 var cellIndex = cell.dom.cellIndex;
9281 var rowIndex = this.getRowIndex(row);
9283 if(this.cellSelection){
9284 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9287 if(this.rowSelection){
9288 this.fireEvent('rowdblclick', this, row, rowIndex, e);
9291 findRowIndex : function(el)
9293 var cell = Roo.get(el);
9297 var row = cell.findParent('tr', false, true);
9299 if(!row || typeof(row) == 'undefined'){
9302 return this.getRowIndex(row);
9304 sort : function(e,el)
9306 var col = Roo.get(el);
9308 if(!col.hasClass('sortable')){
9312 var sort = col.attr('sort');
9315 if(col.select('i', true).first().hasClass('fa-arrow-up')){
9319 this.store.sortInfo = {field : sort, direction : dir};
9322 Roo.log("calling footer first");
9323 this.footer.onClick('first');
9326 this.store.load({ params : { start : 0 } });
9330 renderHeader : function()
9338 this.totalWidth = 0;
9340 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9342 var config = cm.config[i];
9346 cls : 'x-hcol-' + i,
9349 html: cm.getColumnHeader(i)
9352 var tooltip = cm.getColumnTooltip(i);
9354 c.tooltip = tooltip;
9360 if(typeof(config.sortable) != 'undefined' && config.sortable){
9361 c.cls += ' sortable';
9362 c.html = '<i class="fa"></i>' + c.html;
9365 // could use BS4 hidden-..-down
9367 if(typeof(config.lgHeader) != 'undefined'){
9368 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9371 if(typeof(config.mdHeader) != 'undefined'){
9372 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9375 if(typeof(config.smHeader) != 'undefined'){
9376 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9379 if(typeof(config.xsHeader) != 'undefined'){
9380 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9387 if(typeof(config.tooltip) != 'undefined'){
9388 c.tooltip = config.tooltip;
9391 if(typeof(config.colspan) != 'undefined'){
9392 c.colspan = config.colspan;
9395 // hidden is handled by CSS now
9397 if(typeof(config.dataIndex) != 'undefined'){
9398 c.sort = config.dataIndex;
9403 if(typeof(config.align) != 'undefined' && config.align.length){
9404 c.style += ' text-align:' + config.align + ';';
9407 if(typeof(config.width) != 'undefined'){
9408 c.style += ' width:' + config.width + 'px;';
9409 this.totalWidth += config.width;
9411 this.totalWidth += 100; // assume minimum of 100 per column?
9414 if(typeof(config.cls) != 'undefined'){
9415 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9417 // this is the bit that doesnt reall work at all...
9421 ['xs','sm','md','lg'].map(function(size){
9423 if(typeof(config[size]) == 'undefined'){
9427 if (!config[size]) { // 0 = hidden
9428 // BS 4 '0' is treated as hide that column and below.
9429 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9433 c.cls += ' col-' + size + '-' + config[size] + (
9434 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9442 c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9453 renderBody : function()
9463 colspan : this.cm.getColumnCount()
9473 renderFooter : function()
9483 colspan : this.cm.getColumnCount()
9497 // Roo.log('ds onload');
9502 var ds = this.store;
9504 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9505 e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9506 if (_this.store.sortInfo) {
9508 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9509 e.select('i', true).addClass(['fa-arrow-up']);
9512 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9513 e.select('i', true).addClass(['fa-arrow-down']);
9518 var tbody = this.bodyEl;
9520 if(ds.getCount() > 0){
9521 ds.data.each(function(d,rowIndex){
9522 var row = this.renderRow(cm, ds, rowIndex);
9524 tbody.createChild(row);
9528 if(row.cellObjects.length){
9529 Roo.each(row.cellObjects, function(r){
9530 _this.renderCellObject(r);
9537 var tfoot = this.el.select('tfoot', true).first();
9539 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
9541 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
9543 var total = this.ds.getTotalCount();
9545 if(this.footer.pageSize < total){
9546 this.mainFoot.show();
9550 Roo.each(this.el.select('tbody td', true).elements, function(e){
9551 e.on('mouseover', _this.onMouseover, _this);
9554 Roo.each(this.el.select('tbody td', true).elements, function(e){
9555 e.on('mouseout', _this.onMouseout, _this);
9557 this.fireEvent('rowsrendered', this);
9563 onUpdate : function(ds,record)
9565 this.refreshRow(record);
9569 onRemove : function(ds, record, index, isUpdate){
9570 if(isUpdate !== true){
9571 this.fireEvent("beforerowremoved", this, index, record);
9573 var bt = this.bodyEl.dom;
9575 var rows = this.el.select('tbody > tr', true).elements;
9577 if(typeof(rows[index]) != 'undefined'){
9578 bt.removeChild(rows[index].dom);
9581 // if(bt.rows[index]){
9582 // bt.removeChild(bt.rows[index]);
9585 if(isUpdate !== true){
9586 //this.stripeRows(index);
9587 //this.syncRowHeights(index, index);
9589 this.fireEvent("rowremoved", this, index, record);
9593 onAdd : function(ds, records, rowIndex)
9595 //Roo.log('on Add called');
9596 // - note this does not handle multiple adding very well..
9597 var bt = this.bodyEl.dom;
9598 for (var i =0 ; i < records.length;i++) {
9599 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
9600 //Roo.log(records[i]);
9601 //Roo.log(this.store.getAt(rowIndex+i));
9602 this.insertRow(this.store, rowIndex + i, false);
9609 refreshRow : function(record){
9610 var ds = this.store, index;
9611 if(typeof record == 'number'){
9613 record = ds.getAt(index);
9615 index = ds.indexOf(record);
9617 return; // should not happen - but seems to
9620 this.insertRow(ds, index, true);
9622 this.onRemove(ds, record, index+1, true);
9624 //this.syncRowHeights(index, index);
9626 this.fireEvent("rowupdated", this, index, record);
9629 onRowSelect : function(rowIndex){
9630 var row = this.getRowDom(rowIndex);
9631 row.addClass(['bg-info','info']);
9634 onRowDeselect : function(rowIndex){
9635 var row = this.getRowDom(rowIndex);
9636 row.removeClass(['bg-info','info']);
9639 * Focuses the specified row.
9640 * @param {Number} row The row index
9642 focusRow : function(row)
9644 //Roo.log('GridView.focusRow');
9645 var x = this.bodyEl.dom.scrollLeft;
9646 this.focusCell(row, 0, false);
9647 this.bodyEl.dom.scrollLeft = x;
9651 * Focuses the specified cell.
9652 * @param {Number} row The row index
9653 * @param {Number} col The column index
9654 * @param {Boolean} hscroll false to disable horizontal scrolling
9656 focusCell : function(row, col, hscroll)
9658 //Roo.log('GridView.focusCell');
9659 var el = this.ensureVisible(row, col, hscroll);
9660 // not sure what focusEL achives = it's a <a> pos relative
9661 //this.focusEl.alignTo(el, "tl-tl");
9663 // this.focusEl.focus();
9665 // this.focusEl.focus.defer(1, this.focusEl);
9670 * Scrolls the specified cell into view
9671 * @param {Number} row The row index
9672 * @param {Number} col The column index
9673 * @param {Boolean} hscroll false to disable horizontal scrolling
9675 ensureVisible : function(row, col, hscroll)
9677 //Roo.log('GridView.ensureVisible,' + row + ',' + col);
9678 //return null; //disable for testing.
9679 if(typeof row != "number"){
9682 if(row < 0 && row >= this.ds.getCount()){
9685 col = (col !== undefined ? col : 0);
9687 while(cm.isHidden(col)){
9691 var el = this.getCellDom(row, col);
9695 var c = this.bodyEl.dom;
9697 var ctop = parseInt(el.offsetTop, 10);
9698 var cleft = parseInt(el.offsetLeft, 10);
9699 var cbot = ctop + el.offsetHeight;
9700 var cright = cleft + el.offsetWidth;
9702 //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
9703 var ch = 0; //?? header is not withing the area?
9704 var stop = parseInt(c.scrollTop, 10);
9705 var sleft = parseInt(c.scrollLeft, 10);
9706 var sbot = stop + ch;
9707 var sright = sleft + c.clientWidth;
9709 Roo.log('GridView.ensureVisible:' +
9711 ' c.clientHeight:' + c.clientHeight +
9712 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
9721 //Roo.log("set scrolltop to ctop DISABLE?");
9722 }else if(cbot > sbot){
9723 //Roo.log("set scrolltop to cbot-ch");
9724 c.scrollTop = cbot-ch;
9727 if(hscroll !== false){
9729 c.scrollLeft = cleft;
9730 }else if(cright > sright){
9731 c.scrollLeft = cright-c.clientWidth;
9739 insertRow : function(dm, rowIndex, isUpdate){
9742 this.fireEvent("beforerowsinserted", this, rowIndex);
9744 //var s = this.getScrollState();
9745 var row = this.renderRow(this.cm, this.store, rowIndex);
9746 // insert before rowIndex..
9747 var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
9751 if(row.cellObjects.length){
9752 Roo.each(row.cellObjects, function(r){
9753 _this.renderCellObject(r);
9758 this.fireEvent("rowsinserted", this, rowIndex);
9759 //this.syncRowHeights(firstRow, lastRow);
9760 //this.stripeRows(firstRow);
9767 getRowDom : function(rowIndex)
9769 var rows = this.el.select('tbody > tr', true).elements;
9771 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
9774 getCellDom : function(rowIndex, colIndex)
9776 var row = this.getRowDom(rowIndex);
9777 if (row === false) {
9780 var cols = row.select('td', true).elements;
9781 return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
9785 // returns the object tree for a tr..
9788 renderRow : function(cm, ds, rowIndex)
9790 var d = ds.getAt(rowIndex);
9794 cls : 'x-row-' + rowIndex,
9798 var cellObjects = [];
9800 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9801 var config = cm.config[i];
9803 var renderer = cm.getRenderer(i);
9807 if(typeof(renderer) !== 'undefined'){
9808 value = renderer(d.data[cm.getDataIndex(i)], false, d);
9810 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
9811 // and are rendered into the cells after the row is rendered - using the id for the element.
9813 if(typeof(value) === 'object'){
9823 rowIndex : rowIndex,
9828 this.fireEvent('rowclass', this, rowcfg);
9832 // this might end up displaying HTML?
9833 // this is too messy... - better to only do it on columsn you know are going to be too long
9834 //tooltip : (typeof(value) === 'object') ? '' : value,
9835 cls : rowcfg.rowClass + ' x-col-' + i,
9837 html: (typeof(value) === 'object') ? '' : value
9844 if(typeof(config.colspan) != 'undefined'){
9845 td.colspan = config.colspan;
9850 if(typeof(config.align) != 'undefined' && config.align.length){
9851 td.style += ' text-align:' + config.align + ';';
9853 if(typeof(config.valign) != 'undefined' && config.valign.length){
9854 td.style += ' vertical-align:' + config.valign + ';';
9857 if(typeof(config.width) != 'undefined'){
9858 td.style += ' width:' + config.width + 'px;';
9861 if(typeof(config.cursor) != 'undefined'){
9862 td.style += ' cursor:' + config.cursor + ';';
9865 if(typeof(config.cls) != 'undefined'){
9866 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
9869 ['xs','sm','md','lg'].map(function(size){
9871 if(typeof(config[size]) == 'undefined'){
9877 if (!config[size]) { // 0 = hidden
9878 // BS 4 '0' is treated as hide that column and below.
9879 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
9883 td.cls += ' col-' + size + '-' + config[size] + (
9884 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9894 row.cellObjects = cellObjects;
9902 onBeforeLoad : function()
9911 this.el.select('tbody', true).first().dom.innerHTML = '';
9914 * Show or hide a row.
9915 * @param {Number} rowIndex to show or hide
9916 * @param {Boolean} state hide
9918 setRowVisibility : function(rowIndex, state)
9920 var bt = this.bodyEl.dom;
9922 var rows = this.el.select('tbody > tr', true).elements;
9924 if(typeof(rows[rowIndex]) == 'undefined'){
9927 rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
9932 getSelectionModel : function(){
9934 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
9936 return this.selModel;
9939 * Render the Roo.bootstrap object from renderder
9941 renderCellObject : function(r)
9945 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
9947 var t = r.cfg.render(r.container);
9950 Roo.each(r.cfg.cn, function(c){
9952 container: t.getChildContainer(),
9955 _this.renderCellObject(child);
9960 * get the Row Index from a dom element.
9961 * @param {Roo.Element} row The row to look for
9962 * @returns {Number} the row
9964 getRowIndex : function(row)
9968 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
9979 * get the header TH element for columnIndex
9980 * @param {Number} columnIndex
9981 * @returns {Roo.Element}
9983 getHeaderIndex: function(colIndex)
9985 var cols = this.headEl.select('th', true).elements;
9986 return cols[colIndex];
9989 * get the Column Index from a dom element. (using regex on x-hcol-{colid})
9990 * @param {domElement} cell to look for
9991 * @returns {Number} the column
9993 getCellIndex : function(cell)
9995 var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
9997 return parseInt(id[1], 10);
10002 * Returns the grid's underlying element = used by panel.Grid
10003 * @return {Element} The element
10005 getGridEl : function(){
10009 * Forces a resize - used by panel.Grid
10010 * @return {Element} The element
10012 autoSize : function()
10014 //var ctr = Roo.get(this.container.dom.parentElement);
10015 var ctr = Roo.get(this.el.dom);
10017 var thd = this.getGridEl().select('thead',true).first();
10018 var tbd = this.getGridEl().select('tbody', true).first();
10019 var tfd = this.getGridEl().select('tfoot', true).first();
10021 var cw = ctr.getWidth();
10022 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
10026 tbd.setWidth(ctr.getWidth());
10027 // if the body has a max height - and then scrolls - we should perhaps set up the height here
10028 // this needs fixing for various usage - currently only hydra job advers I think..
10030 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10032 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10035 cw = Math.max(cw, this.totalWidth);
10036 this.getGridEl().select('tbody tr',true).setWidth(cw);
10038 // resize 'expandable coloumn?
10040 return; // we doe not have a view in this design..
10043 onBodyScroll: function()
10045 //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10047 this.headEl.setStyle({
10048 'position' : 'relative',
10049 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10055 var scrollHeight = this.bodyEl.dom.scrollHeight;
10057 var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10059 var height = this.bodyEl.getHeight();
10061 if(scrollHeight - height == scrollTop) {
10063 var total = this.ds.getTotalCount();
10065 if(this.footer.cursor + this.footer.pageSize < total){
10067 this.footer.ds.load({
10069 start : this.footer.cursor + this.footer.pageSize,
10070 limit : this.footer.pageSize
10079 onColumnSplitterMoved : function(i, w)
10081 this.userResized = true;
10083 var cm = this.colModel;
10085 cm.setColumnWidth(i, w, true);
10086 //var cid = cm.getColumnId(i); << not used in this version?
10087 Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10089 this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10090 this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10092 //this.updateSplitters();
10093 //this.layout(); << ??
10094 this.fireEvent("columnresize", i, w);
10096 onHeaderChange : function()
10098 var header = this.renderHeader();
10099 var table = this.el.select('table', true).first();
10101 this.headEl.remove();
10102 this.headEl = table.createChild(header, this.bodyEl, false);
10104 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10105 e.on('click', this.sort, this);
10108 if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10109 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10114 onHiddenChange : function(colModel, colIndex, hidden)
10116 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10117 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10119 this.CSS.updateRule(thSelector, "display", "");
10120 this.CSS.updateRule(tdSelector, "display", "");
10123 this.CSS.updateRule(thSelector, "display", "none");
10124 this.CSS.updateRule(tdSelector, "display", "none");
10127 this.onHeaderChange();
10131 setColumnWidth: function(col_index, width)
10133 // width = "md-2 xs-2..."
10134 if(!this.colModel.config[col_index]) {
10138 var w = width.split(" ");
10140 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10142 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10145 for(var j = 0; j < w.length; j++) {
10151 var size_cls = w[j].split("-");
10153 if(!Number.isInteger(size_cls[1] * 1)) {
10157 if(!this.colModel.config[col_index][size_cls[0]]) {
10161 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10165 h_row[0].classList.replace(
10166 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10167 "col-"+size_cls[0]+"-"+size_cls[1]
10170 for(var i = 0; i < rows.length; i++) {
10172 var size_cls = w[j].split("-");
10174 if(!Number.isInteger(size_cls[1] * 1)) {
10178 if(!this.colModel.config[col_index][size_cls[0]]) {
10182 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10186 rows[i].classList.replace(
10187 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10188 "col-"+size_cls[0]+"-"+size_cls[1]
10192 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10197 // currently only used to find the split on drag..
10198 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10203 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10204 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10213 * @class Roo.bootstrap.TableCell
10214 * @extends Roo.bootstrap.Component
10215 * Bootstrap TableCell class
10216 * @cfg {String} html cell contain text
10217 * @cfg {String} cls cell class
10218 * @cfg {String} tag cell tag (td|th) default td
10219 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10220 * @cfg {String} align Aligns the content in a cell
10221 * @cfg {String} axis Categorizes cells
10222 * @cfg {String} bgcolor Specifies the background color of a cell
10223 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10224 * @cfg {Number} colspan Specifies the number of columns a cell should span
10225 * @cfg {String} headers Specifies one or more header cells a cell is related to
10226 * @cfg {Number} height Sets the height of a cell
10227 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10228 * @cfg {Number} rowspan Sets the number of rows a cell should span
10229 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10230 * @cfg {String} valign Vertical aligns the content in a cell
10231 * @cfg {Number} width Specifies the width of a cell
10234 * Create a new TableCell
10235 * @param {Object} config The config object
10238 Roo.bootstrap.TableCell = function(config){
10239 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10242 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
10262 getAutoCreate : function(){
10263 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10270 cfg.tag = this.tag;
10283 cfg.align=this.align
10288 if (this.bgcolor) {
10289 cfg.bgcolor=this.bgcolor
10291 if (this.charoff) {
10292 cfg.charoff=this.charoff
10294 if (this.colspan) {
10295 cfg.colspan=this.colspan
10297 if (this.headers) {
10298 cfg.headers=this.headers
10301 cfg.height=this.height
10304 cfg.nowrap=this.nowrap
10306 if (this.rowspan) {
10307 cfg.rowspan=this.rowspan
10310 cfg.scope=this.scope
10313 cfg.valign=this.valign
10316 cfg.width=this.width
10335 * @class Roo.bootstrap.TableRow
10336 * @extends Roo.bootstrap.Component
10337 * Bootstrap TableRow class
10338 * @cfg {String} cls row class
10339 * @cfg {String} align Aligns the content in a table row
10340 * @cfg {String} bgcolor Specifies a background color for a table row
10341 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10342 * @cfg {String} valign Vertical aligns the content in a table row
10345 * Create a new TableRow
10346 * @param {Object} config The config object
10349 Roo.bootstrap.TableRow = function(config){
10350 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10353 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
10361 getAutoCreate : function(){
10362 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10369 cfg.cls = this.cls;
10372 cfg.align = this.align;
10375 cfg.bgcolor = this.bgcolor;
10378 cfg.charoff = this.charoff;
10381 cfg.valign = this.valign;
10399 * @class Roo.bootstrap.TableBody
10400 * @extends Roo.bootstrap.Component
10401 * Bootstrap TableBody class
10402 * @cfg {String} cls element class
10403 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10404 * @cfg {String} align Aligns the content inside the element
10405 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10406 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10409 * Create a new TableBody
10410 * @param {Object} config The config object
10413 Roo.bootstrap.TableBody = function(config){
10414 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10417 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
10425 getAutoCreate : function(){
10426 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10436 cfg.tag = this.tag;
10440 cfg.align = this.align;
10443 cfg.charoff = this.charoff;
10446 cfg.valign = this.valign;
10453 // initEvents : function()
10456 // if(!this.store){
10460 // this.store = Roo.factory(this.store, Roo.data);
10461 // this.store.on('load', this.onLoad, this);
10463 // this.store.load();
10467 // onLoad: function ()
10469 // this.fireEvent('load', this);
10479 * Ext JS Library 1.1.1
10480 * Copyright(c) 2006-2007, Ext JS, LLC.
10482 * Originally Released Under LGPL - original licence link has changed is not relivant.
10485 * <script type="text/javascript">
10488 // as we use this in bootstrap.
10489 Roo.namespace('Roo.form');
10491 * @class Roo.form.Action
10492 * Internal Class used to handle form actions
10494 * @param {Roo.form.BasicForm} el The form element or its id
10495 * @param {Object} config Configuration options
10500 // define the action interface
10501 Roo.form.Action = function(form, options){
10503 this.options = options || {};
10506 * Client Validation Failed
10509 Roo.form.Action.CLIENT_INVALID = 'client';
10511 * Server Validation Failed
10514 Roo.form.Action.SERVER_INVALID = 'server';
10516 * Connect to Server Failed
10519 Roo.form.Action.CONNECT_FAILURE = 'connect';
10521 * Reading Data from Server Failed
10524 Roo.form.Action.LOAD_FAILURE = 'load';
10526 Roo.form.Action.prototype = {
10528 failureType : undefined,
10529 response : undefined,
10530 result : undefined,
10532 // interface method
10533 run : function(options){
10537 // interface method
10538 success : function(response){
10542 // interface method
10543 handleResponse : function(response){
10547 // default connection failure
10548 failure : function(response){
10550 this.response = response;
10551 this.failureType = Roo.form.Action.CONNECT_FAILURE;
10552 this.form.afterAction(this, false);
10555 processResponse : function(response){
10556 this.response = response;
10557 if(!response.responseText){
10560 this.result = this.handleResponse(response);
10561 return this.result;
10564 // utility functions used internally
10565 getUrl : function(appendParams){
10566 var url = this.options.url || this.form.url || this.form.el.dom.action;
10568 var p = this.getParams();
10570 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
10576 getMethod : function(){
10577 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
10580 getParams : function(){
10581 var bp = this.form.baseParams;
10582 var p = this.options.params;
10584 if(typeof p == "object"){
10585 p = Roo.urlEncode(Roo.applyIf(p, bp));
10586 }else if(typeof p == 'string' && bp){
10587 p += '&' + Roo.urlEncode(bp);
10590 p = Roo.urlEncode(bp);
10595 createCallback : function(){
10597 success: this.success,
10598 failure: this.failure,
10600 timeout: (this.form.timeout*1000),
10601 upload: this.form.fileUpload ? this.success : undefined
10606 Roo.form.Action.Submit = function(form, options){
10607 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
10610 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
10613 haveProgress : false,
10614 uploadComplete : false,
10616 // uploadProgress indicator.
10617 uploadProgress : function()
10619 if (!this.form.progressUrl) {
10623 if (!this.haveProgress) {
10624 Roo.MessageBox.progress("Uploading", "Uploading");
10626 if (this.uploadComplete) {
10627 Roo.MessageBox.hide();
10631 this.haveProgress = true;
10633 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
10635 var c = new Roo.data.Connection();
10637 url : this.form.progressUrl,
10642 success : function(req){
10643 //console.log(data);
10647 rdata = Roo.decode(req.responseText)
10649 Roo.log("Invalid data from server..");
10653 if (!rdata || !rdata.success) {
10655 Roo.MessageBox.alert(Roo.encode(rdata));
10658 var data = rdata.data;
10660 if (this.uploadComplete) {
10661 Roo.MessageBox.hide();
10666 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
10667 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
10670 this.uploadProgress.defer(2000,this);
10673 failure: function(data) {
10674 Roo.log('progress url failed ');
10685 // run get Values on the form, so it syncs any secondary forms.
10686 this.form.getValues();
10688 var o = this.options;
10689 var method = this.getMethod();
10690 var isPost = method == 'POST';
10691 if(o.clientValidation === false || this.form.isValid()){
10693 if (this.form.progressUrl) {
10694 this.form.findField('UPLOAD_IDENTIFIER').setValue(
10695 (new Date() * 1) + '' + Math.random());
10700 Roo.Ajax.request(Roo.apply(this.createCallback(), {
10701 form:this.form.el.dom,
10702 url:this.getUrl(!isPost),
10704 params:isPost ? this.getParams() : null,
10705 isUpload: this.form.fileUpload,
10706 formData : this.form.formData
10709 this.uploadProgress();
10711 }else if (o.clientValidation !== false){ // client validation failed
10712 this.failureType = Roo.form.Action.CLIENT_INVALID;
10713 this.form.afterAction(this, false);
10717 success : function(response)
10719 this.uploadComplete= true;
10720 if (this.haveProgress) {
10721 Roo.MessageBox.hide();
10725 var result = this.processResponse(response);
10726 if(result === true || result.success){
10727 this.form.afterAction(this, true);
10731 this.form.markInvalid(result.errors);
10732 this.failureType = Roo.form.Action.SERVER_INVALID;
10734 this.form.afterAction(this, false);
10736 failure : function(response)
10738 this.uploadComplete= true;
10739 if (this.haveProgress) {
10740 Roo.MessageBox.hide();
10743 this.response = response;
10744 this.failureType = Roo.form.Action.CONNECT_FAILURE;
10745 this.form.afterAction(this, false);
10748 handleResponse : function(response){
10749 if(this.form.errorReader){
10750 var rs = this.form.errorReader.read(response);
10753 for(var i = 0, len = rs.records.length; i < len; i++) {
10754 var r = rs.records[i];
10755 errors[i] = r.data;
10758 if(errors.length < 1){
10762 success : rs.success,
10768 ret = Roo.decode(response.responseText);
10772 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
10782 Roo.form.Action.Load = function(form, options){
10783 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
10784 this.reader = this.form.reader;
10787 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
10792 Roo.Ajax.request(Roo.apply(
10793 this.createCallback(), {
10794 method:this.getMethod(),
10795 url:this.getUrl(false),
10796 params:this.getParams()
10800 success : function(response){
10802 var result = this.processResponse(response);
10803 if(result === true || !result.success || !result.data){
10804 this.failureType = Roo.form.Action.LOAD_FAILURE;
10805 this.form.afterAction(this, false);
10808 this.form.clearInvalid();
10809 this.form.setValues(result.data);
10810 this.form.afterAction(this, true);
10813 handleResponse : function(response){
10814 if(this.form.reader){
10815 var rs = this.form.reader.read(response);
10816 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
10818 success : rs.success,
10822 return Roo.decode(response.responseText);
10826 Roo.form.Action.ACTION_TYPES = {
10827 'load' : Roo.form.Action.Load,
10828 'submit' : Roo.form.Action.Submit
10837 * @class Roo.bootstrap.Form
10838 * @extends Roo.bootstrap.Component
10839 * Bootstrap Form class
10840 * @cfg {String} method GET | POST (default POST)
10841 * @cfg {String} labelAlign top | left (default top)
10842 * @cfg {String} align left | right - for navbars
10843 * @cfg {Boolean} loadMask load mask when submit (default true)
10847 * Create a new Form
10848 * @param {Object} config The config object
10852 Roo.bootstrap.Form = function(config){
10854 Roo.bootstrap.Form.superclass.constructor.call(this, config);
10856 Roo.bootstrap.Form.popover.apply();
10860 * @event clientvalidation
10861 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
10862 * @param {Form} this
10863 * @param {Boolean} valid true if the form has passed client-side validation
10865 clientvalidation: true,
10867 * @event beforeaction
10868 * Fires before any action is performed. Return false to cancel the action.
10869 * @param {Form} this
10870 * @param {Action} action The action to be performed
10872 beforeaction: true,
10874 * @event actionfailed
10875 * Fires when an action fails.
10876 * @param {Form} this
10877 * @param {Action} action The action that failed
10879 actionfailed : true,
10881 * @event actioncomplete
10882 * Fires when an action is completed.
10883 * @param {Form} this
10884 * @param {Action} action The action that completed
10886 actioncomplete : true
10890 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
10893 * @cfg {String} method
10894 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
10898 * @cfg {String} url
10899 * The URL to use for form actions if one isn't supplied in the action options.
10902 * @cfg {Boolean} fileUpload
10903 * Set to true if this form is a file upload.
10907 * @cfg {Object} baseParams
10908 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
10912 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
10916 * @cfg {Sting} align (left|right) for navbar forms
10921 activeAction : null,
10924 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
10925 * element by passing it or its id or mask the form itself by passing in true.
10928 waitMsgTarget : false,
10933 * @cfg {Boolean} errorMask (true|false) default false
10938 * @cfg {Number} maskOffset Default 100
10943 * @cfg {Boolean} maskBody
10947 getAutoCreate : function(){
10951 method : this.method || 'POST',
10952 id : this.id || Roo.id(),
10955 if (this.parent().xtype.match(/^Nav/)) {
10956 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
10960 if (this.labelAlign == 'left' ) {
10961 cfg.cls += ' form-horizontal';
10967 initEvents : function()
10969 this.el.on('submit', this.onSubmit, this);
10970 // this was added as random key presses on the form where triggering form submit.
10971 this.el.on('keypress', function(e) {
10972 if (e.getCharCode() != 13) {
10975 // we might need to allow it for textareas.. and some other items.
10976 // check e.getTarget().
10978 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
10982 Roo.log("keypress blocked");
10984 e.preventDefault();
10990 onSubmit : function(e){
10995 * Returns true if client-side validation on the form is successful.
10998 isValid : function(){
10999 var items = this.getItems();
11001 var target = false;
11003 items.each(function(f){
11009 Roo.log('invalid field: ' + f.name);
11013 if(!target && f.el.isVisible(true)){
11019 if(this.errorMask && !valid){
11020 Roo.bootstrap.Form.popover.mask(this, target);
11027 * Returns true if any fields in this form have changed since their original load.
11030 isDirty : function(){
11032 var items = this.getItems();
11033 items.each(function(f){
11043 * Performs a predefined action (submit or load) or custom actions you define on this form.
11044 * @param {String} actionName The name of the action type
11045 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
11046 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11047 * accept other config options):
11049 Property Type Description
11050 ---------------- --------------- ----------------------------------------------------------------------------------
11051 url String The url for the action (defaults to the form's url)
11052 method String The form method to use (defaults to the form's method, or POST if not defined)
11053 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
11054 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
11055 validate the form on the client (defaults to false)
11057 * @return {BasicForm} this
11059 doAction : function(action, options){
11060 if(typeof action == 'string'){
11061 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11063 if(this.fireEvent('beforeaction', this, action) !== false){
11064 this.beforeAction(action);
11065 action.run.defer(100, action);
11071 beforeAction : function(action){
11072 var o = action.options;
11077 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11079 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11082 // not really supported yet.. ??
11084 //if(this.waitMsgTarget === true){
11085 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11086 //}else if(this.waitMsgTarget){
11087 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11088 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11090 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11096 afterAction : function(action, success){
11097 this.activeAction = null;
11098 var o = action.options;
11103 Roo.get(document.body).unmask();
11109 //if(this.waitMsgTarget === true){
11110 // this.el.unmask();
11111 //}else if(this.waitMsgTarget){
11112 // this.waitMsgTarget.unmask();
11114 // Roo.MessageBox.updateProgress(1);
11115 // Roo.MessageBox.hide();
11122 Roo.callback(o.success, o.scope, [this, action]);
11123 this.fireEvent('actioncomplete', this, action);
11127 // failure condition..
11128 // we have a scenario where updates need confirming.
11129 // eg. if a locking scenario exists..
11130 // we look for { errors : { needs_confirm : true }} in the response.
11132 (typeof(action.result) != 'undefined') &&
11133 (typeof(action.result.errors) != 'undefined') &&
11134 (typeof(action.result.errors.needs_confirm) != 'undefined')
11137 Roo.log("not supported yet");
11140 Roo.MessageBox.confirm(
11141 "Change requires confirmation",
11142 action.result.errorMsg,
11147 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
11157 Roo.callback(o.failure, o.scope, [this, action]);
11158 // show an error message if no failed handler is set..
11159 if (!this.hasListener('actionfailed')) {
11160 Roo.log("need to add dialog support");
11162 Roo.MessageBox.alert("Error",
11163 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11164 action.result.errorMsg :
11165 "Saving Failed, please check your entries or try again"
11170 this.fireEvent('actionfailed', this, action);
11175 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11176 * @param {String} id The value to search for
11179 findField : function(id){
11180 var items = this.getItems();
11181 var field = items.get(id);
11183 items.each(function(f){
11184 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11191 return field || null;
11194 * Mark fields in this form invalid in bulk.
11195 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11196 * @return {BasicForm} this
11198 markInvalid : function(errors){
11199 if(errors instanceof Array){
11200 for(var i = 0, len = errors.length; i < len; i++){
11201 var fieldError = errors[i];
11202 var f = this.findField(fieldError.id);
11204 f.markInvalid(fieldError.msg);
11210 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11211 field.markInvalid(errors[id]);
11215 //Roo.each(this.childForms || [], function (f) {
11216 // f.markInvalid(errors);
11223 * Set values for fields in this form in bulk.
11224 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11225 * @return {BasicForm} this
11227 setValues : function(values){
11228 if(values instanceof Array){ // array of objects
11229 for(var i = 0, len = values.length; i < len; i++){
11231 var f = this.findField(v.id);
11233 f.setValue(v.value);
11234 if(this.trackResetOnLoad){
11235 f.originalValue = f.getValue();
11239 }else{ // object hash
11242 if(typeof values[id] != 'function' && (field = this.findField(id))){
11244 if (field.setFromData &&
11245 field.valueField &&
11246 field.displayField &&
11247 // combos' with local stores can
11248 // be queried via setValue()
11249 // to set their value..
11250 (field.store && !field.store.isLocal)
11254 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11255 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11256 field.setFromData(sd);
11258 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11260 field.setFromData(values);
11263 field.setValue(values[id]);
11267 if(this.trackResetOnLoad){
11268 field.originalValue = field.getValue();
11274 //Roo.each(this.childForms || [], function (f) {
11275 // f.setValues(values);
11282 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11283 * they are returned as an array.
11284 * @param {Boolean} asString
11287 getValues : function(asString){
11288 //if (this.childForms) {
11289 // copy values from the child forms
11290 // Roo.each(this.childForms, function (f) {
11291 // this.setValues(f.getValues());
11297 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11298 if(asString === true){
11301 return Roo.urlDecode(fs);
11305 * Returns the fields in this form as an object with key/value pairs.
11306 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11309 getFieldValues : function(with_hidden)
11311 var items = this.getItems();
11313 items.each(function(f){
11315 if (!f.getName()) {
11319 var v = f.getValue();
11321 if (f.inputType =='radio') {
11322 if (typeof(ret[f.getName()]) == 'undefined') {
11323 ret[f.getName()] = ''; // empty..
11326 if (!f.el.dom.checked) {
11330 v = f.el.dom.value;
11334 if(f.xtype == 'MoneyField'){
11335 ret[f.currencyName] = f.getCurrency();
11338 // not sure if this supported any more..
11339 if ((typeof(v) == 'object') && f.getRawValue) {
11340 v = f.getRawValue() ; // dates..
11342 // combo boxes where name != hiddenName...
11343 if (f.name !== false && f.name != '' && f.name != f.getName()) {
11344 ret[f.name] = f.getRawValue();
11346 ret[f.getName()] = v;
11353 * Clears all invalid messages in this form.
11354 * @return {BasicForm} this
11356 clearInvalid : function(){
11357 var items = this.getItems();
11359 items.each(function(f){
11367 * Resets this form.
11368 * @return {BasicForm} this
11370 reset : function(){
11371 var items = this.getItems();
11372 items.each(function(f){
11376 Roo.each(this.childForms || [], function (f) {
11384 getItems : function()
11386 var r=new Roo.util.MixedCollection(false, function(o){
11387 return o.id || (o.id = Roo.id());
11389 var iter = function(el) {
11396 Roo.each(el.items,function(e) {
11405 hideFields : function(items)
11407 Roo.each(items, function(i){
11409 var f = this.findField(i);
11420 showFields : function(items)
11422 Roo.each(items, function(i){
11424 var f = this.findField(i);
11437 Roo.apply(Roo.bootstrap.Form, {
11453 intervalID : false,
11459 if(this.isApplied){
11464 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11465 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11466 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11467 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11470 this.maskEl.top.enableDisplayMode("block");
11471 this.maskEl.left.enableDisplayMode("block");
11472 this.maskEl.bottom.enableDisplayMode("block");
11473 this.maskEl.right.enableDisplayMode("block");
11475 this.toolTip = new Roo.bootstrap.Tooltip({
11476 cls : 'roo-form-error-popover',
11478 'left' : ['r-l', [-2,0], 'right'],
11479 'right' : ['l-r', [2,0], 'left'],
11480 'bottom' : ['tl-bl', [0,2], 'top'],
11481 'top' : [ 'bl-tl', [0,-2], 'bottom']
11485 this.toolTip.render(Roo.get(document.body));
11487 this.toolTip.el.enableDisplayMode("block");
11489 Roo.get(document.body).on('click', function(){
11493 Roo.get(document.body).on('touchstart', function(){
11497 this.isApplied = true
11500 mask : function(form, target)
11504 this.target = target;
11506 if(!this.form.errorMask || !target.el){
11510 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
11512 Roo.log(scrollable);
11514 var ot = this.target.el.calcOffsetsTo(scrollable);
11516 var scrollTo = ot[1] - this.form.maskOffset;
11518 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
11520 scrollable.scrollTo('top', scrollTo);
11522 var box = this.target.el.getBox();
11524 var zIndex = Roo.bootstrap.Modal.zIndex++;
11527 this.maskEl.top.setStyle('position', 'absolute');
11528 this.maskEl.top.setStyle('z-index', zIndex);
11529 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
11530 this.maskEl.top.setLeft(0);
11531 this.maskEl.top.setTop(0);
11532 this.maskEl.top.show();
11534 this.maskEl.left.setStyle('position', 'absolute');
11535 this.maskEl.left.setStyle('z-index', zIndex);
11536 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
11537 this.maskEl.left.setLeft(0);
11538 this.maskEl.left.setTop(box.y - this.padding);
11539 this.maskEl.left.show();
11541 this.maskEl.bottom.setStyle('position', 'absolute');
11542 this.maskEl.bottom.setStyle('z-index', zIndex);
11543 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
11544 this.maskEl.bottom.setLeft(0);
11545 this.maskEl.bottom.setTop(box.bottom + this.padding);
11546 this.maskEl.bottom.show();
11548 this.maskEl.right.setStyle('position', 'absolute');
11549 this.maskEl.right.setStyle('z-index', zIndex);
11550 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
11551 this.maskEl.right.setLeft(box.right + this.padding);
11552 this.maskEl.right.setTop(box.y - this.padding);
11553 this.maskEl.right.show();
11555 this.toolTip.bindEl = this.target.el;
11557 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
11559 var tip = this.target.blankText;
11561 if(this.target.getValue() !== '' ) {
11563 if (this.target.invalidText.length) {
11564 tip = this.target.invalidText;
11565 } else if (this.target.regexText.length){
11566 tip = this.target.regexText;
11570 this.toolTip.show(tip);
11572 this.intervalID = window.setInterval(function() {
11573 Roo.bootstrap.Form.popover.unmask();
11576 window.onwheel = function(){ return false;};
11578 (function(){ this.isMasked = true; }).defer(500, this);
11582 unmask : function()
11584 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
11588 this.maskEl.top.setStyle('position', 'absolute');
11589 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
11590 this.maskEl.top.hide();
11592 this.maskEl.left.setStyle('position', 'absolute');
11593 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
11594 this.maskEl.left.hide();
11596 this.maskEl.bottom.setStyle('position', 'absolute');
11597 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
11598 this.maskEl.bottom.hide();
11600 this.maskEl.right.setStyle('position', 'absolute');
11601 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
11602 this.maskEl.right.hide();
11604 this.toolTip.hide();
11606 this.toolTip.el.hide();
11608 window.onwheel = function(){ return true;};
11610 if(this.intervalID){
11611 window.clearInterval(this.intervalID);
11612 this.intervalID = false;
11615 this.isMasked = false;
11625 * Ext JS Library 1.1.1
11626 * Copyright(c) 2006-2007, Ext JS, LLC.
11628 * Originally Released Under LGPL - original licence link has changed is not relivant.
11631 * <script type="text/javascript">
11634 * @class Roo.form.VTypes
11635 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
11638 Roo.form.VTypes = function(){
11639 // closure these in so they are only created once.
11640 var alpha = /^[a-zA-Z_]+$/;
11641 var alphanum = /^[a-zA-Z0-9_]+$/;
11642 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
11643 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
11645 // All these messages and functions are configurable
11648 * The function used to validate email addresses
11649 * @param {String} value The email address
11651 'email' : function(v){
11652 return email.test(v);
11655 * The error text to display when the email validation function returns false
11658 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
11660 * The keystroke filter mask to be applied on email input
11663 'emailMask' : /[a-z0-9_\.\-@]/i,
11666 * The function used to validate URLs
11667 * @param {String} value The URL
11669 'url' : function(v){
11670 return url.test(v);
11673 * The error text to display when the url validation function returns false
11676 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
11679 * The function used to validate alpha values
11680 * @param {String} value The value
11682 'alpha' : function(v){
11683 return alpha.test(v);
11686 * The error text to display when the alpha validation function returns false
11689 'alphaText' : 'This field should only contain letters and _',
11691 * The keystroke filter mask to be applied on alpha input
11694 'alphaMask' : /[a-z_]/i,
11697 * The function used to validate alphanumeric values
11698 * @param {String} value The value
11700 'alphanum' : function(v){
11701 return alphanum.test(v);
11704 * The error text to display when the alphanumeric validation function returns false
11707 'alphanumText' : 'This field should only contain letters, numbers and _',
11709 * The keystroke filter mask to be applied on alphanumeric input
11712 'alphanumMask' : /[a-z0-9_]/i
11722 * @class Roo.bootstrap.Input
11723 * @extends Roo.bootstrap.Component
11724 * Bootstrap Input class
11725 * @cfg {Boolean} disabled is it disabled
11726 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
11727 * @cfg {String} name name of the input
11728 * @cfg {string} fieldLabel - the label associated
11729 * @cfg {string} placeholder - placeholder to put in text.
11730 * @cfg {string} before - input group add on before
11731 * @cfg {string} after - input group add on after
11732 * @cfg {string} size - (lg|sm) or leave empty..
11733 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
11734 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
11735 * @cfg {Number} md colspan out of 12 for computer-sized screens
11736 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
11737 * @cfg {string} value default value of the input
11738 * @cfg {Number} labelWidth set the width of label
11739 * @cfg {Number} labellg set the width of label (1-12)
11740 * @cfg {Number} labelmd set the width of label (1-12)
11741 * @cfg {Number} labelsm set the width of label (1-12)
11742 * @cfg {Number} labelxs set the width of label (1-12)
11743 * @cfg {String} labelAlign (top|left)
11744 * @cfg {Boolean} readOnly Specifies that the field should be read-only
11745 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
11746 * @cfg {String} indicatorpos (left|right) default left
11747 * @cfg {String} capture (user|camera) use for file input only. (default empty)
11748 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
11749 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
11751 * @cfg {String} align (left|center|right) Default left
11752 * @cfg {Boolean} forceFeedback (true|false) Default false
11755 * Create a new Input
11756 * @param {Object} config The config object
11759 Roo.bootstrap.Input = function(config){
11761 Roo.bootstrap.Input.superclass.constructor.call(this, config);
11766 * Fires when this field receives input focus.
11767 * @param {Roo.form.Field} this
11772 * Fires when this field loses input focus.
11773 * @param {Roo.form.Field} this
11777 * @event specialkey
11778 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
11779 * {@link Roo.EventObject#getKey} to determine which key was pressed.
11780 * @param {Roo.form.Field} this
11781 * @param {Roo.EventObject} e The event object
11786 * Fires just before the field blurs if the field value has changed.
11787 * @param {Roo.form.Field} this
11788 * @param {Mixed} newValue The new value
11789 * @param {Mixed} oldValue The original value
11794 * Fires after the field has been marked as invalid.
11795 * @param {Roo.form.Field} this
11796 * @param {String} msg The validation message
11801 * Fires after the field has been validated with no errors.
11802 * @param {Roo.form.Field} this
11807 * Fires after the key up
11808 * @param {Roo.form.Field} this
11809 * @param {Roo.EventObject} e The event Object
11814 * Fires after the user pastes into input
11815 * @param {Roo.form.Field} this
11816 * @param {Roo.EventObject} e The event Object
11822 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
11824 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
11825 automatic validation (defaults to "keyup").
11827 validationEvent : "keyup",
11829 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
11831 validateOnBlur : true,
11833 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
11835 validationDelay : 250,
11837 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
11839 focusClass : "x-form-focus", // not needed???
11843 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11845 invalidClass : "has-warning",
11848 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11850 validClass : "has-success",
11853 * @cfg {Boolean} hasFeedback (true|false) default true
11855 hasFeedback : true,
11858 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11860 invalidFeedbackClass : "glyphicon-warning-sign",
11863 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11865 validFeedbackClass : "glyphicon-ok",
11868 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
11870 selectOnFocus : false,
11873 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
11877 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
11882 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
11884 disableKeyFilter : false,
11887 * @cfg {Boolean} disabled True to disable the field (defaults to false).
11891 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
11895 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
11897 blankText : "Please complete this mandatory field",
11900 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
11904 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
11906 maxLength : Number.MAX_VALUE,
11908 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
11910 minLengthText : "The minimum length for this field is {0}",
11912 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
11914 maxLengthText : "The maximum length for this field is {0}",
11918 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
11919 * If available, this function will be called only after the basic validators all return true, and will be passed the
11920 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
11924 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
11925 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
11926 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
11930 * @cfg {String} regexText -- Depricated - use Invalid Text
11935 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
11941 autocomplete: false,
11945 inputType : 'text',
11948 placeholder: false,
11953 preventMark: false,
11954 isFormField : true,
11957 labelAlign : false,
11960 formatedValue : false,
11961 forceFeedback : false,
11963 indicatorpos : 'left',
11973 parentLabelAlign : function()
11976 while (parent.parent()) {
11977 parent = parent.parent();
11978 if (typeof(parent.labelAlign) !='undefined') {
11979 return parent.labelAlign;
11986 getAutoCreate : function()
11988 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11994 if(this.inputType != 'hidden'){
11995 cfg.cls = 'form-group' //input-group
12001 type : this.inputType,
12002 value : this.value,
12003 cls : 'form-control',
12004 placeholder : this.placeholder || '',
12005 autocomplete : this.autocomplete || 'new-password'
12007 if (this.inputType == 'file') {
12008 input.style = 'overflow:hidden'; // why not in CSS?
12011 if(this.capture.length){
12012 input.capture = this.capture;
12015 if(this.accept.length){
12016 input.accept = this.accept + "/*";
12020 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12023 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12024 input.maxLength = this.maxLength;
12027 if (this.disabled) {
12028 input.disabled=true;
12031 if (this.readOnly) {
12032 input.readonly=true;
12036 input.name = this.name;
12040 input.cls += ' input-' + this.size;
12044 ['xs','sm','md','lg'].map(function(size){
12045 if (settings[size]) {
12046 cfg.cls += ' col-' + size + '-' + settings[size];
12050 var inputblock = input;
12054 cls: 'glyphicon form-control-feedback'
12057 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12060 cls : 'has-feedback',
12068 if (this.before || this.after) {
12071 cls : 'input-group',
12075 if (this.before && typeof(this.before) == 'string') {
12077 inputblock.cn.push({
12079 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12083 if (this.before && typeof(this.before) == 'object') {
12084 this.before = Roo.factory(this.before);
12086 inputblock.cn.push({
12088 cls : 'roo-input-before input-group-prepend input-group-' +
12089 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12093 inputblock.cn.push(input);
12095 if (this.after && typeof(this.after) == 'string') {
12096 inputblock.cn.push({
12098 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12102 if (this.after && typeof(this.after) == 'object') {
12103 this.after = Roo.factory(this.after);
12105 inputblock.cn.push({
12107 cls : 'roo-input-after input-group-append input-group-' +
12108 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12112 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12113 inputblock.cls += ' has-feedback';
12114 inputblock.cn.push(feedback);
12119 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12120 tooltip : 'This field is required'
12122 if (this.allowBlank ) {
12123 indicator.style = this.allowBlank ? ' display:none' : '';
12125 if (align ==='left' && this.fieldLabel.length) {
12127 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12134 cls : 'control-label col-form-label',
12135 html : this.fieldLabel
12146 var labelCfg = cfg.cn[1];
12147 var contentCfg = cfg.cn[2];
12149 if(this.indicatorpos == 'right'){
12154 cls : 'control-label col-form-label',
12158 html : this.fieldLabel
12172 labelCfg = cfg.cn[0];
12173 contentCfg = cfg.cn[1];
12177 if(this.labelWidth > 12){
12178 labelCfg.style = "width: " + this.labelWidth + 'px';
12181 if(this.labelWidth < 13 && this.labelmd == 0){
12182 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12185 if(this.labellg > 0){
12186 labelCfg.cls += ' col-lg-' + this.labellg;
12187 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12190 if(this.labelmd > 0){
12191 labelCfg.cls += ' col-md-' + this.labelmd;
12192 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12195 if(this.labelsm > 0){
12196 labelCfg.cls += ' col-sm-' + this.labelsm;
12197 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12200 if(this.labelxs > 0){
12201 labelCfg.cls += ' col-xs-' + this.labelxs;
12202 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12206 } else if ( this.fieldLabel.length) {
12213 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12214 tooltip : 'This field is required',
12215 style : this.allowBlank ? ' display:none' : ''
12219 //cls : 'input-group-addon',
12220 html : this.fieldLabel
12228 if(this.indicatorpos == 'right'){
12233 //cls : 'input-group-addon',
12234 html : this.fieldLabel
12239 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12240 tooltip : 'This field is required',
12241 style : this.allowBlank ? ' display:none' : ''
12261 if (this.parentType === 'Navbar' && this.parent().bar) {
12262 cfg.cls += ' navbar-form';
12265 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12266 // on BS4 we do this only if not form
12267 cfg.cls += ' navbar-form';
12275 * return the real input element.
12277 inputEl: function ()
12279 return this.el.select('input.form-control',true).first();
12282 tooltipEl : function()
12284 return this.inputEl();
12287 indicatorEl : function()
12289 if (Roo.bootstrap.version == 4) {
12290 return false; // not enabled in v4 yet.
12293 var indicator = this.el.select('i.roo-required-indicator',true).first();
12303 setDisabled : function(v)
12305 var i = this.inputEl().dom;
12307 i.removeAttribute('disabled');
12311 i.setAttribute('disabled','true');
12313 initEvents : function()
12316 this.inputEl().on("keydown" , this.fireKey, this);
12317 this.inputEl().on("focus", this.onFocus, this);
12318 this.inputEl().on("blur", this.onBlur, this);
12320 this.inputEl().relayEvent('keyup', this);
12321 this.inputEl().relayEvent('paste', this);
12323 this.indicator = this.indicatorEl();
12325 if(this.indicator){
12326 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
12329 // reference to original value for reset
12330 this.originalValue = this.getValue();
12331 //Roo.form.TextField.superclass.initEvents.call(this);
12332 if(this.validationEvent == 'keyup'){
12333 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12334 this.inputEl().on('keyup', this.filterValidation, this);
12336 else if(this.validationEvent !== false){
12337 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12340 if(this.selectOnFocus){
12341 this.on("focus", this.preFocus, this);
12344 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12345 this.inputEl().on("keypress", this.filterKeys, this);
12347 this.inputEl().relayEvent('keypress', this);
12350 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
12351 this.el.on("click", this.autoSize, this);
12354 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12355 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12358 if (typeof(this.before) == 'object') {
12359 this.before.render(this.el.select('.roo-input-before',true).first());
12361 if (typeof(this.after) == 'object') {
12362 this.after.render(this.el.select('.roo-input-after',true).first());
12365 this.inputEl().on('change', this.onChange, this);
12368 filterValidation : function(e){
12369 if(!e.isNavKeyPress()){
12370 this.validationTask.delay(this.validationDelay);
12374 * Validates the field value
12375 * @return {Boolean} True if the value is valid, else false
12377 validate : function(){
12378 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12379 if(this.disabled || this.validateValue(this.getRawValue())){
12384 this.markInvalid();
12390 * Validates a value according to the field's validation rules and marks the field as invalid
12391 * if the validation fails
12392 * @param {Mixed} value The value to validate
12393 * @return {Boolean} True if the value is valid, else false
12395 validateValue : function(value)
12397 if(this.getVisibilityEl().hasClass('hidden')){
12401 if(value.length < 1) { // if it's blank
12402 if(this.allowBlank){
12408 if(value.length < this.minLength){
12411 if(value.length > this.maxLength){
12415 var vt = Roo.form.VTypes;
12416 if(!vt[this.vtype](value, this)){
12420 if(typeof this.validator == "function"){
12421 var msg = this.validator(value);
12425 if (typeof(msg) == 'string') {
12426 this.invalidText = msg;
12430 if(this.regex && !this.regex.test(value)){
12438 fireKey : function(e){
12439 //Roo.log('field ' + e.getKey());
12440 if(e.isNavKeyPress()){
12441 this.fireEvent("specialkey", this, e);
12444 focus : function (selectText){
12446 this.inputEl().focus();
12447 if(selectText === true){
12448 this.inputEl().dom.select();
12454 onFocus : function(){
12455 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12456 // this.el.addClass(this.focusClass);
12458 if(!this.hasFocus){
12459 this.hasFocus = true;
12460 this.startValue = this.getValue();
12461 this.fireEvent("focus", this);
12465 beforeBlur : Roo.emptyFn,
12469 onBlur : function(){
12471 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12472 //this.el.removeClass(this.focusClass);
12474 this.hasFocus = false;
12475 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12478 var v = this.getValue();
12479 if(String(v) !== String(this.startValue)){
12480 this.fireEvent('change', this, v, this.startValue);
12482 this.fireEvent("blur", this);
12485 onChange : function(e)
12487 var v = this.getValue();
12488 if(String(v) !== String(this.startValue)){
12489 this.fireEvent('change', this, v, this.startValue);
12495 * Resets the current field value to the originally loaded value and clears any validation messages
12497 reset : function(){
12498 this.setValue(this.originalValue);
12502 * Returns the name of the field
12503 * @return {Mixed} name The name field
12505 getName: function(){
12509 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
12510 * @return {Mixed} value The field value
12512 getValue : function(){
12514 var v = this.inputEl().getValue();
12519 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
12520 * @return {Mixed} value The field value
12522 getRawValue : function(){
12523 var v = this.inputEl().getValue();
12529 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
12530 * @param {Mixed} value The value to set
12532 setRawValue : function(v){
12533 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12536 selectText : function(start, end){
12537 var v = this.getRawValue();
12539 start = start === undefined ? 0 : start;
12540 end = end === undefined ? v.length : end;
12541 var d = this.inputEl().dom;
12542 if(d.setSelectionRange){
12543 d.setSelectionRange(start, end);
12544 }else if(d.createTextRange){
12545 var range = d.createTextRange();
12546 range.moveStart("character", start);
12547 range.moveEnd("character", v.length-end);
12554 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
12555 * @param {Mixed} value The value to set
12557 setValue : function(v){
12560 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12566 processValue : function(value){
12567 if(this.stripCharsRe){
12568 var newValue = value.replace(this.stripCharsRe, '');
12569 if(newValue !== value){
12570 this.setRawValue(newValue);
12577 preFocus : function(){
12579 if(this.selectOnFocus){
12580 this.inputEl().dom.select();
12583 filterKeys : function(e){
12584 var k = e.getKey();
12585 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
12588 var c = e.getCharCode(), cc = String.fromCharCode(c);
12589 if(Roo.isIE && (e.isSpecialKey() || !cc)){
12592 if(!this.maskRe.test(cc)){
12597 * Clear any invalid styles/messages for this field
12599 clearInvalid : function(){
12601 if(!this.el || this.preventMark){ // not rendered
12606 this.el.removeClass([this.invalidClass, 'is-invalid']);
12608 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12610 var feedback = this.el.select('.form-control-feedback', true).first();
12613 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12618 if(this.indicator){
12619 this.indicator.removeClass('visible');
12620 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12623 this.fireEvent('valid', this);
12627 * Mark this field as valid
12629 markValid : function()
12631 if(!this.el || this.preventMark){ // not rendered...
12635 this.el.removeClass([this.invalidClass, this.validClass]);
12636 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12638 var feedback = this.el.select('.form-control-feedback', true).first();
12641 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12644 if(this.indicator){
12645 this.indicator.removeClass('visible');
12646 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12654 if(this.allowBlank && !this.getRawValue().length){
12657 if (Roo.bootstrap.version == 3) {
12658 this.el.addClass(this.validClass);
12660 this.inputEl().addClass('is-valid');
12663 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12665 var feedback = this.el.select('.form-control-feedback', true).first();
12668 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12669 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12674 this.fireEvent('valid', this);
12678 * Mark this field as invalid
12679 * @param {String} msg The validation message
12681 markInvalid : function(msg)
12683 if(!this.el || this.preventMark){ // not rendered
12687 this.el.removeClass([this.invalidClass, this.validClass]);
12688 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12690 var feedback = this.el.select('.form-control-feedback', true).first();
12693 this.el.select('.form-control-feedback', true).first().removeClass(
12694 [this.invalidFeedbackClass, this.validFeedbackClass]);
12701 if(this.allowBlank && !this.getRawValue().length){
12705 if(this.indicator){
12706 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12707 this.indicator.addClass('visible');
12709 if (Roo.bootstrap.version == 3) {
12710 this.el.addClass(this.invalidClass);
12712 this.inputEl().addClass('is-invalid');
12717 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12719 var feedback = this.el.select('.form-control-feedback', true).first();
12722 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12724 if(this.getValue().length || this.forceFeedback){
12725 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12732 this.fireEvent('invalid', this, msg);
12735 SafariOnKeyDown : function(event)
12737 // this is a workaround for a password hang bug on chrome/ webkit.
12738 if (this.inputEl().dom.type != 'password') {
12742 var isSelectAll = false;
12744 if(this.inputEl().dom.selectionEnd > 0){
12745 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
12747 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
12748 event.preventDefault();
12753 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
12755 event.preventDefault();
12756 // this is very hacky as keydown always get's upper case.
12758 var cc = String.fromCharCode(event.getCharCode());
12759 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
12763 adjustWidth : function(tag, w){
12764 tag = tag.toLowerCase();
12765 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
12766 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
12767 if(tag == 'input'){
12770 if(tag == 'textarea'){
12773 }else if(Roo.isOpera){
12774 if(tag == 'input'){
12777 if(tag == 'textarea'){
12785 setFieldLabel : function(v)
12787 if(!this.rendered){
12791 if(this.indicatorEl()){
12792 var ar = this.el.select('label > span',true);
12794 if (ar.elements.length) {
12795 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12796 this.fieldLabel = v;
12800 var br = this.el.select('label',true);
12802 if(br.elements.length) {
12803 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12804 this.fieldLabel = v;
12808 Roo.log('Cannot Found any of label > span || label in input');
12812 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12813 this.fieldLabel = v;
12828 * @class Roo.bootstrap.TextArea
12829 * @extends Roo.bootstrap.Input
12830 * Bootstrap TextArea class
12831 * @cfg {Number} cols Specifies the visible width of a text area
12832 * @cfg {Number} rows Specifies the visible number of lines in a text area
12833 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
12834 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
12835 * @cfg {string} html text
12838 * Create a new TextArea
12839 * @param {Object} config The config object
12842 Roo.bootstrap.TextArea = function(config){
12843 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
12847 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
12857 getAutoCreate : function(){
12859 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12865 if(this.inputType != 'hidden'){
12866 cfg.cls = 'form-group' //input-group
12874 value : this.value || '',
12875 html: this.html || '',
12876 cls : 'form-control',
12877 placeholder : this.placeholder || ''
12881 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12882 input.maxLength = this.maxLength;
12886 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
12890 input.cols = this.cols;
12893 if (this.readOnly) {
12894 input.readonly = true;
12898 input.name = this.name;
12902 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
12906 ['xs','sm','md','lg'].map(function(size){
12907 if (settings[size]) {
12908 cfg.cls += ' col-' + size + '-' + settings[size];
12912 var inputblock = input;
12914 if(this.hasFeedback && !this.allowBlank){
12918 cls: 'glyphicon form-control-feedback'
12922 cls : 'has-feedback',
12931 if (this.before || this.after) {
12934 cls : 'input-group',
12938 inputblock.cn.push({
12940 cls : 'input-group-addon',
12945 inputblock.cn.push(input);
12947 if(this.hasFeedback && !this.allowBlank){
12948 inputblock.cls += ' has-feedback';
12949 inputblock.cn.push(feedback);
12953 inputblock.cn.push({
12955 cls : 'input-group-addon',
12962 if (align ==='left' && this.fieldLabel.length) {
12967 cls : 'control-label',
12968 html : this.fieldLabel
12979 if(this.labelWidth > 12){
12980 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
12983 if(this.labelWidth < 13 && this.labelmd == 0){
12984 this.labelmd = this.labelWidth;
12987 if(this.labellg > 0){
12988 cfg.cn[0].cls += ' col-lg-' + this.labellg;
12989 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
12992 if(this.labelmd > 0){
12993 cfg.cn[0].cls += ' col-md-' + this.labelmd;
12994 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
12997 if(this.labelsm > 0){
12998 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
12999 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13002 if(this.labelxs > 0){
13003 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13004 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13007 } else if ( this.fieldLabel.length) {
13012 //cls : 'input-group-addon',
13013 html : this.fieldLabel
13031 if (this.disabled) {
13032 input.disabled=true;
13039 * return the real textarea element.
13041 inputEl: function ()
13043 return this.el.select('textarea.form-control',true).first();
13047 * Clear any invalid styles/messages for this field
13049 clearInvalid : function()
13052 if(!this.el || this.preventMark){ // not rendered
13056 var label = this.el.select('label', true).first();
13057 var icon = this.el.select('i.fa-star', true).first();
13062 this.el.removeClass( this.validClass);
13063 this.inputEl().removeClass('is-invalid');
13065 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13067 var feedback = this.el.select('.form-control-feedback', true).first();
13070 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13075 this.fireEvent('valid', this);
13079 * Mark this field as valid
13081 markValid : function()
13083 if(!this.el || this.preventMark){ // not rendered
13087 this.el.removeClass([this.invalidClass, this.validClass]);
13088 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13090 var feedback = this.el.select('.form-control-feedback', true).first();
13093 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13096 if(this.disabled || this.allowBlank){
13100 var label = this.el.select('label', true).first();
13101 var icon = this.el.select('i.fa-star', true).first();
13106 if (Roo.bootstrap.version == 3) {
13107 this.el.addClass(this.validClass);
13109 this.inputEl().addClass('is-valid');
13113 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13115 var feedback = this.el.select('.form-control-feedback', true).first();
13118 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13119 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13124 this.fireEvent('valid', this);
13128 * Mark this field as invalid
13129 * @param {String} msg The validation message
13131 markInvalid : function(msg)
13133 if(!this.el || this.preventMark){ // not rendered
13137 this.el.removeClass([this.invalidClass, this.validClass]);
13138 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13140 var feedback = this.el.select('.form-control-feedback', true).first();
13143 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13146 if(this.disabled || this.allowBlank){
13150 var label = this.el.select('label', true).first();
13151 var icon = this.el.select('i.fa-star', true).first();
13153 if(!this.getValue().length && label && !icon){
13154 this.el.createChild({
13156 cls : 'text-danger fa fa-lg fa-star',
13157 tooltip : 'This field is required',
13158 style : 'margin-right:5px;'
13162 if (Roo.bootstrap.version == 3) {
13163 this.el.addClass(this.invalidClass);
13165 this.inputEl().addClass('is-invalid');
13168 // fixme ... this may be depricated need to test..
13169 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13171 var feedback = this.el.select('.form-control-feedback', true).first();
13174 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13176 if(this.getValue().length || this.forceFeedback){
13177 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13184 this.fireEvent('invalid', this, msg);
13192 * trigger field - base class for combo..
13197 * @class Roo.bootstrap.TriggerField
13198 * @extends Roo.bootstrap.Input
13199 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13200 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13201 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13202 * for which you can provide a custom implementation. For example:
13204 var trigger = new Roo.bootstrap.TriggerField();
13205 trigger.onTriggerClick = myTriggerFn;
13206 trigger.applyTo('my-field');
13209 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13210 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
13211 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
13212 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13213 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13216 * Create a new TriggerField.
13217 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
13218 * to the base TextField)
13220 Roo.bootstrap.TriggerField = function(config){
13221 this.mimicing = false;
13222 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
13225 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
13227 * @cfg {String} triggerClass A CSS class to apply to the trigger
13230 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13235 * @cfg {Boolean} removable (true|false) special filter default false
13239 /** @cfg {Boolean} grow @hide */
13240 /** @cfg {Number} growMin @hide */
13241 /** @cfg {Number} growMax @hide */
13247 autoSize: Roo.emptyFn,
13251 deferHeight : true,
13254 actionMode : 'wrap',
13259 getAutoCreate : function(){
13261 var align = this.labelAlign || this.parentLabelAlign();
13266 cls: 'form-group' //input-group
13273 type : this.inputType,
13274 cls : 'form-control',
13275 autocomplete: 'new-password',
13276 placeholder : this.placeholder || ''
13280 input.name = this.name;
13283 input.cls += ' input-' + this.size;
13286 if (this.disabled) {
13287 input.disabled=true;
13290 var inputblock = input;
13292 if(this.hasFeedback && !this.allowBlank){
13296 cls: 'glyphicon form-control-feedback'
13299 if(this.removable && !this.editable ){
13301 cls : 'has-feedback',
13307 cls : 'roo-combo-removable-btn close'
13314 cls : 'has-feedback',
13323 if(this.removable && !this.editable ){
13325 cls : 'roo-removable',
13331 cls : 'roo-combo-removable-btn close'
13338 if (this.before || this.after) {
13341 cls : 'input-group',
13345 inputblock.cn.push({
13347 cls : 'input-group-addon input-group-prepend input-group-text',
13352 inputblock.cn.push(input);
13354 if(this.hasFeedback && !this.allowBlank){
13355 inputblock.cls += ' has-feedback';
13356 inputblock.cn.push(feedback);
13360 inputblock.cn.push({
13362 cls : 'input-group-addon input-group-append input-group-text',
13371 var ibwrap = inputblock;
13376 cls: 'roo-select2-choices',
13380 cls: 'roo-select2-search-field',
13392 cls: 'roo-select2-container input-group',
13397 cls: 'form-hidden-field'
13403 if(!this.multiple && this.showToggleBtn){
13409 if (this.caret != false) {
13412 cls: 'fa fa-' + this.caret
13419 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13421 Roo.bootstrap.version == 3 ? caret : '',
13424 cls: 'combobox-clear',
13438 combobox.cls += ' roo-select2-container-multi';
13442 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13443 tooltip : 'This field is required'
13445 if (Roo.bootstrap.version == 4) {
13448 style : 'display:none'
13453 if (align ==='left' && this.fieldLabel.length) {
13455 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
13462 cls : 'control-label',
13463 html : this.fieldLabel
13475 var labelCfg = cfg.cn[1];
13476 var contentCfg = cfg.cn[2];
13478 if(this.indicatorpos == 'right'){
13483 cls : 'control-label',
13487 html : this.fieldLabel
13501 labelCfg = cfg.cn[0];
13502 contentCfg = cfg.cn[1];
13505 if(this.labelWidth > 12){
13506 labelCfg.style = "width: " + this.labelWidth + 'px';
13509 if(this.labelWidth < 13 && this.labelmd == 0){
13510 this.labelmd = this.labelWidth;
13513 if(this.labellg > 0){
13514 labelCfg.cls += ' col-lg-' + this.labellg;
13515 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13518 if(this.labelmd > 0){
13519 labelCfg.cls += ' col-md-' + this.labelmd;
13520 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13523 if(this.labelsm > 0){
13524 labelCfg.cls += ' col-sm-' + this.labelsm;
13525 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13528 if(this.labelxs > 0){
13529 labelCfg.cls += ' col-xs-' + this.labelxs;
13530 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13533 } else if ( this.fieldLabel.length) {
13534 // Roo.log(" label");
13539 //cls : 'input-group-addon',
13540 html : this.fieldLabel
13548 if(this.indicatorpos == 'right'){
13556 html : this.fieldLabel
13570 // Roo.log(" no label && no align");
13577 ['xs','sm','md','lg'].map(function(size){
13578 if (settings[size]) {
13579 cfg.cls += ' col-' + size + '-' + settings[size];
13590 onResize : function(w, h){
13591 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
13592 // if(typeof w == 'number'){
13593 // var x = w - this.trigger.getWidth();
13594 // this.inputEl().setWidth(this.adjustWidth('input', x));
13595 // this.trigger.setStyle('left', x+'px');
13600 adjustSize : Roo.BoxComponent.prototype.adjustSize,
13603 getResizeEl : function(){
13604 return this.inputEl();
13608 getPositionEl : function(){
13609 return this.inputEl();
13613 alignErrorIcon : function(){
13614 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
13618 initEvents : function(){
13622 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
13623 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
13624 if(!this.multiple && this.showToggleBtn){
13625 this.trigger = this.el.select('span.dropdown-toggle',true).first();
13626 if(this.hideTrigger){
13627 this.trigger.setDisplayed(false);
13629 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
13633 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
13636 if(this.removable && !this.editable && !this.tickable){
13637 var close = this.closeTriggerEl();
13640 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13641 close.on('click', this.removeBtnClick, this, close);
13645 //this.trigger.addClassOnOver('x-form-trigger-over');
13646 //this.trigger.addClassOnClick('x-form-trigger-click');
13649 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
13653 closeTriggerEl : function()
13655 var close = this.el.select('.roo-combo-removable-btn', true).first();
13656 return close ? close : false;
13659 removeBtnClick : function(e, h, el)
13661 e.preventDefault();
13663 if(this.fireEvent("remove", this) !== false){
13665 this.fireEvent("afterremove", this)
13669 createList : function()
13671 this.list = Roo.get(document.body).createChild({
13672 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
13673 cls: 'typeahead typeahead-long dropdown-menu shadow',
13674 style: 'display:none'
13677 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
13682 initTrigger : function(){
13687 onDestroy : function(){
13689 this.trigger.removeAllListeners();
13690 // this.trigger.remove();
13693 // this.wrap.remove();
13695 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
13699 onFocus : function(){
13700 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
13702 if(!this.mimicing){
13703 this.wrap.addClass('x-trigger-wrap-focus');
13704 this.mimicing = true;
13705 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
13706 if(this.monitorTab){
13707 this.el.on("keydown", this.checkTab, this);
13714 checkTab : function(e){
13715 if(e.getKey() == e.TAB){
13716 this.triggerBlur();
13721 onBlur : function(){
13726 mimicBlur : function(e, t){
13728 if(!this.wrap.contains(t) && this.validateBlur()){
13729 this.triggerBlur();
13735 triggerBlur : function(){
13736 this.mimicing = false;
13737 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
13738 if(this.monitorTab){
13739 this.el.un("keydown", this.checkTab, this);
13741 //this.wrap.removeClass('x-trigger-wrap-focus');
13742 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
13746 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
13747 validateBlur : function(e, t){
13752 onDisable : function(){
13753 this.inputEl().dom.disabled = true;
13754 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
13756 // this.wrap.addClass('x-item-disabled');
13761 onEnable : function(){
13762 this.inputEl().dom.disabled = false;
13763 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
13765 // this.el.removeClass('x-item-disabled');
13770 onShow : function(){
13771 var ae = this.getActionEl();
13774 ae.dom.style.display = '';
13775 ae.dom.style.visibility = 'visible';
13781 onHide : function(){
13782 var ae = this.getActionEl();
13783 ae.dom.style.display = 'none';
13787 * The function that should handle the trigger's click event. This method does nothing by default until overridden
13788 * by an implementing function.
13790 * @param {EventObject} e
13792 onTriggerClick : Roo.emptyFn
13800 * @class Roo.bootstrap.CardUploader
13801 * @extends Roo.bootstrap.Button
13802 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
13803 * @cfg {Number} errorTimeout default 3000
13804 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
13805 * @cfg {Array} html The button text.
13809 * Create a new CardUploader
13810 * @param {Object} config The config object
13813 Roo.bootstrap.CardUploader = function(config){
13817 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
13820 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
13828 * When a image is clicked on - and needs to display a slideshow or similar..
13829 * @param {Roo.bootstrap.Card} this
13830 * @param {Object} The image information data
13836 * When a the download link is clicked
13837 * @param {Roo.bootstrap.Card} this
13838 * @param {Object} The image information data contains
13845 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
13848 errorTimeout : 3000,
13852 fileCollection : false,
13855 getAutoCreate : function()
13859 cls :'form-group' ,
13864 //cls : 'input-group-addon',
13865 html : this.fieldLabel
13873 value : this.value,
13874 cls : 'd-none form-control'
13879 multiple : 'multiple',
13881 cls : 'd-none roo-card-upload-selector'
13885 cls : 'roo-card-uploader-button-container w-100 mb-2'
13888 cls : 'card-columns roo-card-uploader-container'
13898 getChildContainer : function() /// what children are added to.
13900 return this.containerEl;
13903 getButtonContainer : function() /// what children are added to.
13905 return this.el.select(".roo-card-uploader-button-container").first();
13908 initEvents : function()
13911 Roo.bootstrap.Input.prototype.initEvents.call(this);
13915 xns: Roo.bootstrap,
13918 container_method : 'getButtonContainer' ,
13919 html : this.html, // fix changable?
13922 'click' : function(btn, e) {
13931 this.urlAPI = (window.createObjectURL && window) ||
13932 (window.URL && URL.revokeObjectURL && URL) ||
13933 (window.webkitURL && webkitURL);
13938 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
13940 this.selectorEl.on('change', this.onFileSelected, this);
13943 this.images.forEach(function(img) {
13946 this.images = false;
13948 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
13954 onClick : function(e)
13956 e.preventDefault();
13958 this.selectorEl.dom.click();
13962 onFileSelected : function(e)
13964 e.preventDefault();
13966 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
13970 Roo.each(this.selectorEl.dom.files, function(file){
13971 this.addFile(file);
13980 addFile : function(file)
13983 if(typeof(file) === 'string'){
13984 throw "Add file by name?"; // should not happen
13988 if(!file || !this.urlAPI){
13998 var url = _this.urlAPI.createObjectURL( file);
14001 id : Roo.bootstrap.CardUploader.ID--,
14002 is_uploaded : false,
14006 mimetype : file.type,
14014 * addCard - add an Attachment to the uploader
14015 * @param data - the data about the image to upload
14019 title : "Title of file",
14020 is_uploaded : false,
14021 src : "http://.....",
14022 srcfile : { the File upload object },
14023 mimetype : file.type,
14026 .. any other data...
14032 addCard : function (data)
14034 // hidden input element?
14035 // if the file is not an image...
14036 //then we need to use something other that and header_image
14041 xns : Roo.bootstrap,
14042 xtype : 'CardFooter',
14045 xns : Roo.bootstrap,
14051 xns : Roo.bootstrap,
14053 html : String.format("<small>{0}</small>", data.title),
14054 cls : 'col-10 text-left',
14059 click : function() {
14061 t.fireEvent( "download", t, data );
14067 xns : Roo.bootstrap,
14069 style: 'max-height: 28px; ',
14075 click : function() {
14076 t.removeCard(data.id)
14088 var cn = this.addxtype(
14091 xns : Roo.bootstrap,
14094 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14095 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
14096 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
14101 initEvents : function() {
14102 Roo.bootstrap.Card.prototype.initEvents.call(this);
14104 this.imgEl = this.el.select('.card-img-top').first();
14106 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14107 this.imgEl.set({ 'pointer' : 'cursor' });
14110 this.getCardFooter().addClass('p-1');
14117 // dont' really need ot update items.
14118 // this.items.push(cn);
14119 this.fileCollection.add(cn);
14121 if (!data.srcfile) {
14122 this.updateInput();
14127 var reader = new FileReader();
14128 reader.addEventListener("load", function() {
14129 data.srcdata = reader.result;
14132 reader.readAsDataURL(data.srcfile);
14137 removeCard : function(id)
14140 var card = this.fileCollection.get(id);
14141 card.data.is_deleted = 1;
14142 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14143 //this.fileCollection.remove(card);
14144 //this.items = this.items.filter(function(e) { return e != card });
14145 // dont' really need ot update items.
14146 card.el.dom.parentNode.removeChild(card.el.dom);
14147 this.updateInput();
14153 this.fileCollection.each(function(card) {
14154 if (card.el.dom && card.el.dom.parentNode) {
14155 card.el.dom.parentNode.removeChild(card.el.dom);
14158 this.fileCollection.clear();
14159 this.updateInput();
14162 updateInput : function()
14165 this.fileCollection.each(function(e) {
14169 this.inputEl().dom.value = JSON.stringify(data);
14179 Roo.bootstrap.CardUploader.ID = -1;/*
14181 * Ext JS Library 1.1.1
14182 * Copyright(c) 2006-2007, Ext JS, LLC.
14184 * Originally Released Under LGPL - original licence link has changed is not relivant.
14187 * <script type="text/javascript">
14192 * @class Roo.data.SortTypes
14194 * Defines the default sorting (casting?) comparison functions used when sorting data.
14196 Roo.data.SortTypes = {
14198 * Default sort that does nothing
14199 * @param {Mixed} s The value being converted
14200 * @return {Mixed} The comparison value
14202 none : function(s){
14207 * The regular expression used to strip tags
14211 stripTagsRE : /<\/?[^>]+>/gi,
14214 * Strips all HTML tags to sort on text only
14215 * @param {Mixed} s The value being converted
14216 * @return {String} The comparison value
14218 asText : function(s){
14219 return String(s).replace(this.stripTagsRE, "");
14223 * Strips all HTML tags to sort on text only - Case insensitive
14224 * @param {Mixed} s The value being converted
14225 * @return {String} The comparison value
14227 asUCText : function(s){
14228 return String(s).toUpperCase().replace(this.stripTagsRE, "");
14232 * Case insensitive string
14233 * @param {Mixed} s The value being converted
14234 * @return {String} The comparison value
14236 asUCString : function(s) {
14237 return String(s).toUpperCase();
14242 * @param {Mixed} s The value being converted
14243 * @return {Number} The comparison value
14245 asDate : function(s) {
14249 if(s instanceof Date){
14250 return s.getTime();
14252 return Date.parse(String(s));
14257 * @param {Mixed} s The value being converted
14258 * @return {Float} The comparison value
14260 asFloat : function(s) {
14261 var val = parseFloat(String(s).replace(/,/g, ""));
14270 * @param {Mixed} s The value being converted
14271 * @return {Number} The comparison value
14273 asInt : function(s) {
14274 var val = parseInt(String(s).replace(/,/g, ""));
14282 * Ext JS Library 1.1.1
14283 * Copyright(c) 2006-2007, Ext JS, LLC.
14285 * Originally Released Under LGPL - original licence link has changed is not relivant.
14288 * <script type="text/javascript">
14292 * @class Roo.data.Record
14293 * Instances of this class encapsulate both record <em>definition</em> information, and record
14294 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14295 * to access Records cached in an {@link Roo.data.Store} object.<br>
14297 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14298 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14301 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14303 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14304 * {@link #create}. The parameters are the same.
14305 * @param {Array} data An associative Array of data values keyed by the field name.
14306 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14307 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14308 * not specified an integer id is generated.
14310 Roo.data.Record = function(data, id){
14311 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14316 * Generate a constructor for a specific record layout.
14317 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14318 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14319 * Each field definition object may contain the following properties: <ul>
14320 * <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,
14321 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14322 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14323 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14324 * is being used, then this is a string containing the javascript expression to reference the data relative to
14325 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14326 * to the data item relative to the record element. If the mapping expression is the same as the field name,
14327 * this may be omitted.</p></li>
14328 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14329 * <ul><li>auto (Default, implies no conversion)</li>
14334 * <li>date</li></ul></p></li>
14335 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14336 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14337 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14338 * by the Reader into an object that will be stored in the Record. It is passed the
14339 * following parameters:<ul>
14340 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14342 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14344 * <br>usage:<br><pre><code>
14345 var TopicRecord = Roo.data.Record.create(
14346 {name: 'title', mapping: 'topic_title'},
14347 {name: 'author', mapping: 'username'},
14348 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14349 {name: 'lastPost', mapping: 'post_time', type: 'date'},
14350 {name: 'lastPoster', mapping: 'user2'},
14351 {name: 'excerpt', mapping: 'post_text'}
14354 var myNewRecord = new TopicRecord({
14355 title: 'Do my job please',
14358 lastPost: new Date(),
14359 lastPoster: 'Animal',
14360 excerpt: 'No way dude!'
14362 myStore.add(myNewRecord);
14367 Roo.data.Record.create = function(o){
14368 var f = function(){
14369 f.superclass.constructor.apply(this, arguments);
14371 Roo.extend(f, Roo.data.Record);
14372 var p = f.prototype;
14373 p.fields = new Roo.util.MixedCollection(false, function(field){
14376 for(var i = 0, len = o.length; i < len; i++){
14377 p.fields.add(new Roo.data.Field(o[i]));
14379 f.getField = function(name){
14380 return p.fields.get(name);
14385 Roo.data.Record.AUTO_ID = 1000;
14386 Roo.data.Record.EDIT = 'edit';
14387 Roo.data.Record.REJECT = 'reject';
14388 Roo.data.Record.COMMIT = 'commit';
14390 Roo.data.Record.prototype = {
14392 * Readonly flag - true if this record has been modified.
14401 join : function(store){
14402 this.store = store;
14406 * Set the named field to the specified value.
14407 * @param {String} name The name of the field to set.
14408 * @param {Object} value The value to set the field to.
14410 set : function(name, value){
14411 if(this.data[name] == value){
14415 if(!this.modified){
14416 this.modified = {};
14418 if(typeof this.modified[name] == 'undefined'){
14419 this.modified[name] = this.data[name];
14421 this.data[name] = value;
14422 if(!this.editing && this.store){
14423 this.store.afterEdit(this);
14428 * Get the value of the named field.
14429 * @param {String} name The name of the field to get the value of.
14430 * @return {Object} The value of the field.
14432 get : function(name){
14433 return this.data[name];
14437 beginEdit : function(){
14438 this.editing = true;
14439 this.modified = {};
14443 cancelEdit : function(){
14444 this.editing = false;
14445 delete this.modified;
14449 endEdit : function(){
14450 this.editing = false;
14451 if(this.dirty && this.store){
14452 this.store.afterEdit(this);
14457 * Usually called by the {@link Roo.data.Store} which owns the Record.
14458 * Rejects all changes made to the Record since either creation, or the last commit operation.
14459 * Modified fields are reverted to their original values.
14461 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14462 * of reject operations.
14464 reject : function(){
14465 var m = this.modified;
14467 if(typeof m[n] != "function"){
14468 this.data[n] = m[n];
14471 this.dirty = false;
14472 delete this.modified;
14473 this.editing = false;
14475 this.store.afterReject(this);
14480 * Usually called by the {@link Roo.data.Store} which owns the Record.
14481 * Commits all changes made to the Record since either creation, or the last commit operation.
14483 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14484 * of commit operations.
14486 commit : function(){
14487 this.dirty = false;
14488 delete this.modified;
14489 this.editing = false;
14491 this.store.afterCommit(this);
14496 hasError : function(){
14497 return this.error != null;
14501 clearError : function(){
14506 * Creates a copy of this record.
14507 * @param {String} id (optional) A new record id if you don't want to use this record's id
14510 copy : function(newId) {
14511 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
14515 * Ext JS Library 1.1.1
14516 * Copyright(c) 2006-2007, Ext JS, LLC.
14518 * Originally Released Under LGPL - original licence link has changed is not relivant.
14521 * <script type="text/javascript">
14527 * @class Roo.data.Store
14528 * @extends Roo.util.Observable
14529 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
14530 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
14532 * 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
14533 * has no knowledge of the format of the data returned by the Proxy.<br>
14535 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
14536 * instances from the data object. These records are cached and made available through accessor functions.
14538 * Creates a new Store.
14539 * @param {Object} config A config object containing the objects needed for the Store to access data,
14540 * and read the data into Records.
14542 Roo.data.Store = function(config){
14543 this.data = new Roo.util.MixedCollection(false);
14544 this.data.getKey = function(o){
14547 this.baseParams = {};
14549 this.paramNames = {
14554 "multisort" : "_multisort"
14557 if(config && config.data){
14558 this.inlineData = config.data;
14559 delete config.data;
14562 Roo.apply(this, config);
14564 if(this.reader){ // reader passed
14565 this.reader = Roo.factory(this.reader, Roo.data);
14566 this.reader.xmodule = this.xmodule || false;
14567 if(!this.recordType){
14568 this.recordType = this.reader.recordType;
14570 if(this.reader.onMetaChange){
14571 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
14575 if(this.recordType){
14576 this.fields = this.recordType.prototype.fields;
14578 this.modified = [];
14582 * @event datachanged
14583 * Fires when the data cache has changed, and a widget which is using this Store
14584 * as a Record cache should refresh its view.
14585 * @param {Store} this
14587 datachanged : true,
14589 * @event metachange
14590 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
14591 * @param {Store} this
14592 * @param {Object} meta The JSON metadata
14597 * Fires when Records have been added to the Store
14598 * @param {Store} this
14599 * @param {Roo.data.Record[]} records The array of Records added
14600 * @param {Number} index The index at which the record(s) were added
14605 * Fires when a Record has been removed from the Store
14606 * @param {Store} this
14607 * @param {Roo.data.Record} record The Record that was removed
14608 * @param {Number} index The index at which the record was removed
14613 * Fires when a Record has been updated
14614 * @param {Store} this
14615 * @param {Roo.data.Record} record The Record that was updated
14616 * @param {String} operation The update operation being performed. Value may be one of:
14618 Roo.data.Record.EDIT
14619 Roo.data.Record.REJECT
14620 Roo.data.Record.COMMIT
14626 * Fires when the data cache has been cleared.
14627 * @param {Store} this
14631 * @event beforeload
14632 * Fires before a request is made for a new data object. If the beforeload handler returns false
14633 * the load action will be canceled.
14634 * @param {Store} this
14635 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14639 * @event beforeloadadd
14640 * Fires after a new set of Records has been loaded.
14641 * @param {Store} this
14642 * @param {Roo.data.Record[]} records The Records that were loaded
14643 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14645 beforeloadadd : true,
14648 * Fires after a new set of Records has been loaded, before they are added to the store.
14649 * @param {Store} this
14650 * @param {Roo.data.Record[]} records The Records that were loaded
14651 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14652 * @params {Object} return from reader
14656 * @event loadexception
14657 * Fires if an exception occurs in the Proxy during loading.
14658 * Called with the signature of the Proxy's "loadexception" event.
14659 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
14662 * @param {Object} return from JsonData.reader() - success, totalRecords, records
14663 * @param {Object} load options
14664 * @param {Object} jsonData from your request (normally this contains the Exception)
14666 loadexception : true
14670 this.proxy = Roo.factory(this.proxy, Roo.data);
14671 this.proxy.xmodule = this.xmodule || false;
14672 this.relayEvents(this.proxy, ["loadexception"]);
14674 this.sortToggle = {};
14675 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
14677 Roo.data.Store.superclass.constructor.call(this);
14679 if(this.inlineData){
14680 this.loadData(this.inlineData);
14681 delete this.inlineData;
14685 Roo.extend(Roo.data.Store, Roo.util.Observable, {
14687 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
14688 * without a remote query - used by combo/forms at present.
14692 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
14695 * @cfg {Array} data Inline data to be loaded when the store is initialized.
14698 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
14699 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
14702 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
14703 * on any HTTP request
14706 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
14709 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
14713 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
14714 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
14716 remoteSort : false,
14719 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
14720 * loaded or when a record is removed. (defaults to false).
14722 pruneModifiedRecords : false,
14725 lastOptions : null,
14728 * Add Records to the Store and fires the add event.
14729 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14731 add : function(records){
14732 records = [].concat(records);
14733 for(var i = 0, len = records.length; i < len; i++){
14734 records[i].join(this);
14736 var index = this.data.length;
14737 this.data.addAll(records);
14738 this.fireEvent("add", this, records, index);
14742 * Remove a Record from the Store and fires the remove event.
14743 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
14745 remove : function(record){
14746 var index = this.data.indexOf(record);
14747 this.data.removeAt(index);
14749 if(this.pruneModifiedRecords){
14750 this.modified.remove(record);
14752 this.fireEvent("remove", this, record, index);
14756 * Remove all Records from the Store and fires the clear event.
14758 removeAll : function(){
14760 if(this.pruneModifiedRecords){
14761 this.modified = [];
14763 this.fireEvent("clear", this);
14767 * Inserts Records to the Store at the given index and fires the add event.
14768 * @param {Number} index The start index at which to insert the passed Records.
14769 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14771 insert : function(index, records){
14772 records = [].concat(records);
14773 for(var i = 0, len = records.length; i < len; i++){
14774 this.data.insert(index, records[i]);
14775 records[i].join(this);
14777 this.fireEvent("add", this, records, index);
14781 * Get the index within the cache of the passed Record.
14782 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
14783 * @return {Number} The index of the passed Record. Returns -1 if not found.
14785 indexOf : function(record){
14786 return this.data.indexOf(record);
14790 * Get the index within the cache of the Record with the passed id.
14791 * @param {String} id The id of the Record to find.
14792 * @return {Number} The index of the Record. Returns -1 if not found.
14794 indexOfId : function(id){
14795 return this.data.indexOfKey(id);
14799 * Get the Record with the specified id.
14800 * @param {String} id The id of the Record to find.
14801 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
14803 getById : function(id){
14804 return this.data.key(id);
14808 * Get the Record at the specified index.
14809 * @param {Number} index The index of the Record to find.
14810 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
14812 getAt : function(index){
14813 return this.data.itemAt(index);
14817 * Returns a range of Records between specified indices.
14818 * @param {Number} startIndex (optional) The starting index (defaults to 0)
14819 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
14820 * @return {Roo.data.Record[]} An array of Records
14822 getRange : function(start, end){
14823 return this.data.getRange(start, end);
14827 storeOptions : function(o){
14828 o = Roo.apply({}, o);
14831 this.lastOptions = o;
14835 * Loads the Record cache from the configured Proxy using the configured Reader.
14837 * If using remote paging, then the first load call must specify the <em>start</em>
14838 * and <em>limit</em> properties in the options.params property to establish the initial
14839 * position within the dataset, and the number of Records to cache on each read from the Proxy.
14841 * <strong>It is important to note that for remote data sources, loading is asynchronous,
14842 * and this call will return before the new data has been loaded. Perform any post-processing
14843 * in a callback function, or in a "load" event handler.</strong>
14845 * @param {Object} options An object containing properties which control loading options:<ul>
14846 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
14847 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
14848 * passed the following arguments:<ul>
14849 * <li>r : Roo.data.Record[]</li>
14850 * <li>options: Options object from the load call</li>
14851 * <li>success: Boolean success indicator</li></ul></li>
14852 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
14853 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
14856 load : function(options){
14857 options = options || {};
14858 if(this.fireEvent("beforeload", this, options) !== false){
14859 this.storeOptions(options);
14860 var p = Roo.apply(options.params || {}, this.baseParams);
14861 // if meta was not loaded from remote source.. try requesting it.
14862 if (!this.reader.metaFromRemote) {
14863 p._requestMeta = 1;
14865 if(this.sortInfo && this.remoteSort){
14866 var pn = this.paramNames;
14867 p[pn["sort"]] = this.sortInfo.field;
14868 p[pn["dir"]] = this.sortInfo.direction;
14870 if (this.multiSort) {
14871 var pn = this.paramNames;
14872 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
14875 this.proxy.load(p, this.reader, this.loadRecords, this, options);
14880 * Reloads the Record cache from the configured Proxy using the configured Reader and
14881 * the options from the last load operation performed.
14882 * @param {Object} options (optional) An object containing properties which may override the options
14883 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
14884 * the most recently used options are reused).
14886 reload : function(options){
14887 this.load(Roo.applyIf(options||{}, this.lastOptions));
14891 // Called as a callback by the Reader during a load operation.
14892 loadRecords : function(o, options, success){
14893 if(!o || success === false){
14894 if(success !== false){
14895 this.fireEvent("load", this, [], options, o);
14897 if(options.callback){
14898 options.callback.call(options.scope || this, [], options, false);
14902 // if data returned failure - throw an exception.
14903 if (o.success === false) {
14904 // show a message if no listener is registered.
14905 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
14906 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
14908 // loadmask wil be hooked into this..
14909 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
14912 var r = o.records, t = o.totalRecords || r.length;
14914 this.fireEvent("beforeloadadd", this, r, options, o);
14916 if(!options || options.add !== true){
14917 if(this.pruneModifiedRecords){
14918 this.modified = [];
14920 for(var i = 0, len = r.length; i < len; i++){
14924 this.data = this.snapshot;
14925 delete this.snapshot;
14928 this.data.addAll(r);
14929 this.totalLength = t;
14931 this.fireEvent("datachanged", this);
14933 this.totalLength = Math.max(t, this.data.length+r.length);
14937 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
14939 var e = new Roo.data.Record({});
14941 e.set(this.parent.displayField, this.parent.emptyTitle);
14942 e.set(this.parent.valueField, '');
14947 this.fireEvent("load", this, r, options, o);
14948 if(options.callback){
14949 options.callback.call(options.scope || this, r, options, true);
14955 * Loads data from a passed data block. A Reader which understands the format of the data
14956 * must have been configured in the constructor.
14957 * @param {Object} data The data block from which to read the Records. The format of the data expected
14958 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
14959 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
14961 loadData : function(o, append){
14962 var r = this.reader.readRecords(o);
14963 this.loadRecords(r, {add: append}, true);
14967 * using 'cn' the nested child reader read the child array into it's child stores.
14968 * @param {Object} rec The record with a 'children array
14970 loadDataFromChildren : function(rec)
14972 this.loadData(this.reader.toLoadData(rec));
14977 * Gets the number of cached records.
14979 * <em>If using paging, this may not be the total size of the dataset. If the data object
14980 * used by the Reader contains the dataset size, then the getTotalCount() function returns
14981 * the data set size</em>
14983 getCount : function(){
14984 return this.data.length || 0;
14988 * Gets the total number of records in the dataset as returned by the server.
14990 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
14991 * the dataset size</em>
14993 getTotalCount : function(){
14994 return this.totalLength || 0;
14998 * Returns the sort state of the Store as an object with two properties:
15000 field {String} The name of the field by which the Records are sorted
15001 direction {String} The sort order, "ASC" or "DESC"
15004 getSortState : function(){
15005 return this.sortInfo;
15009 applySort : function(){
15010 if(this.sortInfo && !this.remoteSort){
15011 var s = this.sortInfo, f = s.field;
15012 var st = this.fields.get(f).sortType;
15013 var fn = function(r1, r2){
15014 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15015 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15017 this.data.sort(s.direction, fn);
15018 if(this.snapshot && this.snapshot != this.data){
15019 this.snapshot.sort(s.direction, fn);
15025 * Sets the default sort column and order to be used by the next load operation.
15026 * @param {String} fieldName The name of the field to sort by.
15027 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15029 setDefaultSort : function(field, dir){
15030 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15034 * Sort the Records.
15035 * If remote sorting is used, the sort is performed on the server, and the cache is
15036 * reloaded. If local sorting is used, the cache is sorted internally.
15037 * @param {String} fieldName The name of the field to sort by.
15038 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15040 sort : function(fieldName, dir){
15041 var f = this.fields.get(fieldName);
15043 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15045 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15046 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15051 this.sortToggle[f.name] = dir;
15052 this.sortInfo = {field: f.name, direction: dir};
15053 if(!this.remoteSort){
15055 this.fireEvent("datachanged", this);
15057 this.load(this.lastOptions);
15062 * Calls the specified function for each of the Records in the cache.
15063 * @param {Function} fn The function to call. The Record is passed as the first parameter.
15064 * Returning <em>false</em> aborts and exits the iteration.
15065 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15067 each : function(fn, scope){
15068 this.data.each(fn, scope);
15072 * Gets all records modified since the last commit. Modified records are persisted across load operations
15073 * (e.g., during paging).
15074 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15076 getModifiedRecords : function(){
15077 return this.modified;
15081 createFilterFn : function(property, value, anyMatch){
15082 if(!value.exec){ // not a regex
15083 value = String(value);
15084 if(value.length == 0){
15087 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15089 return function(r){
15090 return value.test(r.data[property]);
15095 * Sums the value of <i>property</i> for each record between start and end and returns the result.
15096 * @param {String} property A field on your records
15097 * @param {Number} start The record index to start at (defaults to 0)
15098 * @param {Number} end The last record index to include (defaults to length - 1)
15099 * @return {Number} The sum
15101 sum : function(property, start, end){
15102 var rs = this.data.items, v = 0;
15103 start = start || 0;
15104 end = (end || end === 0) ? end : rs.length-1;
15106 for(var i = start; i <= end; i++){
15107 v += (rs[i].data[property] || 0);
15113 * Filter the records by a specified property.
15114 * @param {String} field A field on your records
15115 * @param {String/RegExp} value Either a string that the field
15116 * should start with or a RegExp to test against the field
15117 * @param {Boolean} anyMatch True to match any part not just the beginning
15119 filter : function(property, value, anyMatch){
15120 var fn = this.createFilterFn(property, value, anyMatch);
15121 return fn ? this.filterBy(fn) : this.clearFilter();
15125 * Filter by a function. The specified function will be called with each
15126 * record in this data source. If the function returns true the record is included,
15127 * otherwise it is filtered.
15128 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15129 * @param {Object} scope (optional) The scope of the function (defaults to this)
15131 filterBy : function(fn, scope){
15132 this.snapshot = this.snapshot || this.data;
15133 this.data = this.queryBy(fn, scope||this);
15134 this.fireEvent("datachanged", this);
15138 * Query the records by a specified property.
15139 * @param {String} field A field on your records
15140 * @param {String/RegExp} value Either a string that the field
15141 * should start with or a RegExp to test against the field
15142 * @param {Boolean} anyMatch True to match any part not just the beginning
15143 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15145 query : function(property, value, anyMatch){
15146 var fn = this.createFilterFn(property, value, anyMatch);
15147 return fn ? this.queryBy(fn) : this.data.clone();
15151 * Query by a function. The specified function will be called with each
15152 * record in this data source. If the function returns true the record is included
15154 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15155 * @param {Object} scope (optional) The scope of the function (defaults to this)
15156 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15158 queryBy : function(fn, scope){
15159 var data = this.snapshot || this.data;
15160 return data.filterBy(fn, scope||this);
15164 * Collects unique values for a particular dataIndex from this store.
15165 * @param {String} dataIndex The property to collect
15166 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15167 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15168 * @return {Array} An array of the unique values
15170 collect : function(dataIndex, allowNull, bypassFilter){
15171 var d = (bypassFilter === true && this.snapshot) ?
15172 this.snapshot.items : this.data.items;
15173 var v, sv, r = [], l = {};
15174 for(var i = 0, len = d.length; i < len; i++){
15175 v = d[i].data[dataIndex];
15177 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15186 * Revert to a view of the Record cache with no filtering applied.
15187 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15189 clearFilter : function(suppressEvent){
15190 if(this.snapshot && this.snapshot != this.data){
15191 this.data = this.snapshot;
15192 delete this.snapshot;
15193 if(suppressEvent !== true){
15194 this.fireEvent("datachanged", this);
15200 afterEdit : function(record){
15201 if(this.modified.indexOf(record) == -1){
15202 this.modified.push(record);
15204 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15208 afterReject : function(record){
15209 this.modified.remove(record);
15210 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15214 afterCommit : function(record){
15215 this.modified.remove(record);
15216 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15220 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15221 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15223 commitChanges : function(){
15224 var m = this.modified.slice(0);
15225 this.modified = [];
15226 for(var i = 0, len = m.length; i < len; i++){
15232 * Cancel outstanding changes on all changed records.
15234 rejectChanges : function(){
15235 var m = this.modified.slice(0);
15236 this.modified = [];
15237 for(var i = 0, len = m.length; i < len; i++){
15242 onMetaChange : function(meta, rtype, o){
15243 this.recordType = rtype;
15244 this.fields = rtype.prototype.fields;
15245 delete this.snapshot;
15246 this.sortInfo = meta.sortInfo || this.sortInfo;
15247 this.modified = [];
15248 this.fireEvent('metachange', this, this.reader.meta);
15251 moveIndex : function(data, type)
15253 var index = this.indexOf(data);
15255 var newIndex = index + type;
15259 this.insert(newIndex, data);
15264 * Ext JS Library 1.1.1
15265 * Copyright(c) 2006-2007, Ext JS, LLC.
15267 * Originally Released Under LGPL - original licence link has changed is not relivant.
15270 * <script type="text/javascript">
15274 * @class Roo.data.SimpleStore
15275 * @extends Roo.data.Store
15276 * Small helper class to make creating Stores from Array data easier.
15277 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15278 * @cfg {Array} fields An array of field definition objects, or field name strings.
15279 * @cfg {Object} an existing reader (eg. copied from another store)
15280 * @cfg {Array} data The multi-dimensional array of data
15282 * @param {Object} config
15284 Roo.data.SimpleStore = function(config)
15286 Roo.data.SimpleStore.superclass.constructor.call(this, {
15288 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15291 Roo.data.Record.create(config.fields)
15293 proxy : new Roo.data.MemoryProxy(config.data)
15297 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15299 * Ext JS Library 1.1.1
15300 * Copyright(c) 2006-2007, Ext JS, LLC.
15302 * Originally Released Under LGPL - original licence link has changed is not relivant.
15305 * <script type="text/javascript">
15310 * @extends Roo.data.Store
15311 * @class Roo.data.JsonStore
15312 * Small helper class to make creating Stores for JSON data easier. <br/>
15314 var store = new Roo.data.JsonStore({
15315 url: 'get-images.php',
15317 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15320 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15321 * JsonReader and HttpProxy (unless inline data is provided).</b>
15322 * @cfg {Array} fields An array of field definition objects, or field name strings.
15324 * @param {Object} config
15326 Roo.data.JsonStore = function(c){
15327 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15328 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15329 reader: new Roo.data.JsonReader(c, c.fields)
15332 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15334 * Ext JS Library 1.1.1
15335 * Copyright(c) 2006-2007, Ext JS, LLC.
15337 * Originally Released Under LGPL - original licence link has changed is not relivant.
15340 * <script type="text/javascript">
15344 Roo.data.Field = function(config){
15345 if(typeof config == "string"){
15346 config = {name: config};
15348 Roo.apply(this, config);
15351 this.type = "auto";
15354 var st = Roo.data.SortTypes;
15355 // named sortTypes are supported, here we look them up
15356 if(typeof this.sortType == "string"){
15357 this.sortType = st[this.sortType];
15360 // set default sortType for strings and dates
15361 if(!this.sortType){
15364 this.sortType = st.asUCString;
15367 this.sortType = st.asDate;
15370 this.sortType = st.none;
15375 var stripRe = /[\$,%]/g;
15377 // prebuilt conversion function for this field, instead of
15378 // switching every time we're reading a value
15380 var cv, dateFormat = this.dateFormat;
15385 cv = function(v){ return v; };
15388 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15392 return v !== undefined && v !== null && v !== '' ?
15393 parseInt(String(v).replace(stripRe, ""), 10) : '';
15398 return v !== undefined && v !== null && v !== '' ?
15399 parseFloat(String(v).replace(stripRe, ""), 10) : '';
15404 cv = function(v){ return v === true || v === "true" || v == 1; };
15411 if(v instanceof Date){
15415 if(dateFormat == "timestamp"){
15416 return new Date(v*1000);
15418 return Date.parseDate(v, dateFormat);
15420 var parsed = Date.parse(v);
15421 return parsed ? new Date(parsed) : null;
15430 Roo.data.Field.prototype = {
15438 * Ext JS Library 1.1.1
15439 * Copyright(c) 2006-2007, Ext JS, LLC.
15441 * Originally Released Under LGPL - original licence link has changed is not relivant.
15444 * <script type="text/javascript">
15447 // Base class for reading structured data from a data source. This class is intended to be
15448 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15451 * @class Roo.data.DataReader
15452 * Base class for reading structured data from a data source. This class is intended to be
15453 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15456 Roo.data.DataReader = function(meta, recordType){
15460 this.recordType = recordType instanceof Array ?
15461 Roo.data.Record.create(recordType) : recordType;
15464 Roo.data.DataReader.prototype = {
15467 readerType : 'Data',
15469 * Create an empty record
15470 * @param {Object} data (optional) - overlay some values
15471 * @return {Roo.data.Record} record created.
15473 newRow : function(d) {
15475 this.recordType.prototype.fields.each(function(c) {
15477 case 'int' : da[c.name] = 0; break;
15478 case 'date' : da[c.name] = new Date(); break;
15479 case 'float' : da[c.name] = 0.0; break;
15480 case 'boolean' : da[c.name] = false; break;
15481 default : da[c.name] = ""; break;
15485 return new this.recordType(Roo.apply(da, d));
15491 * Ext JS Library 1.1.1
15492 * Copyright(c) 2006-2007, Ext JS, LLC.
15494 * Originally Released Under LGPL - original licence link has changed is not relivant.
15497 * <script type="text/javascript">
15501 * @class Roo.data.DataProxy
15502 * @extends Roo.data.Observable
15503 * This class is an abstract base class for implementations which provide retrieval of
15504 * unformatted data objects.<br>
15506 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
15507 * (of the appropriate type which knows how to parse the data object) to provide a block of
15508 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
15510 * Custom implementations must implement the load method as described in
15511 * {@link Roo.data.HttpProxy#load}.
15513 Roo.data.DataProxy = function(){
15516 * @event beforeload
15517 * Fires before a network request is made to retrieve a data object.
15518 * @param {Object} This DataProxy object.
15519 * @param {Object} params The params parameter to the load function.
15524 * Fires before the load method's callback is called.
15525 * @param {Object} This DataProxy object.
15526 * @param {Object} o The data object.
15527 * @param {Object} arg The callback argument object passed to the load function.
15531 * @event loadexception
15532 * Fires if an Exception occurs during data retrieval.
15533 * @param {Object} This DataProxy object.
15534 * @param {Object} o The data object.
15535 * @param {Object} arg The callback argument object passed to the load function.
15536 * @param {Object} e The Exception.
15538 loadexception : true
15540 Roo.data.DataProxy.superclass.constructor.call(this);
15543 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
15546 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
15550 * Ext JS Library 1.1.1
15551 * Copyright(c) 2006-2007, Ext JS, LLC.
15553 * Originally Released Under LGPL - original licence link has changed is not relivant.
15556 * <script type="text/javascript">
15559 * @class Roo.data.MemoryProxy
15560 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
15561 * to the Reader when its load method is called.
15563 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
15565 Roo.data.MemoryProxy = function(data){
15569 Roo.data.MemoryProxy.superclass.constructor.call(this);
15573 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
15576 * Load data from the requested source (in this case an in-memory
15577 * data object passed to the constructor), read the data object into
15578 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15579 * process that block using the passed callback.
15580 * @param {Object} params This parameter is not used by the MemoryProxy class.
15581 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15582 * object into a block of Roo.data.Records.
15583 * @param {Function} callback The function into which to pass the block of Roo.data.records.
15584 * The function must be passed <ul>
15585 * <li>The Record block object</li>
15586 * <li>The "arg" argument from the load function</li>
15587 * <li>A boolean success indicator</li>
15589 * @param {Object} scope The scope in which to call the callback
15590 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15592 load : function(params, reader, callback, scope, arg){
15593 params = params || {};
15596 result = reader.readRecords(params.data ? params.data :this.data);
15598 this.fireEvent("loadexception", this, arg, null, e);
15599 callback.call(scope, null, arg, false);
15602 callback.call(scope, result, arg, true);
15606 update : function(params, records){
15611 * Ext JS Library 1.1.1
15612 * Copyright(c) 2006-2007, Ext JS, LLC.
15614 * Originally Released Under LGPL - original licence link has changed is not relivant.
15617 * <script type="text/javascript">
15620 * @class Roo.data.HttpProxy
15621 * @extends Roo.data.DataProxy
15622 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
15623 * configured to reference a certain URL.<br><br>
15625 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
15626 * from which the running page was served.<br><br>
15628 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
15630 * Be aware that to enable the browser to parse an XML document, the server must set
15631 * the Content-Type header in the HTTP response to "text/xml".
15633 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
15634 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
15635 * will be used to make the request.
15637 Roo.data.HttpProxy = function(conn){
15638 Roo.data.HttpProxy.superclass.constructor.call(this);
15639 // is conn a conn config or a real conn?
15641 this.useAjax = !conn || !conn.events;
15645 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
15646 // thse are take from connection...
15649 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
15652 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
15653 * extra parameters to each request made by this object. (defaults to undefined)
15656 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
15657 * to each request made by this object. (defaults to undefined)
15660 * @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)
15663 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
15666 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
15672 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
15676 * Return the {@link Roo.data.Connection} object being used by this Proxy.
15677 * @return {Connection} The Connection object. This object may be used to subscribe to events on
15678 * a finer-grained basis than the DataProxy events.
15680 getConnection : function(){
15681 return this.useAjax ? Roo.Ajax : this.conn;
15685 * Load data from the configured {@link Roo.data.Connection}, read the data object into
15686 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
15687 * process that block using the passed callback.
15688 * @param {Object} params An object containing properties which are to be used as HTTP parameters
15689 * for the request to the remote server.
15690 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15691 * object into a block of Roo.data.Records.
15692 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15693 * The function must be passed <ul>
15694 * <li>The Record block object</li>
15695 * <li>The "arg" argument from the load function</li>
15696 * <li>A boolean success indicator</li>
15698 * @param {Object} scope The scope in which to call the callback
15699 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15701 load : function(params, reader, callback, scope, arg){
15702 if(this.fireEvent("beforeload", this, params) !== false){
15704 params : params || {},
15706 callback : callback,
15711 callback : this.loadResponse,
15715 Roo.applyIf(o, this.conn);
15716 if(this.activeRequest){
15717 Roo.Ajax.abort(this.activeRequest);
15719 this.activeRequest = Roo.Ajax.request(o);
15721 this.conn.request(o);
15724 callback.call(scope||this, null, arg, false);
15729 loadResponse : function(o, success, response){
15730 delete this.activeRequest;
15732 this.fireEvent("loadexception", this, o, response);
15733 o.request.callback.call(o.request.scope, null, o.request.arg, false);
15738 result = o.reader.read(response);
15740 this.fireEvent("loadexception", this, o, response, e);
15741 o.request.callback.call(o.request.scope, null, o.request.arg, false);
15745 this.fireEvent("load", this, o, o.request.arg);
15746 o.request.callback.call(o.request.scope, result, o.request.arg, true);
15750 update : function(dataSet){
15755 updateResponse : function(dataSet){
15760 * Ext JS Library 1.1.1
15761 * Copyright(c) 2006-2007, Ext JS, LLC.
15763 * Originally Released Under LGPL - original licence link has changed is not relivant.
15766 * <script type="text/javascript">
15770 * @class Roo.data.ScriptTagProxy
15771 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
15772 * other than the originating domain of the running page.<br><br>
15774 * <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
15775 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
15777 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
15778 * source code that is used as the source inside a <script> tag.<br><br>
15780 * In order for the browser to process the returned data, the server must wrap the data object
15781 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
15782 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
15783 * depending on whether the callback name was passed:
15786 boolean scriptTag = false;
15787 String cb = request.getParameter("callback");
15790 response.setContentType("text/javascript");
15792 response.setContentType("application/x-json");
15794 Writer out = response.getWriter();
15796 out.write(cb + "(");
15798 out.print(dataBlock.toJsonString());
15805 * @param {Object} config A configuration object.
15807 Roo.data.ScriptTagProxy = function(config){
15808 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
15809 Roo.apply(this, config);
15810 this.head = document.getElementsByTagName("head")[0];
15813 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
15815 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
15817 * @cfg {String} url The URL from which to request the data object.
15820 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
15824 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
15825 * the server the name of the callback function set up by the load call to process the returned data object.
15826 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
15827 * javascript output which calls this named function passing the data object as its only parameter.
15829 callbackParam : "callback",
15831 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
15832 * name to the request.
15837 * Load data from the configured URL, read the data object into
15838 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15839 * process that block using the passed callback.
15840 * @param {Object} params An object containing properties which are to be used as HTTP parameters
15841 * for the request to the remote server.
15842 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15843 * object into a block of Roo.data.Records.
15844 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15845 * The function must be passed <ul>
15846 * <li>The Record block object</li>
15847 * <li>The "arg" argument from the load function</li>
15848 * <li>A boolean success indicator</li>
15850 * @param {Object} scope The scope in which to call the callback
15851 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15853 load : function(params, reader, callback, scope, arg){
15854 if(this.fireEvent("beforeload", this, params) !== false){
15856 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
15858 var url = this.url;
15859 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
15861 url += "&_dc=" + (new Date().getTime());
15863 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
15866 cb : "stcCallback"+transId,
15867 scriptId : "stcScript"+transId,
15871 callback : callback,
15877 window[trans.cb] = function(o){
15878 conn.handleResponse(o, trans);
15881 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
15883 if(this.autoAbort !== false){
15887 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
15889 var script = document.createElement("script");
15890 script.setAttribute("src", url);
15891 script.setAttribute("type", "text/javascript");
15892 script.setAttribute("id", trans.scriptId);
15893 this.head.appendChild(script);
15895 this.trans = trans;
15897 callback.call(scope||this, null, arg, false);
15902 isLoading : function(){
15903 return this.trans ? true : false;
15907 * Abort the current server request.
15909 abort : function(){
15910 if(this.isLoading()){
15911 this.destroyTrans(this.trans);
15916 destroyTrans : function(trans, isLoaded){
15917 this.head.removeChild(document.getElementById(trans.scriptId));
15918 clearTimeout(trans.timeoutId);
15920 window[trans.cb] = undefined;
15922 delete window[trans.cb];
15925 // if hasn't been loaded, wait for load to remove it to prevent script error
15926 window[trans.cb] = function(){
15927 window[trans.cb] = undefined;
15929 delete window[trans.cb];
15936 handleResponse : function(o, trans){
15937 this.trans = false;
15938 this.destroyTrans(trans, true);
15941 result = trans.reader.readRecords(o);
15943 this.fireEvent("loadexception", this, o, trans.arg, e);
15944 trans.callback.call(trans.scope||window, null, trans.arg, false);
15947 this.fireEvent("load", this, o, trans.arg);
15948 trans.callback.call(trans.scope||window, result, trans.arg, true);
15952 handleFailure : function(trans){
15953 this.trans = false;
15954 this.destroyTrans(trans, false);
15955 this.fireEvent("loadexception", this, null, trans.arg);
15956 trans.callback.call(trans.scope||window, null, trans.arg, false);
15960 * Ext JS Library 1.1.1
15961 * Copyright(c) 2006-2007, Ext JS, LLC.
15963 * Originally Released Under LGPL - original licence link has changed is not relivant.
15966 * <script type="text/javascript">
15970 * @class Roo.data.JsonReader
15971 * @extends Roo.data.DataReader
15972 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
15973 * based on mappings in a provided Roo.data.Record constructor.
15975 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
15976 * in the reply previously.
15981 var RecordDef = Roo.data.Record.create([
15982 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
15983 {name: 'occupation'} // This field will use "occupation" as the mapping.
15985 var myReader = new Roo.data.JsonReader({
15986 totalProperty: "results", // The property which contains the total dataset size (optional)
15987 root: "rows", // The property which contains an Array of row objects
15988 id: "id" // The property within each row object that provides an ID for the record (optional)
15992 * This would consume a JSON file like this:
15994 { 'results': 2, 'rows': [
15995 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
15996 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
15999 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16000 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16001 * paged from the remote server.
16002 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16003 * @cfg {String} root name of the property which contains the Array of row objects.
16004 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16005 * @cfg {Array} fields Array of field definition objects
16007 * Create a new JsonReader
16008 * @param {Object} meta Metadata configuration options
16009 * @param {Object} recordType Either an Array of field definition objects,
16010 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16012 Roo.data.JsonReader = function(meta, recordType){
16015 // set some defaults:
16016 Roo.applyIf(meta, {
16017 totalProperty: 'total',
16018 successProperty : 'success',
16023 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16025 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16027 readerType : 'Json',
16030 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
16031 * Used by Store query builder to append _requestMeta to params.
16034 metaFromRemote : false,
16036 * This method is only used by a DataProxy which has retrieved data from a remote server.
16037 * @param {Object} response The XHR object which contains the JSON data in its responseText.
16038 * @return {Object} data A data block which is used by an Roo.data.Store object as
16039 * a cache of Roo.data.Records.
16041 read : function(response){
16042 var json = response.responseText;
16044 var o = /* eval:var:o */ eval("("+json+")");
16046 throw {message: "JsonReader.read: Json object not found"};
16052 this.metaFromRemote = true;
16053 this.meta = o.metaData;
16054 this.recordType = Roo.data.Record.create(o.metaData.fields);
16055 this.onMetaChange(this.meta, this.recordType, o);
16057 return this.readRecords(o);
16060 // private function a store will implement
16061 onMetaChange : function(meta, recordType, o){
16068 simpleAccess: function(obj, subsc) {
16075 getJsonAccessor: function(){
16077 return function(expr) {
16079 return(re.test(expr))
16080 ? new Function("obj", "return obj." + expr)
16085 return Roo.emptyFn;
16090 * Create a data block containing Roo.data.Records from an XML document.
16091 * @param {Object} o An object which contains an Array of row objects in the property specified
16092 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16093 * which contains the total size of the dataset.
16094 * @return {Object} data A data block which is used by an Roo.data.Store object as
16095 * a cache of Roo.data.Records.
16097 readRecords : function(o){
16099 * After any data loads, the raw JSON data is available for further custom processing.
16103 var s = this.meta, Record = this.recordType,
16104 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16106 // Generate extraction functions for the totalProperty, the root, the id, and for each field
16108 if(s.totalProperty) {
16109 this.getTotal = this.getJsonAccessor(s.totalProperty);
16111 if(s.successProperty) {
16112 this.getSuccess = this.getJsonAccessor(s.successProperty);
16114 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16116 var g = this.getJsonAccessor(s.id);
16117 this.getId = function(rec) {
16119 return (r === undefined || r === "") ? null : r;
16122 this.getId = function(){return null;};
16125 for(var jj = 0; jj < fl; jj++){
16127 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16128 this.ef[jj] = this.getJsonAccessor(map);
16132 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16133 if(s.totalProperty){
16134 var vt = parseInt(this.getTotal(o), 10);
16139 if(s.successProperty){
16140 var vs = this.getSuccess(o);
16141 if(vs === false || vs === 'false'){
16146 for(var i = 0; i < c; i++){
16149 var id = this.getId(n);
16150 for(var j = 0; j < fl; j++){
16152 var v = this.ef[j](n);
16154 Roo.log('missing convert for ' + f.name);
16158 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16160 var record = new Record(values, id);
16162 records[i] = record;
16168 totalRecords : totalRecords
16171 // used when loading children.. @see loadDataFromChildren
16172 toLoadData: function(rec)
16174 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16175 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16176 return { data : data, total : data.length };
16181 * Ext JS Library 1.1.1
16182 * Copyright(c) 2006-2007, Ext JS, LLC.
16184 * Originally Released Under LGPL - original licence link has changed is not relivant.
16187 * <script type="text/javascript">
16191 * @class Roo.data.ArrayReader
16192 * @extends Roo.data.DataReader
16193 * Data reader class to create an Array of Roo.data.Record objects from an Array.
16194 * Each element of that Array represents a row of data fields. The
16195 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16196 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16200 var RecordDef = Roo.data.Record.create([
16201 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
16202 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
16204 var myReader = new Roo.data.ArrayReader({
16205 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
16209 * This would consume an Array like this:
16211 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16215 * Create a new JsonReader
16216 * @param {Object} meta Metadata configuration options.
16217 * @param {Object|Array} recordType Either an Array of field definition objects
16219 * @cfg {Array} fields Array of field definition objects
16220 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16221 * as specified to {@link Roo.data.Record#create},
16222 * or an {@link Roo.data.Record} object
16225 * created using {@link Roo.data.Record#create}.
16227 Roo.data.ArrayReader = function(meta, recordType)
16229 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16232 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16235 * Create a data block containing Roo.data.Records from an XML document.
16236 * @param {Object} o An Array of row objects which represents the dataset.
16237 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16238 * a cache of Roo.data.Records.
16240 readRecords : function(o)
16242 var sid = this.meta ? this.meta.id : null;
16243 var recordType = this.recordType, fields = recordType.prototype.fields;
16246 for(var i = 0; i < root.length; i++){
16249 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16250 for(var j = 0, jlen = fields.length; j < jlen; j++){
16251 var f = fields.items[j];
16252 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16253 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16255 values[f.name] = v;
16257 var record = new recordType(values, id);
16259 records[records.length] = record;
16263 totalRecords : records.length
16266 // used when loading children.. @see loadDataFromChildren
16267 toLoadData: function(rec)
16269 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16270 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16281 * @class Roo.bootstrap.ComboBox
16282 * @extends Roo.bootstrap.TriggerField
16283 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16284 * @cfg {Boolean} append (true|false) default false
16285 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16286 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16287 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16288 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16289 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16290 * @cfg {Boolean} animate default true
16291 * @cfg {Boolean} emptyResultText only for touch device
16292 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16293 * @cfg {String} emptyTitle default ''
16294 * @cfg {Number} width fixed with? experimental
16296 * Create a new ComboBox.
16297 * @param {Object} config Configuration options
16299 Roo.bootstrap.ComboBox = function(config){
16300 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
16304 * Fires when the dropdown list is expanded
16305 * @param {Roo.bootstrap.ComboBox} combo This combo box
16310 * Fires when the dropdown list is collapsed
16311 * @param {Roo.bootstrap.ComboBox} combo This combo box
16315 * @event beforeselect
16316 * Fires before a list item is selected. Return false to cancel the selection.
16317 * @param {Roo.bootstrap.ComboBox} combo This combo box
16318 * @param {Roo.data.Record} record The data record returned from the underlying store
16319 * @param {Number} index The index of the selected item in the dropdown list
16321 'beforeselect' : true,
16324 * Fires when a list item is selected
16325 * @param {Roo.bootstrap.ComboBox} combo This combo box
16326 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16327 * @param {Number} index The index of the selected item in the dropdown list
16331 * @event beforequery
16332 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16333 * The event object passed has these properties:
16334 * @param {Roo.bootstrap.ComboBox} combo This combo box
16335 * @param {String} query The query
16336 * @param {Boolean} forceAll true to force "all" query
16337 * @param {Boolean} cancel true to cancel the query
16338 * @param {Object} e The query event object
16340 'beforequery': true,
16343 * Fires when the 'add' icon is pressed (add a listener to enable add button)
16344 * @param {Roo.bootstrap.ComboBox} combo This combo box
16349 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16350 * @param {Roo.bootstrap.ComboBox} combo This combo box
16351 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16356 * Fires when the remove value from the combobox array
16357 * @param {Roo.bootstrap.ComboBox} combo This combo box
16361 * @event afterremove
16362 * Fires when the remove value from the combobox array
16363 * @param {Roo.bootstrap.ComboBox} combo This combo box
16365 'afterremove' : true,
16367 * @event specialfilter
16368 * Fires when specialfilter
16369 * @param {Roo.bootstrap.ComboBox} combo This combo box
16371 'specialfilter' : true,
16374 * Fires when tick the element
16375 * @param {Roo.bootstrap.ComboBox} combo This combo box
16379 * @event touchviewdisplay
16380 * Fires when touch view require special display (default is using displayField)
16381 * @param {Roo.bootstrap.ComboBox} combo This combo box
16382 * @param {Object} cfg set html .
16384 'touchviewdisplay' : true
16389 this.tickItems = [];
16391 this.selectedIndex = -1;
16392 if(this.mode == 'local'){
16393 if(config.queryDelay === undefined){
16394 this.queryDelay = 10;
16396 if(config.minChars === undefined){
16402 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
16405 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16406 * rendering into an Roo.Editor, defaults to false)
16409 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16410 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16413 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16416 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16417 * the dropdown list (defaults to undefined, with no header element)
16421 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
16425 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16427 listWidth: undefined,
16429 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16430 * mode = 'remote' or 'text' if mode = 'local')
16432 displayField: undefined,
16435 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16436 * mode = 'remote' or 'value' if mode = 'local').
16437 * Note: use of a valueField requires the user make a selection
16438 * in order for a value to be mapped.
16440 valueField: undefined,
16442 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16447 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16448 * field's data value (defaults to the underlying DOM element's name)
16450 hiddenName: undefined,
16452 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16456 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16458 selectedClass: 'active',
16461 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16465 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16466 * anchor positions (defaults to 'tl-bl')
16468 listAlign: 'tl-bl?',
16470 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16474 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
16475 * query specified by the allQuery config option (defaults to 'query')
16477 triggerAction: 'query',
16479 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16480 * (defaults to 4, does not apply if editable = false)
16484 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16485 * delay (typeAheadDelay) if it matches a known value (defaults to false)
16489 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
16490 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
16494 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
16495 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
16499 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
16500 * when editable = true (defaults to false)
16502 selectOnFocus:false,
16504 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
16506 queryParam: 'query',
16508 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
16509 * when mode = 'remote' (defaults to 'Loading...')
16511 loadingText: 'Loading...',
16513 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
16517 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
16521 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
16522 * traditional select (defaults to true)
16526 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
16530 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
16534 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
16535 * listWidth has a higher value)
16539 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
16540 * allow the user to set arbitrary text into the field (defaults to false)
16542 forceSelection:false,
16544 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
16545 * if typeAhead = true (defaults to 250)
16547 typeAheadDelay : 250,
16549 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
16550 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
16552 valueNotFoundText : undefined,
16554 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
16556 blockFocus : false,
16559 * @cfg {Boolean} disableClear Disable showing of clear button.
16561 disableClear : false,
16563 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
16565 alwaysQuery : false,
16568 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
16573 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
16575 invalidClass : "has-warning",
16578 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
16580 validClass : "has-success",
16583 * @cfg {Boolean} specialFilter (true|false) special filter default false
16585 specialFilter : false,
16588 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
16590 mobileTouchView : true,
16593 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
16595 useNativeIOS : false,
16598 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
16600 mobile_restrict_height : false,
16602 ios_options : false,
16614 btnPosition : 'right',
16615 triggerList : true,
16616 showToggleBtn : true,
16618 emptyResultText: 'Empty',
16619 triggerText : 'Select',
16623 // element that contains real text value.. (when hidden is used..)
16625 getAutoCreate : function()
16630 * Render classic select for iso
16633 if(Roo.isIOS && this.useNativeIOS){
16634 cfg = this.getAutoCreateNativeIOS();
16642 if(Roo.isTouch && this.mobileTouchView){
16643 cfg = this.getAutoCreateTouchView();
16650 if(!this.tickable){
16651 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
16656 * ComboBox with tickable selections
16659 var align = this.labelAlign || this.parentLabelAlign();
16662 cls : 'form-group roo-combobox-tickable' //input-group
16665 var btn_text_select = '';
16666 var btn_text_done = '';
16667 var btn_text_cancel = '';
16669 if (this.btn_text_show) {
16670 btn_text_select = 'Select';
16671 btn_text_done = 'Done';
16672 btn_text_cancel = 'Cancel';
16677 cls : 'tickable-buttons',
16682 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
16683 //html : this.triggerText
16684 html: btn_text_select
16690 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
16692 html: btn_text_done
16698 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
16700 html: btn_text_cancel
16706 buttons.cn.unshift({
16708 cls: 'roo-select2-search-field-input'
16714 Roo.each(buttons.cn, function(c){
16716 c.cls += ' btn-' + _this.size;
16719 if (_this.disabled) {
16726 style : 'display: contents',
16731 cls: 'form-hidden-field'
16735 cls: 'roo-select2-choices',
16739 cls: 'roo-select2-search-field',
16750 cls: 'roo-select2-container input-group roo-select2-container-multi',
16756 // cls: 'typeahead typeahead-long dropdown-menu',
16757 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
16762 if(this.hasFeedback && !this.allowBlank){
16766 cls: 'glyphicon form-control-feedback'
16769 combobox.cn.push(feedback);
16776 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
16777 tooltip : 'This field is required'
16779 if (Roo.bootstrap.version == 4) {
16782 style : 'display:none'
16785 if (align ==='left' && this.fieldLabel.length) {
16787 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
16794 cls : 'control-label col-form-label',
16795 html : this.fieldLabel
16807 var labelCfg = cfg.cn[1];
16808 var contentCfg = cfg.cn[2];
16811 if(this.indicatorpos == 'right'){
16817 cls : 'control-label col-form-label',
16821 html : this.fieldLabel
16837 labelCfg = cfg.cn[0];
16838 contentCfg = cfg.cn[1];
16842 if(this.labelWidth > 12){
16843 labelCfg.style = "width: " + this.labelWidth + 'px';
16845 if(this.width * 1 > 0){
16846 contentCfg.style = "width: " + this.width + 'px';
16848 if(this.labelWidth < 13 && this.labelmd == 0){
16849 this.labelmd = this.labelWidth;
16852 if(this.labellg > 0){
16853 labelCfg.cls += ' col-lg-' + this.labellg;
16854 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
16857 if(this.labelmd > 0){
16858 labelCfg.cls += ' col-md-' + this.labelmd;
16859 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
16862 if(this.labelsm > 0){
16863 labelCfg.cls += ' col-sm-' + this.labelsm;
16864 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
16867 if(this.labelxs > 0){
16868 labelCfg.cls += ' col-xs-' + this.labelxs;
16869 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
16873 } else if ( this.fieldLabel.length) {
16874 // Roo.log(" label");
16879 //cls : 'input-group-addon',
16880 html : this.fieldLabel
16885 if(this.indicatorpos == 'right'){
16889 //cls : 'input-group-addon',
16890 html : this.fieldLabel
16900 // Roo.log(" no label && no align");
16907 ['xs','sm','md','lg'].map(function(size){
16908 if (settings[size]) {
16909 cfg.cls += ' col-' + size + '-' + settings[size];
16917 _initEventsCalled : false,
16920 initEvents: function()
16922 if (this._initEventsCalled) { // as we call render... prevent looping...
16925 this._initEventsCalled = true;
16928 throw "can not find store for combo";
16931 this.indicator = this.indicatorEl();
16933 this.store = Roo.factory(this.store, Roo.data);
16934 this.store.parent = this;
16936 // if we are building from html. then this element is so complex, that we can not really
16937 // use the rendered HTML.
16938 // so we have to trash and replace the previous code.
16939 if (Roo.XComponent.build_from_html) {
16940 // remove this element....
16941 var e = this.el.dom, k=0;
16942 while (e ) { e = e.previousSibling; ++k;}
16947 this.rendered = false;
16949 this.render(this.parent().getChildContainer(true), k);
16952 if(Roo.isIOS && this.useNativeIOS){
16953 this.initIOSView();
16961 if(Roo.isTouch && this.mobileTouchView){
16962 this.initTouchView();
16967 this.initTickableEvents();
16971 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
16973 if(this.hiddenName){
16975 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16977 this.hiddenField.dom.value =
16978 this.hiddenValue !== undefined ? this.hiddenValue :
16979 this.value !== undefined ? this.value : '';
16981 // prevent input submission
16982 this.el.dom.removeAttribute('name');
16983 this.hiddenField.dom.setAttribute('name', this.hiddenName);
16988 // this.el.dom.setAttribute('autocomplete', 'off');
16991 var cls = 'x-combo-list';
16993 //this.list = new Roo.Layer({
16994 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17000 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17001 _this.list.setWidth(lw);
17004 this.list.on('mouseover', this.onViewOver, this);
17005 this.list.on('mousemove', this.onViewMove, this);
17006 this.list.on('scroll', this.onViewScroll, this);
17009 this.list.swallowEvent('mousewheel');
17010 this.assetHeight = 0;
17013 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17014 this.assetHeight += this.header.getHeight();
17017 this.innerList = this.list.createChild({cls:cls+'-inner'});
17018 this.innerList.on('mouseover', this.onViewOver, this);
17019 this.innerList.on('mousemove', this.onViewMove, this);
17020 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17022 if(this.allowBlank && !this.pageSize && !this.disableClear){
17023 this.footer = this.list.createChild({cls:cls+'-ft'});
17024 this.pageTb = new Roo.Toolbar(this.footer);
17028 this.footer = this.list.createChild({cls:cls+'-ft'});
17029 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17030 {pageSize: this.pageSize});
17034 if (this.pageTb && this.allowBlank && !this.disableClear) {
17036 this.pageTb.add(new Roo.Toolbar.Fill(), {
17037 cls: 'x-btn-icon x-btn-clear',
17039 handler: function()
17042 _this.clearValue();
17043 _this.onSelect(false, -1);
17048 this.assetHeight += this.footer.getHeight();
17053 this.tpl = Roo.bootstrap.version == 4 ?
17054 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
17055 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17058 this.view = new Roo.View(this.list, this.tpl, {
17059 singleSelect:true, store: this.store, selectedClass: this.selectedClass
17061 //this.view.wrapEl.setDisplayed(false);
17062 this.view.on('click', this.onViewClick, this);
17065 this.store.on('beforeload', this.onBeforeLoad, this);
17066 this.store.on('load', this.onLoad, this);
17067 this.store.on('loadexception', this.onLoadException, this);
17069 if(this.resizable){
17070 this.resizer = new Roo.Resizable(this.list, {
17071 pinned:true, handles:'se'
17073 this.resizer.on('resize', function(r, w, h){
17074 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17075 this.listWidth = w;
17076 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17077 this.restrictHeight();
17079 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17082 if(!this.editable){
17083 this.editable = true;
17084 this.setEditable(false);
17089 if (typeof(this.events.add.listeners) != 'undefined') {
17091 this.addicon = this.wrap.createChild(
17092 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
17094 this.addicon.on('click', function(e) {
17095 this.fireEvent('add', this);
17098 if (typeof(this.events.edit.listeners) != 'undefined') {
17100 this.editicon = this.wrap.createChild(
17101 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
17102 if (this.addicon) {
17103 this.editicon.setStyle('margin-left', '40px');
17105 this.editicon.on('click', function(e) {
17107 // we fire even if inothing is selected..
17108 this.fireEvent('edit', this, this.lastData );
17114 this.keyNav = new Roo.KeyNav(this.inputEl(), {
17115 "up" : function(e){
17116 this.inKeyMode = true;
17120 "down" : function(e){
17121 if(!this.isExpanded()){
17122 this.onTriggerClick();
17124 this.inKeyMode = true;
17129 "enter" : function(e){
17130 // this.onViewClick();
17134 if(this.fireEvent("specialkey", this, e)){
17135 this.onViewClick(false);
17141 "esc" : function(e){
17145 "tab" : function(e){
17148 if(this.fireEvent("specialkey", this, e)){
17149 this.onViewClick(false);
17157 doRelay : function(foo, bar, hname){
17158 if(hname == 'down' || this.scope.isExpanded()){
17159 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17168 this.queryDelay = Math.max(this.queryDelay || 10,
17169 this.mode == 'local' ? 10 : 250);
17172 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17174 if(this.typeAhead){
17175 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17177 if(this.editable !== false){
17178 this.inputEl().on("keyup", this.onKeyUp, this);
17180 if(this.forceSelection){
17181 this.inputEl().on('blur', this.doForce, this);
17185 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17186 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17190 initTickableEvents: function()
17194 if(this.hiddenName){
17196 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17198 this.hiddenField.dom.value =
17199 this.hiddenValue !== undefined ? this.hiddenValue :
17200 this.value !== undefined ? this.value : '';
17202 // prevent input submission
17203 this.el.dom.removeAttribute('name');
17204 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17209 // this.list = this.el.select('ul.dropdown-menu',true).first();
17211 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17212 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17213 if(this.triggerList){
17214 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17217 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17218 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17220 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17221 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17223 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17224 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17226 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17227 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17228 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17231 this.cancelBtn.hide();
17236 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17237 _this.list.setWidth(lw);
17240 this.list.on('mouseover', this.onViewOver, this);
17241 this.list.on('mousemove', this.onViewMove, this);
17243 this.list.on('scroll', this.onViewScroll, this);
17246 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
17247 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17250 this.view = new Roo.View(this.list, this.tpl, {
17255 selectedClass: this.selectedClass
17258 //this.view.wrapEl.setDisplayed(false);
17259 this.view.on('click', this.onViewClick, this);
17263 this.store.on('beforeload', this.onBeforeLoad, this);
17264 this.store.on('load', this.onLoad, this);
17265 this.store.on('loadexception', this.onLoadException, this);
17268 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17269 "up" : function(e){
17270 this.inKeyMode = true;
17274 "down" : function(e){
17275 this.inKeyMode = true;
17279 "enter" : function(e){
17280 if(this.fireEvent("specialkey", this, e)){
17281 this.onViewClick(false);
17287 "esc" : function(e){
17288 this.onTickableFooterButtonClick(e, false, false);
17291 "tab" : function(e){
17292 this.fireEvent("specialkey", this, e);
17294 this.onTickableFooterButtonClick(e, false, false);
17301 doRelay : function(e, fn, key){
17302 if(this.scope.isExpanded()){
17303 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17312 this.queryDelay = Math.max(this.queryDelay || 10,
17313 this.mode == 'local' ? 10 : 250);
17316 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17318 if(this.typeAhead){
17319 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17322 if(this.editable !== false){
17323 this.tickableInputEl().on("keyup", this.onKeyUp, this);
17326 this.indicator = this.indicatorEl();
17328 if(this.indicator){
17329 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17330 this.indicator.hide();
17335 onDestroy : function(){
17337 this.view.setStore(null);
17338 this.view.el.removeAllListeners();
17339 this.view.el.remove();
17340 this.view.purgeListeners();
17343 this.list.dom.innerHTML = '';
17347 this.store.un('beforeload', this.onBeforeLoad, this);
17348 this.store.un('load', this.onLoad, this);
17349 this.store.un('loadexception', this.onLoadException, this);
17351 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
17355 fireKey : function(e){
17356 if(e.isNavKeyPress() && !this.list.isVisible()){
17357 this.fireEvent("specialkey", this, e);
17362 onResize: function(w, h)
17366 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
17368 // if(typeof w != 'number'){
17369 // // we do not handle it!?!?
17372 // var tw = this.trigger.getWidth();
17373 // // tw += this.addicon ? this.addicon.getWidth() : 0;
17374 // // tw += this.editicon ? this.editicon.getWidth() : 0;
17376 // this.inputEl().setWidth( this.adjustWidth('input', x));
17378 // //this.trigger.setStyle('left', x+'px');
17380 // if(this.list && this.listWidth === undefined){
17381 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17382 // this.list.setWidth(lw);
17383 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17391 * Allow or prevent the user from directly editing the field text. If false is passed,
17392 * the user will only be able to select from the items defined in the dropdown list. This method
17393 * is the runtime equivalent of setting the 'editable' config option at config time.
17394 * @param {Boolean} value True to allow the user to directly edit the field text
17396 setEditable : function(value){
17397 if(value == this.editable){
17400 this.editable = value;
17402 this.inputEl().dom.setAttribute('readOnly', true);
17403 this.inputEl().on('mousedown', this.onTriggerClick, this);
17404 this.inputEl().addClass('x-combo-noedit');
17406 this.inputEl().dom.removeAttribute('readOnly');
17407 this.inputEl().un('mousedown', this.onTriggerClick, this);
17408 this.inputEl().removeClass('x-combo-noedit');
17414 onBeforeLoad : function(combo,opts){
17415 if(!this.hasFocus){
17419 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17421 this.restrictHeight();
17422 this.selectedIndex = -1;
17426 onLoad : function(){
17428 this.hasQuery = false;
17430 if(!this.hasFocus){
17434 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17435 this.loading.hide();
17438 if(this.store.getCount() > 0){
17441 this.restrictHeight();
17442 if(this.lastQuery == this.allQuery){
17443 if(this.editable && !this.tickable){
17444 this.inputEl().dom.select();
17448 !this.selectByValue(this.value, true) &&
17451 !this.store.lastOptions ||
17452 typeof(this.store.lastOptions.add) == 'undefined' ||
17453 this.store.lastOptions.add != true
17456 this.select(0, true);
17459 if(this.autoFocus){
17462 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17463 this.taTask.delay(this.typeAheadDelay);
17467 this.onEmptyResults();
17473 onLoadException : function()
17475 this.hasQuery = false;
17477 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17478 this.loading.hide();
17481 if(this.tickable && this.editable){
17486 // only causes errors at present
17487 //Roo.log(this.store.reader.jsonData);
17488 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
17490 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
17496 onTypeAhead : function(){
17497 if(this.store.getCount() > 0){
17498 var r = this.store.getAt(0);
17499 var newValue = r.data[this.displayField];
17500 var len = newValue.length;
17501 var selStart = this.getRawValue().length;
17503 if(selStart != len){
17504 this.setRawValue(newValue);
17505 this.selectText(selStart, newValue.length);
17511 onSelect : function(record, index){
17513 if(this.fireEvent('beforeselect', this, record, index) !== false){
17515 this.setFromData(index > -1 ? record.data : false);
17518 this.fireEvent('select', this, record, index);
17523 * Returns the currently selected field value or empty string if no value is set.
17524 * @return {String} value The selected value
17526 getValue : function()
17528 if(Roo.isIOS && this.useNativeIOS){
17529 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
17533 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
17536 if(this.valueField){
17537 return typeof this.value != 'undefined' ? this.value : '';
17539 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
17543 getRawValue : function()
17545 if(Roo.isIOS && this.useNativeIOS){
17546 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
17549 var v = this.inputEl().getValue();
17555 * Clears any text/value currently set in the field
17557 clearValue : function(){
17559 if(this.hiddenField){
17560 this.hiddenField.dom.value = '';
17563 this.setRawValue('');
17564 this.lastSelectionText = '';
17565 this.lastData = false;
17567 var close = this.closeTriggerEl();
17578 * Sets the specified value into the field. If the value finds a match, the corresponding record text
17579 * will be displayed in the field. If the value does not match the data value of an existing item,
17580 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
17581 * Otherwise the field will be blank (although the value will still be set).
17582 * @param {String} value The value to match
17584 setValue : function(v)
17586 if(Roo.isIOS && this.useNativeIOS){
17587 this.setIOSValue(v);
17597 if(this.valueField){
17598 var r = this.findRecord(this.valueField, v);
17600 text = r.data[this.displayField];
17601 }else if(this.valueNotFoundText !== undefined){
17602 text = this.valueNotFoundText;
17605 this.lastSelectionText = text;
17606 if(this.hiddenField){
17607 this.hiddenField.dom.value = v;
17609 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
17612 var close = this.closeTriggerEl();
17615 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
17621 * @property {Object} the last set data for the element
17626 * Sets the value of the field based on a object which is related to the record format for the store.
17627 * @param {Object} value the value to set as. or false on reset?
17629 setFromData : function(o){
17636 var dv = ''; // display value
17637 var vv = ''; // value value..
17639 if (this.displayField) {
17640 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17642 // this is an error condition!!!
17643 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
17646 if(this.valueField){
17647 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
17650 var close = this.closeTriggerEl();
17653 if(dv.length || vv * 1 > 0){
17655 this.blockFocus=true;
17661 if(this.hiddenField){
17662 this.hiddenField.dom.value = vv;
17664 this.lastSelectionText = dv;
17665 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17669 // no hidden field.. - we store the value in 'value', but still display
17670 // display field!!!!
17671 this.lastSelectionText = dv;
17672 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17679 reset : function(){
17680 // overridden so that last data is reset..
17687 this.setValue(this.originalValue);
17688 //this.clearInvalid();
17689 this.lastData = false;
17691 this.view.clearSelections();
17697 findRecord : function(prop, value){
17699 if(this.store.getCount() > 0){
17700 this.store.each(function(r){
17701 if(r.data[prop] == value){
17711 getName: function()
17713 // returns hidden if it's set..
17714 if (!this.rendered) {return ''};
17715 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
17719 onViewMove : function(e, t){
17720 this.inKeyMode = false;
17724 onViewOver : function(e, t){
17725 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
17728 var item = this.view.findItemFromChild(t);
17731 var index = this.view.indexOf(item);
17732 this.select(index, false);
17737 onViewClick : function(view, doFocus, el, e)
17739 var index = this.view.getSelectedIndexes()[0];
17741 var r = this.store.getAt(index);
17745 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
17752 Roo.each(this.tickItems, function(v,k){
17754 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
17756 _this.tickItems.splice(k, 1);
17758 if(typeof(e) == 'undefined' && view == false){
17759 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
17771 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
17772 this.tickItems.push(r.data);
17775 if(typeof(e) == 'undefined' && view == false){
17776 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
17783 this.onSelect(r, index);
17785 if(doFocus !== false && !this.blockFocus){
17786 this.inputEl().focus();
17791 restrictHeight : function(){
17792 //this.innerList.dom.style.height = '';
17793 //var inner = this.innerList.dom;
17794 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
17795 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
17796 //this.list.beginUpdate();
17797 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
17798 this.list.alignTo(this.inputEl(), this.listAlign);
17799 this.list.alignTo(this.inputEl(), this.listAlign);
17800 //this.list.endUpdate();
17804 onEmptyResults : function(){
17806 if(this.tickable && this.editable){
17807 this.hasFocus = false;
17808 this.restrictHeight();
17816 * Returns true if the dropdown list is expanded, else false.
17818 isExpanded : function(){
17819 return this.list.isVisible();
17823 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
17824 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17825 * @param {String} value The data value of the item to select
17826 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17827 * selected item if it is not currently in view (defaults to true)
17828 * @return {Boolean} True if the value matched an item in the list, else false
17830 selectByValue : function(v, scrollIntoView){
17831 if(v !== undefined && v !== null){
17832 var r = this.findRecord(this.valueField || this.displayField, v);
17834 this.select(this.store.indexOf(r), scrollIntoView);
17842 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
17843 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17844 * @param {Number} index The zero-based index of the list item to select
17845 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17846 * selected item if it is not currently in view (defaults to true)
17848 select : function(index, scrollIntoView){
17849 this.selectedIndex = index;
17850 this.view.select(index);
17851 if(scrollIntoView !== false){
17852 var el = this.view.getNode(index);
17854 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
17857 this.list.scrollChildIntoView(el, false);
17863 selectNext : function(){
17864 var ct = this.store.getCount();
17866 if(this.selectedIndex == -1){
17868 }else if(this.selectedIndex < ct-1){
17869 this.select(this.selectedIndex+1);
17875 selectPrev : function(){
17876 var ct = this.store.getCount();
17878 if(this.selectedIndex == -1){
17880 }else if(this.selectedIndex != 0){
17881 this.select(this.selectedIndex-1);
17887 onKeyUp : function(e){
17888 if(this.editable !== false && !e.isSpecialKey()){
17889 this.lastKey = e.getKey();
17890 this.dqTask.delay(this.queryDelay);
17895 validateBlur : function(){
17896 return !this.list || !this.list.isVisible();
17900 initQuery : function(){
17902 var v = this.getRawValue();
17904 if(this.tickable && this.editable){
17905 v = this.tickableInputEl().getValue();
17912 doForce : function(){
17913 if(this.inputEl().dom.value.length > 0){
17914 this.inputEl().dom.value =
17915 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
17921 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
17922 * query allowing the query action to be canceled if needed.
17923 * @param {String} query The SQL query to execute
17924 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
17925 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
17926 * saved in the current store (defaults to false)
17928 doQuery : function(q, forceAll){
17930 if(q === undefined || q === null){
17935 forceAll: forceAll,
17939 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
17944 forceAll = qe.forceAll;
17945 if(forceAll === true || (q.length >= this.minChars)){
17947 this.hasQuery = true;
17949 if(this.lastQuery != q || this.alwaysQuery){
17950 this.lastQuery = q;
17951 if(this.mode == 'local'){
17952 this.selectedIndex = -1;
17954 this.store.clearFilter();
17957 if(this.specialFilter){
17958 this.fireEvent('specialfilter', this);
17963 this.store.filter(this.displayField, q);
17966 this.store.fireEvent("datachanged", this.store);
17973 this.store.baseParams[this.queryParam] = q;
17975 var options = {params : this.getParams(q)};
17978 options.add = true;
17979 options.params.start = this.page * this.pageSize;
17982 this.store.load(options);
17985 * this code will make the page width larger, at the beginning, the list not align correctly,
17986 * we should expand the list on onLoad
17987 * so command out it
17992 this.selectedIndex = -1;
17997 this.loadNext = false;
18001 getParams : function(q){
18003 //p[this.queryParam] = q;
18007 p.limit = this.pageSize;
18013 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18015 collapse : function(){
18016 if(!this.isExpanded()){
18022 this.hasFocus = false;
18026 this.cancelBtn.hide();
18027 this.trigger.show();
18030 this.tickableInputEl().dom.value = '';
18031 this.tickableInputEl().blur();
18036 Roo.get(document).un('mousedown', this.collapseIf, this);
18037 Roo.get(document).un('mousewheel', this.collapseIf, this);
18038 if (!this.editable) {
18039 Roo.get(document).un('keydown', this.listKeyPress, this);
18041 this.fireEvent('collapse', this);
18047 collapseIf : function(e){
18048 var in_combo = e.within(this.el);
18049 var in_list = e.within(this.list);
18050 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18052 if (in_combo || in_list || is_list) {
18053 //e.stopPropagation();
18058 this.onTickableFooterButtonClick(e, false, false);
18066 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18068 expand : function(){
18070 if(this.isExpanded() || !this.hasFocus){
18074 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18075 this.list.setWidth(lw);
18081 this.restrictHeight();
18085 this.tickItems = Roo.apply([], this.item);
18088 this.cancelBtn.show();
18089 this.trigger.hide();
18092 this.tickableInputEl().focus();
18097 Roo.get(document).on('mousedown', this.collapseIf, this);
18098 Roo.get(document).on('mousewheel', this.collapseIf, this);
18099 if (!this.editable) {
18100 Roo.get(document).on('keydown', this.listKeyPress, this);
18103 this.fireEvent('expand', this);
18107 // Implements the default empty TriggerField.onTriggerClick function
18108 onTriggerClick : function(e)
18110 Roo.log('trigger click');
18112 if(this.disabled || !this.triggerList){
18117 this.loadNext = false;
18119 if(this.isExpanded()){
18121 if (!this.blockFocus) {
18122 this.inputEl().focus();
18126 this.hasFocus = true;
18127 if(this.triggerAction == 'all') {
18128 this.doQuery(this.allQuery, true);
18130 this.doQuery(this.getRawValue());
18132 if (!this.blockFocus) {
18133 this.inputEl().focus();
18138 onTickableTriggerClick : function(e)
18145 this.loadNext = false;
18146 this.hasFocus = true;
18148 if(this.triggerAction == 'all') {
18149 this.doQuery(this.allQuery, true);
18151 this.doQuery(this.getRawValue());
18155 onSearchFieldClick : function(e)
18157 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18158 this.onTickableFooterButtonClick(e, false, false);
18162 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18167 this.loadNext = false;
18168 this.hasFocus = true;
18170 if(this.triggerAction == 'all') {
18171 this.doQuery(this.allQuery, true);
18173 this.doQuery(this.getRawValue());
18177 listKeyPress : function(e)
18179 //Roo.log('listkeypress');
18180 // scroll to first matching element based on key pres..
18181 if (e.isSpecialKey()) {
18184 var k = String.fromCharCode(e.getKey()).toUpperCase();
18187 var csel = this.view.getSelectedNodes();
18188 var cselitem = false;
18190 var ix = this.view.indexOf(csel[0]);
18191 cselitem = this.store.getAt(ix);
18192 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18198 this.store.each(function(v) {
18200 // start at existing selection.
18201 if (cselitem.id == v.id) {
18207 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18208 match = this.store.indexOf(v);
18214 if (match === false) {
18215 return true; // no more action?
18218 this.view.select(match);
18219 var sn = Roo.get(this.view.getSelectedNodes()[0]);
18220 sn.scrollIntoView(sn.dom.parentNode, false);
18223 onViewScroll : function(e, t){
18225 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){
18229 this.hasQuery = true;
18231 this.loading = this.list.select('.loading', true).first();
18233 if(this.loading === null){
18234 this.list.createChild({
18236 cls: 'loading roo-select2-more-results roo-select2-active',
18237 html: 'Loading more results...'
18240 this.loading = this.list.select('.loading', true).first();
18242 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18244 this.loading.hide();
18247 this.loading.show();
18252 this.loadNext = true;
18254 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18259 addItem : function(o)
18261 var dv = ''; // display value
18263 if (this.displayField) {
18264 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18266 // this is an error condition!!!
18267 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
18274 var choice = this.choices.createChild({
18276 cls: 'roo-select2-search-choice',
18285 cls: 'roo-select2-search-choice-close fa fa-times',
18290 }, this.searchField);
18292 var close = choice.select('a.roo-select2-search-choice-close', true).first();
18294 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18302 this.inputEl().dom.value = '';
18307 onRemoveItem : function(e, _self, o)
18309 e.preventDefault();
18311 this.lastItem = Roo.apply([], this.item);
18313 var index = this.item.indexOf(o.data) * 1;
18316 Roo.log('not this item?!');
18320 this.item.splice(index, 1);
18325 this.fireEvent('remove', this, e);
18331 syncValue : function()
18333 if(!this.item.length){
18340 Roo.each(this.item, function(i){
18341 if(_this.valueField){
18342 value.push(i[_this.valueField]);
18349 this.value = value.join(',');
18351 if(this.hiddenField){
18352 this.hiddenField.dom.value = this.value;
18355 this.store.fireEvent("datachanged", this.store);
18360 clearItem : function()
18362 if(!this.multiple){
18368 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18376 if(this.tickable && !Roo.isTouch){
18377 this.view.refresh();
18381 inputEl: function ()
18383 if(Roo.isIOS && this.useNativeIOS){
18384 return this.el.select('select.roo-ios-select', true).first();
18387 if(Roo.isTouch && this.mobileTouchView){
18388 return this.el.select('input.form-control',true).first();
18392 return this.searchField;
18395 return this.el.select('input.form-control',true).first();
18398 onTickableFooterButtonClick : function(e, btn, el)
18400 e.preventDefault();
18402 this.lastItem = Roo.apply([], this.item);
18404 if(btn && btn.name == 'cancel'){
18405 this.tickItems = Roo.apply([], this.item);
18414 Roo.each(this.tickItems, function(o){
18422 validate : function()
18424 if(this.getVisibilityEl().hasClass('hidden')){
18428 var v = this.getRawValue();
18431 v = this.getValue();
18434 if(this.disabled || this.allowBlank || v.length){
18439 this.markInvalid();
18443 tickableInputEl : function()
18445 if(!this.tickable || !this.editable){
18446 return this.inputEl();
18449 return this.inputEl().select('.roo-select2-search-field-input', true).first();
18453 getAutoCreateTouchView : function()
18458 cls: 'form-group' //input-group
18464 type : this.inputType,
18465 cls : 'form-control x-combo-noedit',
18466 autocomplete: 'new-password',
18467 placeholder : this.placeholder || '',
18472 input.name = this.name;
18476 input.cls += ' input-' + this.size;
18479 if (this.disabled) {
18480 input.disabled = true;
18484 cls : 'roo-combobox-wrap',
18491 inputblock.cls += ' input-group';
18493 inputblock.cn.unshift({
18495 cls : 'input-group-addon input-group-prepend input-group-text',
18500 if(this.removable && !this.multiple){
18501 inputblock.cls += ' roo-removable';
18503 inputblock.cn.push({
18506 cls : 'roo-combo-removable-btn close'
18510 if(this.hasFeedback && !this.allowBlank){
18512 inputblock.cls += ' has-feedback';
18514 inputblock.cn.push({
18516 cls: 'glyphicon form-control-feedback'
18523 inputblock.cls += (this.before) ? '' : ' input-group';
18525 inputblock.cn.push({
18527 cls : 'input-group-addon input-group-append input-group-text',
18533 var ibwrap = inputblock;
18538 cls: 'roo-select2-choices',
18542 cls: 'roo-select2-search-field',
18555 cls: 'roo-select2-container input-group roo-touchview-combobox ',
18560 cls: 'form-hidden-field'
18566 if(!this.multiple && this.showToggleBtn){
18572 if (this.caret != false) {
18575 cls: 'fa fa-' + this.caret
18582 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
18584 Roo.bootstrap.version == 3 ? caret : '',
18587 cls: 'combobox-clear',
18601 combobox.cls += ' roo-select2-container-multi';
18604 var required = this.allowBlank ? {
18606 style: 'display: none'
18609 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
18610 tooltip : 'This field is required'
18613 var align = this.labelAlign || this.parentLabelAlign();
18615 if (align ==='left' && this.fieldLabel.length) {
18621 cls : 'control-label col-form-label',
18622 html : this.fieldLabel
18626 cls : 'roo-combobox-wrap ',
18633 var labelCfg = cfg.cn[1];
18634 var contentCfg = cfg.cn[2];
18637 if(this.indicatorpos == 'right'){
18642 cls : 'control-label col-form-label',
18646 html : this.fieldLabel
18652 cls : "roo-combobox-wrap ",
18660 labelCfg = cfg.cn[0];
18661 contentCfg = cfg.cn[1];
18666 if(this.labelWidth > 12){
18667 labelCfg.style = "width: " + this.labelWidth + 'px';
18670 if(this.labelWidth < 13 && this.labelmd == 0){
18671 this.labelmd = this.labelWidth;
18674 if(this.labellg > 0){
18675 labelCfg.cls += ' col-lg-' + this.labellg;
18676 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
18679 if(this.labelmd > 0){
18680 labelCfg.cls += ' col-md-' + this.labelmd;
18681 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
18684 if(this.labelsm > 0){
18685 labelCfg.cls += ' col-sm-' + this.labelsm;
18686 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
18689 if(this.labelxs > 0){
18690 labelCfg.cls += ' col-xs-' + this.labelxs;
18691 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
18695 } else if ( this.fieldLabel.length) {
18700 cls : 'control-label',
18701 html : this.fieldLabel
18712 if(this.indicatorpos == 'right'){
18716 cls : 'control-label',
18717 html : this.fieldLabel,
18735 var settings = this;
18737 ['xs','sm','md','lg'].map(function(size){
18738 if (settings[size]) {
18739 cfg.cls += ' col-' + size + '-' + settings[size];
18746 initTouchView : function()
18748 this.renderTouchView();
18750 this.touchViewEl.on('scroll', function(){
18751 this.el.dom.scrollTop = 0;
18754 this.originalValue = this.getValue();
18756 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
18758 this.inputEl().on("click", this.showTouchView, this);
18759 if (this.triggerEl) {
18760 this.triggerEl.on("click", this.showTouchView, this);
18764 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
18765 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
18767 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
18769 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
18770 this.store.on('load', this.onTouchViewLoad, this);
18771 this.store.on('loadexception', this.onTouchViewLoadException, this);
18773 if(this.hiddenName){
18775 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
18777 this.hiddenField.dom.value =
18778 this.hiddenValue !== undefined ? this.hiddenValue :
18779 this.value !== undefined ? this.value : '';
18781 this.el.dom.removeAttribute('name');
18782 this.hiddenField.dom.setAttribute('name', this.hiddenName);
18786 this.choices = this.el.select('ul.roo-select2-choices', true).first();
18787 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
18790 if(this.removable && !this.multiple){
18791 var close = this.closeTriggerEl();
18793 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
18794 close.on('click', this.removeBtnClick, this, close);
18798 * fix the bug in Safari iOS8
18800 this.inputEl().on("focus", function(e){
18801 document.activeElement.blur();
18804 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
18811 renderTouchView : function()
18813 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
18814 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18816 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
18817 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18819 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
18820 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18821 this.touchViewBodyEl.setStyle('overflow', 'auto');
18823 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
18824 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18826 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
18827 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18831 showTouchView : function()
18837 this.touchViewHeaderEl.hide();
18839 if(this.modalTitle.length){
18840 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
18841 this.touchViewHeaderEl.show();
18844 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
18845 this.touchViewEl.show();
18847 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
18849 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
18850 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18852 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18854 if(this.modalTitle.length){
18855 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18858 this.touchViewBodyEl.setHeight(bodyHeight);
18862 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
18864 this.touchViewEl.addClass(['in','show']);
18867 if(this._touchViewMask){
18868 Roo.get(document.body).addClass("x-body-masked");
18869 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18870 this._touchViewMask.setStyle('z-index', 10000);
18871 this._touchViewMask.addClass('show');
18874 this.doTouchViewQuery();
18878 hideTouchView : function()
18880 this.touchViewEl.removeClass(['in','show']);
18884 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
18886 this.touchViewEl.setStyle('display', 'none');
18889 if(this._touchViewMask){
18890 this._touchViewMask.removeClass('show');
18891 Roo.get(document.body).removeClass("x-body-masked");
18895 setTouchViewValue : function()
18902 Roo.each(this.tickItems, function(o){
18907 this.hideTouchView();
18910 doTouchViewQuery : function()
18919 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
18923 if(!this.alwaysQuery || this.mode == 'local'){
18924 this.onTouchViewLoad();
18931 onTouchViewBeforeLoad : function(combo,opts)
18937 onTouchViewLoad : function()
18939 if(this.store.getCount() < 1){
18940 this.onTouchViewEmptyResults();
18944 this.clearTouchView();
18946 var rawValue = this.getRawValue();
18948 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
18950 this.tickItems = [];
18952 this.store.data.each(function(d, rowIndex){
18953 var row = this.touchViewListGroup.createChild(template);
18955 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
18956 row.addClass(d.data.cls);
18959 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
18962 html : d.data[this.displayField]
18965 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
18966 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
18969 row.removeClass('selected');
18970 if(!this.multiple && this.valueField &&
18971 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
18974 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18975 row.addClass('selected');
18978 if(this.multiple && this.valueField &&
18979 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
18983 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18984 this.tickItems.push(d.data);
18987 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
18991 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
18993 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18995 if(this.modalTitle.length){
18996 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18999 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19001 if(this.mobile_restrict_height && listHeight < bodyHeight){
19002 this.touchViewBodyEl.setHeight(listHeight);
19007 if(firstChecked && listHeight > bodyHeight){
19008 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19013 onTouchViewLoadException : function()
19015 this.hideTouchView();
19018 onTouchViewEmptyResults : function()
19020 this.clearTouchView();
19022 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
19024 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19028 clearTouchView : function()
19030 this.touchViewListGroup.dom.innerHTML = '';
19033 onTouchViewClick : function(e, el, o)
19035 e.preventDefault();
19038 var rowIndex = o.rowIndex;
19040 var r = this.store.getAt(rowIndex);
19042 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19044 if(!this.multiple){
19045 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19046 c.dom.removeAttribute('checked');
19049 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19051 this.setFromData(r.data);
19053 var close = this.closeTriggerEl();
19059 this.hideTouchView();
19061 this.fireEvent('select', this, r, rowIndex);
19066 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19067 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19068 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19072 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19073 this.addItem(r.data);
19074 this.tickItems.push(r.data);
19078 getAutoCreateNativeIOS : function()
19081 cls: 'form-group' //input-group,
19086 cls : 'roo-ios-select'
19090 combobox.name = this.name;
19093 if (this.disabled) {
19094 combobox.disabled = true;
19097 var settings = this;
19099 ['xs','sm','md','lg'].map(function(size){
19100 if (settings[size]) {
19101 cfg.cls += ' col-' + size + '-' + settings[size];
19111 initIOSView : function()
19113 this.store.on('load', this.onIOSViewLoad, this);
19118 onIOSViewLoad : function()
19120 if(this.store.getCount() < 1){
19124 this.clearIOSView();
19126 if(this.allowBlank) {
19128 var default_text = '-- SELECT --';
19130 if(this.placeholder.length){
19131 default_text = this.placeholder;
19134 if(this.emptyTitle.length){
19135 default_text += ' - ' + this.emptyTitle + ' -';
19138 var opt = this.inputEl().createChild({
19141 html : default_text
19145 o[this.valueField] = 0;
19146 o[this.displayField] = default_text;
19148 this.ios_options.push({
19155 this.store.data.each(function(d, rowIndex){
19159 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19160 html = d.data[this.displayField];
19165 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19166 value = d.data[this.valueField];
19175 if(this.value == d.data[this.valueField]){
19176 option['selected'] = true;
19179 var opt = this.inputEl().createChild(option);
19181 this.ios_options.push({
19188 this.inputEl().on('change', function(){
19189 this.fireEvent('select', this);
19194 clearIOSView: function()
19196 this.inputEl().dom.innerHTML = '';
19198 this.ios_options = [];
19201 setIOSValue: function(v)
19205 if(!this.ios_options){
19209 Roo.each(this.ios_options, function(opts){
19211 opts.el.dom.removeAttribute('selected');
19213 if(opts.data[this.valueField] != v){
19217 opts.el.dom.setAttribute('selected', true);
19223 * @cfg {Boolean} grow
19227 * @cfg {Number} growMin
19231 * @cfg {Number} growMax
19240 Roo.apply(Roo.bootstrap.ComboBox, {
19244 cls: 'modal-header',
19266 cls: 'list-group-item',
19270 cls: 'roo-combobox-list-group-item-value'
19274 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19288 listItemCheckbox : {
19290 cls: 'list-group-item',
19294 cls: 'roo-combobox-list-group-item-value'
19298 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19314 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19319 cls: 'modal-footer',
19327 cls: 'col-xs-6 text-left',
19330 cls: 'btn btn-danger roo-touch-view-cancel',
19336 cls: 'col-xs-6 text-right',
19339 cls: 'btn btn-success roo-touch-view-ok',
19350 Roo.apply(Roo.bootstrap.ComboBox, {
19352 touchViewTemplate : {
19354 cls: 'modal fade roo-combobox-touch-view',
19358 cls: 'modal-dialog',
19359 style : 'position:fixed', // we have to fix position....
19363 cls: 'modal-content',
19365 Roo.bootstrap.ComboBox.header,
19366 Roo.bootstrap.ComboBox.body,
19367 Roo.bootstrap.ComboBox.footer
19376 * Ext JS Library 1.1.1
19377 * Copyright(c) 2006-2007, Ext JS, LLC.
19379 * Originally Released Under LGPL - original licence link has changed is not relivant.
19382 * <script type="text/javascript">
19387 * @extends Roo.util.Observable
19388 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
19389 * This class also supports single and multi selection modes. <br>
19390 * Create a data model bound view:
19392 var store = new Roo.data.Store(...);
19394 var view = new Roo.View({
19396 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
19398 singleSelect: true,
19399 selectedClass: "ydataview-selected",
19403 // listen for node click?
19404 view.on("click", function(vw, index, node, e){
19405 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19409 dataModel.load("foobar.xml");
19411 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19413 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19414 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19416 * Note: old style constructor is still suported (container, template, config)
19419 * Create a new View
19420 * @param {Object} config The config object
19423 Roo.View = function(config, depreciated_tpl, depreciated_config){
19425 this.parent = false;
19427 if (typeof(depreciated_tpl) == 'undefined') {
19428 // new way.. - universal constructor.
19429 Roo.apply(this, config);
19430 this.el = Roo.get(this.el);
19433 this.el = Roo.get(config);
19434 this.tpl = depreciated_tpl;
19435 Roo.apply(this, depreciated_config);
19437 this.wrapEl = this.el.wrap().wrap();
19438 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19441 if(typeof(this.tpl) == "string"){
19442 this.tpl = new Roo.Template(this.tpl);
19444 // support xtype ctors..
19445 this.tpl = new Roo.factory(this.tpl, Roo);
19449 this.tpl.compile();
19454 * @event beforeclick
19455 * Fires before a click is processed. Returns false to cancel the default action.
19456 * @param {Roo.View} this
19457 * @param {Number} index The index of the target node
19458 * @param {HTMLElement} node The target node
19459 * @param {Roo.EventObject} e The raw event object
19461 "beforeclick" : true,
19464 * Fires when a template node is clicked.
19465 * @param {Roo.View} this
19466 * @param {Number} index The index of the target node
19467 * @param {HTMLElement} node The target node
19468 * @param {Roo.EventObject} e The raw event object
19473 * Fires when a template node is double clicked.
19474 * @param {Roo.View} this
19475 * @param {Number} index The index of the target node
19476 * @param {HTMLElement} node The target node
19477 * @param {Roo.EventObject} e The raw event object
19481 * @event contextmenu
19482 * Fires when a template node is right clicked.
19483 * @param {Roo.View} this
19484 * @param {Number} index The index of the target node
19485 * @param {HTMLElement} node The target node
19486 * @param {Roo.EventObject} e The raw event object
19488 "contextmenu" : true,
19490 * @event selectionchange
19491 * Fires when the selected nodes change.
19492 * @param {Roo.View} this
19493 * @param {Array} selections Array of the selected nodes
19495 "selectionchange" : true,
19498 * @event beforeselect
19499 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
19500 * @param {Roo.View} this
19501 * @param {HTMLElement} node The node to be selected
19502 * @param {Array} selections Array of currently selected nodes
19504 "beforeselect" : true,
19506 * @event preparedata
19507 * Fires on every row to render, to allow you to change the data.
19508 * @param {Roo.View} this
19509 * @param {Object} data to be rendered (change this)
19511 "preparedata" : true
19519 "click": this.onClick,
19520 "dblclick": this.onDblClick,
19521 "contextmenu": this.onContextMenu,
19525 this.selections = [];
19527 this.cmp = new Roo.CompositeElementLite([]);
19529 this.store = Roo.factory(this.store, Roo.data);
19530 this.setStore(this.store, true);
19533 if ( this.footer && this.footer.xtype) {
19535 var fctr = this.wrapEl.appendChild(document.createElement("div"));
19537 this.footer.dataSource = this.store;
19538 this.footer.container = fctr;
19539 this.footer = Roo.factory(this.footer, Roo);
19540 fctr.insertFirst(this.el);
19542 // this is a bit insane - as the paging toolbar seems to detach the el..
19543 // dom.parentNode.parentNode.parentNode
19544 // they get detached?
19548 Roo.View.superclass.constructor.call(this);
19553 Roo.extend(Roo.View, Roo.util.Observable, {
19556 * @cfg {Roo.data.Store} store Data store to load data from.
19561 * @cfg {String|Roo.Element} el The container element.
19566 * @cfg {String|Roo.Template} tpl The template used by this View
19570 * @cfg {String} dataName the named area of the template to use as the data area
19571 * Works with domtemplates roo-name="name"
19575 * @cfg {String} selectedClass The css class to add to selected nodes
19577 selectedClass : "x-view-selected",
19579 * @cfg {String} emptyText The empty text to show when nothing is loaded.
19584 * @cfg {String} text to display on mask (default Loading)
19588 * @cfg {Boolean} multiSelect Allow multiple selection
19590 multiSelect : false,
19592 * @cfg {Boolean} singleSelect Allow single selection
19594 singleSelect: false,
19597 * @cfg {Boolean} toggleSelect - selecting
19599 toggleSelect : false,
19602 * @cfg {Boolean} tickable - selecting
19607 * Returns the element this view is bound to.
19608 * @return {Roo.Element}
19610 getEl : function(){
19611 return this.wrapEl;
19617 * Refreshes the view. - called by datachanged on the store. - do not call directly.
19619 refresh : function(){
19620 //Roo.log('refresh');
19623 // if we are using something like 'domtemplate', then
19624 // the what gets used is:
19625 // t.applySubtemplate(NAME, data, wrapping data..)
19626 // the outer template then get' applied with
19627 // the store 'extra data'
19628 // and the body get's added to the
19629 // roo-name="data" node?
19630 // <span class='roo-tpl-{name}'></span> ?????
19634 this.clearSelections();
19635 this.el.update("");
19637 var records = this.store.getRange();
19638 if(records.length < 1) {
19640 // is this valid?? = should it render a template??
19642 this.el.update(this.emptyText);
19646 if (this.dataName) {
19647 this.el.update(t.apply(this.store.meta)); //????
19648 el = this.el.child('.roo-tpl-' + this.dataName);
19651 for(var i = 0, len = records.length; i < len; i++){
19652 var data = this.prepareData(records[i].data, i, records[i]);
19653 this.fireEvent("preparedata", this, data, i, records[i]);
19655 var d = Roo.apply({}, data);
19658 Roo.apply(d, {'roo-id' : Roo.id()});
19662 Roo.each(this.parent.item, function(item){
19663 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
19666 Roo.apply(d, {'roo-data-checked' : 'checked'});
19670 html[html.length] = Roo.util.Format.trim(
19672 t.applySubtemplate(this.dataName, d, this.store.meta) :
19679 el.update(html.join(""));
19680 this.nodes = el.dom.childNodes;
19681 this.updateIndexes(0);
19686 * Function to override to reformat the data that is sent to
19687 * the template for each node.
19688 * DEPRICATED - use the preparedata event handler.
19689 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
19690 * a JSON object for an UpdateManager bound view).
19692 prepareData : function(data, index, record)
19694 this.fireEvent("preparedata", this, data, index, record);
19698 onUpdate : function(ds, record){
19699 // Roo.log('on update');
19700 this.clearSelections();
19701 var index = this.store.indexOf(record);
19702 var n = this.nodes[index];
19703 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
19704 n.parentNode.removeChild(n);
19705 this.updateIndexes(index, index);
19711 onAdd : function(ds, records, index)
19713 //Roo.log(['on Add', ds, records, index] );
19714 this.clearSelections();
19715 if(this.nodes.length == 0){
19719 var n = this.nodes[index];
19720 for(var i = 0, len = records.length; i < len; i++){
19721 var d = this.prepareData(records[i].data, i, records[i]);
19723 this.tpl.insertBefore(n, d);
19726 this.tpl.append(this.el, d);
19729 this.updateIndexes(index);
19732 onRemove : function(ds, record, index){
19733 // Roo.log('onRemove');
19734 this.clearSelections();
19735 var el = this.dataName ?
19736 this.el.child('.roo-tpl-' + this.dataName) :
19739 el.dom.removeChild(this.nodes[index]);
19740 this.updateIndexes(index);
19744 * Refresh an individual node.
19745 * @param {Number} index
19747 refreshNode : function(index){
19748 this.onUpdate(this.store, this.store.getAt(index));
19751 updateIndexes : function(startIndex, endIndex){
19752 var ns = this.nodes;
19753 startIndex = startIndex || 0;
19754 endIndex = endIndex || ns.length - 1;
19755 for(var i = startIndex; i <= endIndex; i++){
19756 ns[i].nodeIndex = i;
19761 * Changes the data store this view uses and refresh the view.
19762 * @param {Store} store
19764 setStore : function(store, initial){
19765 if(!initial && this.store){
19766 this.store.un("datachanged", this.refresh);
19767 this.store.un("add", this.onAdd);
19768 this.store.un("remove", this.onRemove);
19769 this.store.un("update", this.onUpdate);
19770 this.store.un("clear", this.refresh);
19771 this.store.un("beforeload", this.onBeforeLoad);
19772 this.store.un("load", this.onLoad);
19773 this.store.un("loadexception", this.onLoad);
19777 store.on("datachanged", this.refresh, this);
19778 store.on("add", this.onAdd, this);
19779 store.on("remove", this.onRemove, this);
19780 store.on("update", this.onUpdate, this);
19781 store.on("clear", this.refresh, this);
19782 store.on("beforeload", this.onBeforeLoad, this);
19783 store.on("load", this.onLoad, this);
19784 store.on("loadexception", this.onLoad, this);
19792 * onbeforeLoad - masks the loading area.
19795 onBeforeLoad : function(store,opts)
19797 //Roo.log('onBeforeLoad');
19799 this.el.update("");
19801 this.el.mask(this.mask ? this.mask : "Loading" );
19803 onLoad : function ()
19810 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
19811 * @param {HTMLElement} node
19812 * @return {HTMLElement} The template node
19814 findItemFromChild : function(node){
19815 var el = this.dataName ?
19816 this.el.child('.roo-tpl-' + this.dataName,true) :
19819 if(!node || node.parentNode == el){
19822 var p = node.parentNode;
19823 while(p && p != el){
19824 if(p.parentNode == el){
19833 onClick : function(e){
19834 var item = this.findItemFromChild(e.getTarget());
19836 var index = this.indexOf(item);
19837 if(this.onItemClick(item, index, e) !== false){
19838 this.fireEvent("click", this, index, item, e);
19841 this.clearSelections();
19846 onContextMenu : function(e){
19847 var item = this.findItemFromChild(e.getTarget());
19849 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
19854 onDblClick : function(e){
19855 var item = this.findItemFromChild(e.getTarget());
19857 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
19861 onItemClick : function(item, index, e)
19863 if(this.fireEvent("beforeclick", this, index, item, e) === false){
19866 if (this.toggleSelect) {
19867 var m = this.isSelected(item) ? 'unselect' : 'select';
19870 _t[m](item, true, false);
19873 if(this.multiSelect || this.singleSelect){
19874 if(this.multiSelect && e.shiftKey && this.lastSelection){
19875 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
19877 this.select(item, this.multiSelect && e.ctrlKey);
19878 this.lastSelection = item;
19881 if(!this.tickable){
19882 e.preventDefault();
19890 * Get the number of selected nodes.
19893 getSelectionCount : function(){
19894 return this.selections.length;
19898 * Get the currently selected nodes.
19899 * @return {Array} An array of HTMLElements
19901 getSelectedNodes : function(){
19902 return this.selections;
19906 * Get the indexes of the selected nodes.
19909 getSelectedIndexes : function(){
19910 var indexes = [], s = this.selections;
19911 for(var i = 0, len = s.length; i < len; i++){
19912 indexes.push(s[i].nodeIndex);
19918 * Clear all selections
19919 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
19921 clearSelections : function(suppressEvent){
19922 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
19923 this.cmp.elements = this.selections;
19924 this.cmp.removeClass(this.selectedClass);
19925 this.selections = [];
19926 if(!suppressEvent){
19927 this.fireEvent("selectionchange", this, this.selections);
19933 * Returns true if the passed node is selected
19934 * @param {HTMLElement/Number} node The node or node index
19935 * @return {Boolean}
19937 isSelected : function(node){
19938 var s = this.selections;
19942 node = this.getNode(node);
19943 return s.indexOf(node) !== -1;
19948 * @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
19949 * @param {Boolean} keepExisting (optional) true to keep existing selections
19950 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
19952 select : function(nodeInfo, keepExisting, suppressEvent){
19953 if(nodeInfo instanceof Array){
19955 this.clearSelections(true);
19957 for(var i = 0, len = nodeInfo.length; i < len; i++){
19958 this.select(nodeInfo[i], true, true);
19962 var node = this.getNode(nodeInfo);
19963 if(!node || this.isSelected(node)){
19964 return; // already selected.
19967 this.clearSelections(true);
19970 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
19971 Roo.fly(node).addClass(this.selectedClass);
19972 this.selections.push(node);
19973 if(!suppressEvent){
19974 this.fireEvent("selectionchange", this, this.selections);
19982 * @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
19983 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
19984 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
19986 unselect : function(nodeInfo, keepExisting, suppressEvent)
19988 if(nodeInfo instanceof Array){
19989 Roo.each(this.selections, function(s) {
19990 this.unselect(s, nodeInfo);
19994 var node = this.getNode(nodeInfo);
19995 if(!node || !this.isSelected(node)){
19996 //Roo.log("not selected");
19997 return; // not selected.
20001 Roo.each(this.selections, function(s) {
20003 Roo.fly(node).removeClass(this.selectedClass);
20010 this.selections= ns;
20011 this.fireEvent("selectionchange", this, this.selections);
20015 * Gets a template node.
20016 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20017 * @return {HTMLElement} The node or null if it wasn't found
20019 getNode : function(nodeInfo){
20020 if(typeof nodeInfo == "string"){
20021 return document.getElementById(nodeInfo);
20022 }else if(typeof nodeInfo == "number"){
20023 return this.nodes[nodeInfo];
20029 * Gets a range template nodes.
20030 * @param {Number} startIndex
20031 * @param {Number} endIndex
20032 * @return {Array} An array of nodes
20034 getNodes : function(start, end){
20035 var ns = this.nodes;
20036 start = start || 0;
20037 end = typeof end == "undefined" ? ns.length - 1 : end;
20040 for(var i = start; i <= end; i++){
20044 for(var i = start; i >= end; i--){
20052 * Finds the index of the passed node
20053 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20054 * @return {Number} The index of the node or -1
20056 indexOf : function(node){
20057 node = this.getNode(node);
20058 if(typeof node.nodeIndex == "number"){
20059 return node.nodeIndex;
20061 var ns = this.nodes;
20062 for(var i = 0, len = ns.length; i < len; i++){
20073 * based on jquery fullcalendar
20077 Roo.bootstrap = Roo.bootstrap || {};
20079 * @class Roo.bootstrap.Calendar
20080 * @extends Roo.bootstrap.Component
20081 * Bootstrap Calendar class
20082 * @cfg {Boolean} loadMask (true|false) default false
20083 * @cfg {Object} header generate the user specific header of the calendar, default false
20086 * Create a new Container
20087 * @param {Object} config The config object
20092 Roo.bootstrap.Calendar = function(config){
20093 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20097 * Fires when a date is selected
20098 * @param {DatePicker} this
20099 * @param {Date} date The selected date
20103 * @event monthchange
20104 * Fires when the displayed month changes
20105 * @param {DatePicker} this
20106 * @param {Date} date The selected month
20108 'monthchange': true,
20110 * @event evententer
20111 * Fires when mouse over an event
20112 * @param {Calendar} this
20113 * @param {event} Event
20115 'evententer': true,
20117 * @event eventleave
20118 * Fires when the mouse leaves an
20119 * @param {Calendar} this
20122 'eventleave': true,
20124 * @event eventclick
20125 * Fires when the mouse click an
20126 * @param {Calendar} this
20135 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
20138 * @cfg {Number} startDay
20139 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20147 getAutoCreate : function(){
20150 var fc_button = function(name, corner, style, content ) {
20151 return Roo.apply({},{
20153 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
20155 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20158 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20169 style : 'width:100%',
20176 cls : 'fc-header-left',
20178 fc_button('prev', 'left', 'arrow', '‹' ),
20179 fc_button('next', 'right', 'arrow', '›' ),
20180 { tag: 'span', cls: 'fc-header-space' },
20181 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
20189 cls : 'fc-header-center',
20193 cls: 'fc-header-title',
20196 html : 'month / year'
20204 cls : 'fc-header-right',
20206 /* fc_button('month', 'left', '', 'month' ),
20207 fc_button('week', '', '', 'week' ),
20208 fc_button('day', 'right', '', 'day' )
20220 header = this.header;
20223 var cal_heads = function() {
20225 // fixme - handle this.
20227 for (var i =0; i < Date.dayNames.length; i++) {
20228 var d = Date.dayNames[i];
20231 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20232 html : d.substring(0,3)
20236 ret[0].cls += ' fc-first';
20237 ret[6].cls += ' fc-last';
20240 var cal_cell = function(n) {
20243 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20248 cls: 'fc-day-number',
20252 cls: 'fc-day-content',
20256 style: 'position: relative;' // height: 17px;
20268 var cal_rows = function() {
20271 for (var r = 0; r < 6; r++) {
20278 for (var i =0; i < Date.dayNames.length; i++) {
20279 var d = Date.dayNames[i];
20280 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20283 row.cn[0].cls+=' fc-first';
20284 row.cn[0].cn[0].style = 'min-height:90px';
20285 row.cn[6].cls+=' fc-last';
20289 ret[0].cls += ' fc-first';
20290 ret[4].cls += ' fc-prev-last';
20291 ret[5].cls += ' fc-last';
20298 cls: 'fc-border-separate',
20299 style : 'width:100%',
20307 cls : 'fc-first fc-last',
20325 cls : 'fc-content',
20326 style : "position: relative;",
20329 cls : 'fc-view fc-view-month fc-grid',
20330 style : 'position: relative',
20331 unselectable : 'on',
20334 cls : 'fc-event-container',
20335 style : 'position:absolute;z-index:8;top:0;left:0;'
20353 initEvents : function()
20356 throw "can not find store for calendar";
20362 style: "text-align:center",
20366 style: "background-color:white;width:50%;margin:250 auto",
20370 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
20381 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20383 var size = this.el.select('.fc-content', true).first().getSize();
20384 this.maskEl.setSize(size.width, size.height);
20385 this.maskEl.enableDisplayMode("block");
20386 if(!this.loadMask){
20387 this.maskEl.hide();
20390 this.store = Roo.factory(this.store, Roo.data);
20391 this.store.on('load', this.onLoad, this);
20392 this.store.on('beforeload', this.onBeforeLoad, this);
20396 this.cells = this.el.select('.fc-day',true);
20397 //Roo.log(this.cells);
20398 this.textNodes = this.el.query('.fc-day-number');
20399 this.cells.addClassOnOver('fc-state-hover');
20401 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20402 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20403 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20404 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20406 this.on('monthchange', this.onMonthChange, this);
20408 this.update(new Date().clearTime());
20411 resize : function() {
20412 var sz = this.el.getSize();
20414 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20415 this.el.select('.fc-day-content div',true).setHeight(34);
20420 showPrevMonth : function(e){
20421 this.update(this.activeDate.add("mo", -1));
20423 showToday : function(e){
20424 this.update(new Date().clearTime());
20427 showNextMonth : function(e){
20428 this.update(this.activeDate.add("mo", 1));
20432 showPrevYear : function(){
20433 this.update(this.activeDate.add("y", -1));
20437 showNextYear : function(){
20438 this.update(this.activeDate.add("y", 1));
20443 update : function(date)
20445 var vd = this.activeDate;
20446 this.activeDate = date;
20447 // if(vd && this.el){
20448 // var t = date.getTime();
20449 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20450 // Roo.log('using add remove');
20452 // this.fireEvent('monthchange', this, date);
20454 // this.cells.removeClass("fc-state-highlight");
20455 // this.cells.each(function(c){
20456 // if(c.dateValue == t){
20457 // c.addClass("fc-state-highlight");
20458 // setTimeout(function(){
20459 // try{c.dom.firstChild.focus();}catch(e){}
20469 var days = date.getDaysInMonth();
20471 var firstOfMonth = date.getFirstDateOfMonth();
20472 var startingPos = firstOfMonth.getDay()-this.startDay;
20474 if(startingPos < this.startDay){
20478 var pm = date.add(Date.MONTH, -1);
20479 var prevStart = pm.getDaysInMonth()-startingPos;
20481 this.cells = this.el.select('.fc-day',true);
20482 this.textNodes = this.el.query('.fc-day-number');
20483 this.cells.addClassOnOver('fc-state-hover');
20485 var cells = this.cells.elements;
20486 var textEls = this.textNodes;
20488 Roo.each(cells, function(cell){
20489 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
20492 days += startingPos;
20494 // convert everything to numbers so it's fast
20495 var day = 86400000;
20496 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
20499 //Roo.log(prevStart);
20501 var today = new Date().clearTime().getTime();
20502 var sel = date.clearTime().getTime();
20503 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
20504 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
20505 var ddMatch = this.disabledDatesRE;
20506 var ddText = this.disabledDatesText;
20507 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
20508 var ddaysText = this.disabledDaysText;
20509 var format = this.format;
20511 var setCellClass = function(cal, cell){
20515 //Roo.log('set Cell Class');
20517 var t = d.getTime();
20521 cell.dateValue = t;
20523 cell.className += " fc-today";
20524 cell.className += " fc-state-highlight";
20525 cell.title = cal.todayText;
20528 // disable highlight in other month..
20529 //cell.className += " fc-state-highlight";
20534 cell.className = " fc-state-disabled";
20535 cell.title = cal.minText;
20539 cell.className = " fc-state-disabled";
20540 cell.title = cal.maxText;
20544 if(ddays.indexOf(d.getDay()) != -1){
20545 cell.title = ddaysText;
20546 cell.className = " fc-state-disabled";
20549 if(ddMatch && format){
20550 var fvalue = d.dateFormat(format);
20551 if(ddMatch.test(fvalue)){
20552 cell.title = ddText.replace("%0", fvalue);
20553 cell.className = " fc-state-disabled";
20557 if (!cell.initialClassName) {
20558 cell.initialClassName = cell.dom.className;
20561 cell.dom.className = cell.initialClassName + ' ' + cell.className;
20566 for(; i < startingPos; i++) {
20567 textEls[i].innerHTML = (++prevStart);
20568 d.setDate(d.getDate()+1);
20570 cells[i].className = "fc-past fc-other-month";
20571 setCellClass(this, cells[i]);
20576 for(; i < days; i++){
20577 intDay = i - startingPos + 1;
20578 textEls[i].innerHTML = (intDay);
20579 d.setDate(d.getDate()+1);
20581 cells[i].className = ''; // "x-date-active";
20582 setCellClass(this, cells[i]);
20586 for(; i < 42; i++) {
20587 textEls[i].innerHTML = (++extraDays);
20588 d.setDate(d.getDate()+1);
20590 cells[i].className = "fc-future fc-other-month";
20591 setCellClass(this, cells[i]);
20594 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
20596 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
20598 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
20599 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
20601 if(totalRows != 6){
20602 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
20603 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
20606 this.fireEvent('monthchange', this, date);
20610 if(!this.internalRender){
20611 var main = this.el.dom.firstChild;
20612 var w = main.offsetWidth;
20613 this.el.setWidth(w + this.el.getBorderWidth("lr"));
20614 Roo.fly(main).setWidth(w);
20615 this.internalRender = true;
20616 // opera does not respect the auto grow header center column
20617 // then, after it gets a width opera refuses to recalculate
20618 // without a second pass
20619 if(Roo.isOpera && !this.secondPass){
20620 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
20621 this.secondPass = true;
20622 this.update.defer(10, this, [date]);
20629 findCell : function(dt) {
20630 dt = dt.clearTime().getTime();
20632 this.cells.each(function(c){
20633 //Roo.log("check " +c.dateValue + '?=' + dt);
20634 if(c.dateValue == dt){
20644 findCells : function(ev) {
20645 var s = ev.start.clone().clearTime().getTime();
20647 var e= ev.end.clone().clearTime().getTime();
20650 this.cells.each(function(c){
20651 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
20653 if(c.dateValue > e){
20656 if(c.dateValue < s){
20665 // findBestRow: function(cells)
20669 // for (var i =0 ; i < cells.length;i++) {
20670 // ret = Math.max(cells[i].rows || 0,ret);
20677 addItem : function(ev)
20679 // look for vertical location slot in
20680 var cells = this.findCells(ev);
20682 // ev.row = this.findBestRow(cells);
20684 // work out the location.
20688 for(var i =0; i < cells.length; i++) {
20690 cells[i].row = cells[0].row;
20693 cells[i].row = cells[i].row + 1;
20703 if (crow.start.getY() == cells[i].getY()) {
20705 crow.end = cells[i];
20722 cells[0].events.push(ev);
20724 this.calevents.push(ev);
20727 clearEvents: function() {
20729 if(!this.calevents){
20733 Roo.each(this.cells.elements, function(c){
20739 Roo.each(this.calevents, function(e) {
20740 Roo.each(e.els, function(el) {
20741 el.un('mouseenter' ,this.onEventEnter, this);
20742 el.un('mouseleave' ,this.onEventLeave, this);
20747 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
20753 renderEvents: function()
20757 this.cells.each(function(c) {
20766 if(c.row != c.events.length){
20767 r = 4 - (4 - (c.row - c.events.length));
20770 c.events = ev.slice(0, r);
20771 c.more = ev.slice(r);
20773 if(c.more.length && c.more.length == 1){
20774 c.events.push(c.more.pop());
20777 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
20781 this.cells.each(function(c) {
20783 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
20786 for (var e = 0; e < c.events.length; e++){
20787 var ev = c.events[e];
20788 var rows = ev.rows;
20790 for(var i = 0; i < rows.length; i++) {
20792 // how many rows should it span..
20795 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
20796 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
20798 unselectable : "on",
20801 cls: 'fc-event-inner',
20805 // cls: 'fc-event-time',
20806 // html : cells.length > 1 ? '' : ev.time
20810 cls: 'fc-event-title',
20811 html : String.format('{0}', ev.title)
20818 cls: 'ui-resizable-handle ui-resizable-e',
20819 html : '  '
20826 cfg.cls += ' fc-event-start';
20828 if ((i+1) == rows.length) {
20829 cfg.cls += ' fc-event-end';
20832 var ctr = _this.el.select('.fc-event-container',true).first();
20833 var cg = ctr.createChild(cfg);
20835 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
20836 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
20838 var r = (c.more.length) ? 1 : 0;
20839 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
20840 cg.setWidth(ebox.right - sbox.x -2);
20842 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
20843 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
20844 cg.on('click', _this.onEventClick, _this, ev);
20855 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
20856 style : 'position: absolute',
20857 unselectable : "on",
20860 cls: 'fc-event-inner',
20864 cls: 'fc-event-title',
20872 cls: 'ui-resizable-handle ui-resizable-e',
20873 html : '  '
20879 var ctr = _this.el.select('.fc-event-container',true).first();
20880 var cg = ctr.createChild(cfg);
20882 var sbox = c.select('.fc-day-content',true).first().getBox();
20883 var ebox = c.select('.fc-day-content',true).first().getBox();
20885 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
20886 cg.setWidth(ebox.right - sbox.x -2);
20888 cg.on('click', _this.onMoreEventClick, _this, c.more);
20898 onEventEnter: function (e, el,event,d) {
20899 this.fireEvent('evententer', this, el, event);
20902 onEventLeave: function (e, el,event,d) {
20903 this.fireEvent('eventleave', this, el, event);
20906 onEventClick: function (e, el,event,d) {
20907 this.fireEvent('eventclick', this, el, event);
20910 onMonthChange: function () {
20914 onMoreEventClick: function(e, el, more)
20918 this.calpopover.placement = 'right';
20919 this.calpopover.setTitle('More');
20921 this.calpopover.setContent('');
20923 var ctr = this.calpopover.el.select('.popover-content', true).first();
20925 Roo.each(more, function(m){
20927 cls : 'fc-event-hori fc-event-draggable',
20930 var cg = ctr.createChild(cfg);
20932 cg.on('click', _this.onEventClick, _this, m);
20935 this.calpopover.show(el);
20940 onLoad: function ()
20942 this.calevents = [];
20945 if(this.store.getCount() > 0){
20946 this.store.data.each(function(d){
20949 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
20950 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
20951 time : d.data.start_time,
20952 title : d.data.title,
20953 description : d.data.description,
20954 venue : d.data.venue
20959 this.renderEvents();
20961 if(this.calevents.length && this.loadMask){
20962 this.maskEl.hide();
20966 onBeforeLoad: function()
20968 this.clearEvents();
20970 this.maskEl.show();
20984 * @class Roo.bootstrap.Popover
20985 * @extends Roo.bootstrap.Component
20986 * Bootstrap Popover class
20987 * @cfg {String} html contents of the popover (or false to use children..)
20988 * @cfg {String} title of popover (or false to hide)
20989 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
20990 * @cfg {String} trigger click || hover (or false to trigger manually)
20991 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
20992 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
20993 * - if false and it has a 'parent' then it will be automatically added to that element
20994 * - if string - Roo.get will be called
20995 * @cfg {Number} delay - delay before showing
20998 * Create a new Popover
20999 * @param {Object} config The config object
21002 Roo.bootstrap.Popover = function(config){
21003 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21009 * After the popover show
21011 * @param {Roo.bootstrap.Popover} this
21016 * After the popover hide
21018 * @param {Roo.bootstrap.Popover} this
21024 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
21029 placement : 'right',
21030 trigger : 'hover', // hover
21036 can_build_overlaid : false,
21038 maskEl : false, // the mask element
21041 alignEl : false, // when show is called with an element - this get's stored.
21043 getChildContainer : function()
21045 return this.contentEl;
21048 getPopoverHeader : function()
21050 this.title = true; // flag not to hide it..
21051 this.headerEl.addClass('p-0');
21052 return this.headerEl
21056 getAutoCreate : function(){
21059 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21060 style: 'display:block',
21066 cls : 'popover-inner ',
21070 cls: 'popover-title popover-header',
21071 html : this.title === false ? '' : this.title
21074 cls : 'popover-content popover-body ' + (this.cls || ''),
21075 html : this.html || ''
21086 * @param {string} the title
21088 setTitle: function(str)
21092 this.headerEl.dom.innerHTML = str;
21097 * @param {string} the body content
21099 setContent: function(str)
21102 if (this.contentEl) {
21103 this.contentEl.dom.innerHTML = str;
21107 // as it get's added to the bottom of the page.
21108 onRender : function(ct, position)
21110 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21115 var cfg = Roo.apply({}, this.getAutoCreate());
21119 cfg.cls += ' ' + this.cls;
21122 cfg.style = this.style;
21124 //Roo.log("adding to ");
21125 this.el = Roo.get(document.body).createChild(cfg, position);
21126 // Roo.log(this.el);
21129 this.contentEl = this.el.select('.popover-content',true).first();
21130 this.headerEl = this.el.select('.popover-title',true).first();
21133 if(typeof(this.items) != 'undefined'){
21134 var items = this.items;
21137 for(var i =0;i < items.length;i++) {
21138 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21142 this.items = nitems;
21144 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21145 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21152 resizeMask : function()
21154 this.maskEl.setSize(
21155 Roo.lib.Dom.getViewWidth(true),
21156 Roo.lib.Dom.getViewHeight(true)
21160 initEvents : function()
21164 Roo.bootstrap.Popover.register(this);
21167 this.arrowEl = this.el.select('.arrow',true).first();
21168 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21169 this.el.enableDisplayMode('block');
21173 if (this.over === false && !this.parent()) {
21176 if (this.triggers === false) {
21181 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21182 var triggers = this.trigger ? this.trigger.split(' ') : [];
21183 Roo.each(triggers, function(trigger) {
21185 if (trigger == 'click') {
21186 on_el.on('click', this.toggle, this);
21187 } else if (trigger != 'manual') {
21188 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
21189 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21191 on_el.on(eventIn ,this.enter, this);
21192 on_el.on(eventOut, this.leave, this);
21202 toggle : function () {
21203 this.hoverState == 'in' ? this.leave() : this.enter();
21206 enter : function () {
21208 clearTimeout(this.timeout);
21210 this.hoverState = 'in';
21212 if (!this.delay || !this.delay.show) {
21217 this.timeout = setTimeout(function () {
21218 if (_t.hoverState == 'in') {
21221 }, this.delay.show)
21224 leave : function() {
21225 clearTimeout(this.timeout);
21227 this.hoverState = 'out';
21229 if (!this.delay || !this.delay.hide) {
21234 this.timeout = setTimeout(function () {
21235 if (_t.hoverState == 'out') {
21238 }, this.delay.hide)
21242 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21243 * @param {string} (left|right|top|bottom) position
21245 show : function (on_el, placement)
21247 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
21248 on_el = on_el || false; // default to false
21251 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21252 on_el = this.parent().el;
21253 } else if (this.over) {
21254 on_el = Roo.get(this.over);
21259 this.alignEl = Roo.get( on_el );
21262 this.render(document.body);
21268 if (this.title === false) {
21269 this.headerEl.hide();
21274 this.el.dom.style.display = 'block';
21277 if (this.alignEl) {
21278 this.updatePosition(this.placement, true);
21281 // this is usually just done by the builder = to show the popoup in the middle of the scren.
21282 var es = this.el.getSize();
21283 var x = Roo.lib.Dom.getViewWidth()/2;
21284 var y = Roo.lib.Dom.getViewHeight()/2;
21285 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
21290 //var arrow = this.el.select('.arrow',true).first();
21291 //arrow.set(align[2],
21293 this.el.addClass('in');
21297 this.hoverState = 'in';
21300 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
21301 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21302 this.maskEl.dom.style.display = 'block';
21303 this.maskEl.addClass('show');
21305 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21307 this.fireEvent('show', this);
21311 * fire this manually after loading a grid in the table for example
21312 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21313 * @param {Boolean} try and move it if we cant get right position.
21315 updatePosition : function(placement, try_move)
21317 // allow for calling with no parameters
21318 placement = placement ? placement : this.placement;
21319 try_move = typeof(try_move) == 'undefined' ? true : try_move;
21321 this.el.removeClass([
21322 'fade','top','bottom', 'left', 'right','in',
21323 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21325 this.el.addClass(placement + ' bs-popover-' + placement);
21327 if (!this.alignEl ) {
21331 switch (placement) {
21333 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21334 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21335 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21336 //normal display... or moved up/down.
21337 this.el.setXY(offset);
21338 var xy = this.alignEl.getAnchorXY('tr', false);
21340 this.arrowEl.setXY(xy);
21343 // continue through...
21344 return this.updatePosition('left', false);
21348 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21349 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21350 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21351 //normal display... or moved up/down.
21352 this.el.setXY(offset);
21353 var xy = this.alignEl.getAnchorXY('tl', false);
21354 xy[0]-=10;xy[1]+=5; // << fix me
21355 this.arrowEl.setXY(xy);
21359 return this.updatePosition('right', false);
21362 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21363 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21364 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21365 //normal display... or moved up/down.
21366 this.el.setXY(offset);
21367 var xy = this.alignEl.getAnchorXY('t', false);
21368 xy[1]-=10; // << fix me
21369 this.arrowEl.setXY(xy);
21373 return this.updatePosition('bottom', false);
21376 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21377 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21378 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21379 //normal display... or moved up/down.
21380 this.el.setXY(offset);
21381 var xy = this.alignEl.getAnchorXY('b', false);
21382 xy[1]+=2; // << fix me
21383 this.arrowEl.setXY(xy);
21387 return this.updatePosition('top', false);
21398 this.el.setXY([0,0]);
21399 this.el.removeClass('in');
21401 this.hoverState = null;
21402 this.maskEl.hide(); // always..
21403 this.fireEvent('hide', this);
21409 Roo.apply(Roo.bootstrap.Popover, {
21412 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21413 'right' : ['l-br', [10,0], 'right bs-popover-right'],
21414 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21415 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21420 clickHander : false,
21424 onMouseDown : function(e)
21426 if (this.popups.length && !e.getTarget(".roo-popover")) {
21427 /// what is nothing is showing..
21436 register : function(popup)
21438 if (!Roo.bootstrap.Popover.clickHandler) {
21439 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21441 // hide other popups.
21442 popup.on('show', Roo.bootstrap.Popover.onShow, popup);
21443 popup.on('hide', Roo.bootstrap.Popover.onHide, popup);
21444 this.hideAll(); //<< why?
21445 //this.popups.push(popup);
21447 hideAll : function()
21449 this.popups.forEach(function(p) {
21453 onShow : function() {
21454 Roo.bootstrap.Popover.popups.push(this);
21456 onHide : function() {
21457 Roo.bootstrap.Popover.popups.remove(this);
21463 * Card header - holder for the card header elements.
21468 * @class Roo.bootstrap.PopoverNav
21469 * @extends Roo.bootstrap.NavGroup
21470 * Bootstrap Popover header navigation class
21472 * Create a new Popover Header Navigation
21473 * @param {Object} config The config object
21476 Roo.bootstrap.PopoverNav = function(config){
21477 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
21480 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
21483 container_method : 'getPopoverHeader'
21501 * @class Roo.bootstrap.Progress
21502 * @extends Roo.bootstrap.Component
21503 * Bootstrap Progress class
21504 * @cfg {Boolean} striped striped of the progress bar
21505 * @cfg {Boolean} active animated of the progress bar
21509 * Create a new Progress
21510 * @param {Object} config The config object
21513 Roo.bootstrap.Progress = function(config){
21514 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
21517 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
21522 getAutoCreate : function(){
21530 cfg.cls += ' progress-striped';
21534 cfg.cls += ' active';
21553 * @class Roo.bootstrap.ProgressBar
21554 * @extends Roo.bootstrap.Component
21555 * Bootstrap ProgressBar class
21556 * @cfg {Number} aria_valuenow aria-value now
21557 * @cfg {Number} aria_valuemin aria-value min
21558 * @cfg {Number} aria_valuemax aria-value max
21559 * @cfg {String} label label for the progress bar
21560 * @cfg {String} panel (success | info | warning | danger )
21561 * @cfg {String} role role of the progress bar
21562 * @cfg {String} sr_only text
21566 * Create a new ProgressBar
21567 * @param {Object} config The config object
21570 Roo.bootstrap.ProgressBar = function(config){
21571 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
21574 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
21578 aria_valuemax : 100,
21584 getAutoCreate : function()
21589 cls: 'progress-bar',
21590 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
21602 cfg.role = this.role;
21605 if(this.aria_valuenow){
21606 cfg['aria-valuenow'] = this.aria_valuenow;
21609 if(this.aria_valuemin){
21610 cfg['aria-valuemin'] = this.aria_valuemin;
21613 if(this.aria_valuemax){
21614 cfg['aria-valuemax'] = this.aria_valuemax;
21617 if(this.label && !this.sr_only){
21618 cfg.html = this.label;
21622 cfg.cls += ' progress-bar-' + this.panel;
21628 update : function(aria_valuenow)
21630 this.aria_valuenow = aria_valuenow;
21632 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
21647 * @class Roo.bootstrap.TabGroup
21648 * @extends Roo.bootstrap.Column
21649 * Bootstrap Column class
21650 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
21651 * @cfg {Boolean} carousel true to make the group behave like a carousel
21652 * @cfg {Boolean} bullets show bullets for the panels
21653 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
21654 * @cfg {Number} timer auto slide timer .. default 0 millisecond
21655 * @cfg {Boolean} showarrow (true|false) show arrow default true
21658 * Create a new TabGroup
21659 * @param {Object} config The config object
21662 Roo.bootstrap.TabGroup = function(config){
21663 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
21665 this.navId = Roo.id();
21668 Roo.bootstrap.TabGroup.register(this);
21672 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
21675 transition : false,
21680 slideOnTouch : false,
21683 getAutoCreate : function()
21685 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
21687 cfg.cls += ' tab-content';
21689 if (this.carousel) {
21690 cfg.cls += ' carousel slide';
21693 cls : 'carousel-inner',
21697 if(this.bullets && !Roo.isTouch){
21700 cls : 'carousel-bullets',
21704 if(this.bullets_cls){
21705 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
21712 cfg.cn[0].cn.push(bullets);
21715 if(this.showarrow){
21716 cfg.cn[0].cn.push({
21718 class : 'carousel-arrow',
21722 class : 'carousel-prev',
21726 class : 'fa fa-chevron-left'
21732 class : 'carousel-next',
21736 class : 'fa fa-chevron-right'
21749 initEvents: function()
21751 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
21752 // this.el.on("touchstart", this.onTouchStart, this);
21755 if(this.autoslide){
21758 this.slideFn = window.setInterval(function() {
21759 _this.showPanelNext();
21763 if(this.showarrow){
21764 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
21765 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
21771 // onTouchStart : function(e, el, o)
21773 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
21777 // this.showPanelNext();
21781 getChildContainer : function()
21783 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
21787 * register a Navigation item
21788 * @param {Roo.bootstrap.NavItem} the navitem to add
21790 register : function(item)
21792 this.tabs.push( item);
21793 item.navId = this.navId; // not really needed..
21798 getActivePanel : function()
21801 Roo.each(this.tabs, function(t) {
21811 getPanelByName : function(n)
21814 Roo.each(this.tabs, function(t) {
21815 if (t.tabId == n) {
21823 indexOfPanel : function(p)
21826 Roo.each(this.tabs, function(t,i) {
21827 if (t.tabId == p.tabId) {
21836 * show a specific panel
21837 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
21838 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
21840 showPanel : function (pan)
21842 if(this.transition || typeof(pan) == 'undefined'){
21843 Roo.log("waiting for the transitionend");
21847 if (typeof(pan) == 'number') {
21848 pan = this.tabs[pan];
21851 if (typeof(pan) == 'string') {
21852 pan = this.getPanelByName(pan);
21855 var cur = this.getActivePanel();
21858 Roo.log('pan or acitve pan is undefined');
21862 if (pan.tabId == this.getActivePanel().tabId) {
21866 if (false === cur.fireEvent('beforedeactivate')) {
21870 if(this.bullets > 0 && !Roo.isTouch){
21871 this.setActiveBullet(this.indexOfPanel(pan));
21874 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
21876 //class="carousel-item carousel-item-next carousel-item-left"
21878 this.transition = true;
21879 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
21880 var lr = dir == 'next' ? 'left' : 'right';
21881 pan.el.addClass(dir); // or prev
21882 pan.el.addClass('carousel-item-' + dir); // or prev
21883 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
21884 cur.el.addClass(lr); // or right
21885 pan.el.addClass(lr);
21886 cur.el.addClass('carousel-item-' +lr); // or right
21887 pan.el.addClass('carousel-item-' +lr);
21891 cur.el.on('transitionend', function() {
21892 Roo.log("trans end?");
21894 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
21895 pan.setActive(true);
21897 cur.el.removeClass([lr, 'carousel-item-' + lr]);
21898 cur.setActive(false);
21900 _this.transition = false;
21902 }, this, { single: true } );
21907 cur.setActive(false);
21908 pan.setActive(true);
21913 showPanelNext : function()
21915 var i = this.indexOfPanel(this.getActivePanel());
21917 if (i >= this.tabs.length - 1 && !this.autoslide) {
21921 if (i >= this.tabs.length - 1 && this.autoslide) {
21925 this.showPanel(this.tabs[i+1]);
21928 showPanelPrev : function()
21930 var i = this.indexOfPanel(this.getActivePanel());
21932 if (i < 1 && !this.autoslide) {
21936 if (i < 1 && this.autoslide) {
21937 i = this.tabs.length;
21940 this.showPanel(this.tabs[i-1]);
21944 addBullet: function()
21946 if(!this.bullets || Roo.isTouch){
21949 var ctr = this.el.select('.carousel-bullets',true).first();
21950 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
21951 var bullet = ctr.createChild({
21952 cls : 'bullet bullet-' + i
21953 },ctr.dom.lastChild);
21958 bullet.on('click', (function(e, el, o, ii, t){
21960 e.preventDefault();
21962 this.showPanel(ii);
21964 if(this.autoslide && this.slideFn){
21965 clearInterval(this.slideFn);
21966 this.slideFn = window.setInterval(function() {
21967 _this.showPanelNext();
21971 }).createDelegate(this, [i, bullet], true));
21976 setActiveBullet : function(i)
21982 Roo.each(this.el.select('.bullet', true).elements, function(el){
21983 el.removeClass('selected');
21986 var bullet = this.el.select('.bullet-' + i, true).first();
21992 bullet.addClass('selected');
22003 Roo.apply(Roo.bootstrap.TabGroup, {
22007 * register a Navigation Group
22008 * @param {Roo.bootstrap.NavGroup} the navgroup to add
22010 register : function(navgrp)
22012 this.groups[navgrp.navId] = navgrp;
22016 * fetch a Navigation Group based on the navigation ID
22017 * if one does not exist , it will get created.
22018 * @param {string} the navgroup to add
22019 * @returns {Roo.bootstrap.NavGroup} the navgroup
22021 get: function(navId) {
22022 if (typeof(this.groups[navId]) == 'undefined') {
22023 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22025 return this.groups[navId] ;
22040 * @class Roo.bootstrap.TabPanel
22041 * @extends Roo.bootstrap.Component
22042 * Bootstrap TabPanel class
22043 * @cfg {Boolean} active panel active
22044 * @cfg {String} html panel content
22045 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22046 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
22047 * @cfg {String} href click to link..
22048 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22052 * Create a new TabPanel
22053 * @param {Object} config The config object
22056 Roo.bootstrap.TabPanel = function(config){
22057 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22061 * Fires when the active status changes
22062 * @param {Roo.bootstrap.TabPanel} this
22063 * @param {Boolean} state the new state
22068 * @event beforedeactivate
22069 * Fires before a tab is de-activated - can be used to do validation on a form.
22070 * @param {Roo.bootstrap.TabPanel} this
22071 * @return {Boolean} false if there is an error
22074 'beforedeactivate': true
22077 this.tabId = this.tabId || Roo.id();
22081 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
22088 touchSlide : false,
22089 getAutoCreate : function(){
22094 // item is needed for carousel - not sure if it has any effect otherwise
22095 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22096 html: this.html || ''
22100 cfg.cls += ' active';
22104 cfg.tabId = this.tabId;
22112 initEvents: function()
22114 var p = this.parent();
22116 this.navId = this.navId || p.navId;
22118 if (typeof(this.navId) != 'undefined') {
22119 // not really needed.. but just in case.. parent should be a NavGroup.
22120 var tg = Roo.bootstrap.TabGroup.get(this.navId);
22124 var i = tg.tabs.length - 1;
22126 if(this.active && tg.bullets > 0 && i < tg.bullets){
22127 tg.setActiveBullet(i);
22131 this.el.on('click', this.onClick, this);
22133 if(Roo.isTouch && this.touchSlide){
22134 this.el.on("touchstart", this.onTouchStart, this);
22135 this.el.on("touchmove", this.onTouchMove, this);
22136 this.el.on("touchend", this.onTouchEnd, this);
22141 onRender : function(ct, position)
22143 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22146 setActive : function(state)
22148 Roo.log("panel - set active " + this.tabId + "=" + state);
22150 this.active = state;
22152 this.el.removeClass('active');
22154 } else if (!this.el.hasClass('active')) {
22155 this.el.addClass('active');
22158 this.fireEvent('changed', this, state);
22161 onClick : function(e)
22163 e.preventDefault();
22165 if(!this.href.length){
22169 window.location.href = this.href;
22178 onTouchStart : function(e)
22180 this.swiping = false;
22182 this.startX = e.browserEvent.touches[0].clientX;
22183 this.startY = e.browserEvent.touches[0].clientY;
22186 onTouchMove : function(e)
22188 this.swiping = true;
22190 this.endX = e.browserEvent.touches[0].clientX;
22191 this.endY = e.browserEvent.touches[0].clientY;
22194 onTouchEnd : function(e)
22201 var tabGroup = this.parent();
22203 if(this.endX > this.startX){ // swiping right
22204 tabGroup.showPanelPrev();
22208 if(this.startX > this.endX){ // swiping left
22209 tabGroup.showPanelNext();
22228 * @class Roo.bootstrap.DateField
22229 * @extends Roo.bootstrap.Input
22230 * Bootstrap DateField class
22231 * @cfg {Number} weekStart default 0
22232 * @cfg {String} viewMode default empty, (months|years)
22233 * @cfg {String} minViewMode default empty, (months|years)
22234 * @cfg {Number} startDate default -Infinity
22235 * @cfg {Number} endDate default Infinity
22236 * @cfg {Boolean} todayHighlight default false
22237 * @cfg {Boolean} todayBtn default false
22238 * @cfg {Boolean} calendarWeeks default false
22239 * @cfg {Object} daysOfWeekDisabled default empty
22240 * @cfg {Boolean} singleMode default false (true | false)
22242 * @cfg {Boolean} keyboardNavigation default true
22243 * @cfg {String} language default en
22246 * Create a new DateField
22247 * @param {Object} config The config object
22250 Roo.bootstrap.DateField = function(config){
22251 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
22255 * Fires when this field show.
22256 * @param {Roo.bootstrap.DateField} this
22257 * @param {Mixed} date The date value
22262 * Fires when this field hide.
22263 * @param {Roo.bootstrap.DateField} this
22264 * @param {Mixed} date The date value
22269 * Fires when select a date.
22270 * @param {Roo.bootstrap.DateField} this
22271 * @param {Mixed} date The date value
22275 * @event beforeselect
22276 * Fires when before select a date.
22277 * @param {Roo.bootstrap.DateField} this
22278 * @param {Mixed} date The date value
22280 beforeselect : true
22284 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
22287 * @cfg {String} format
22288 * The default date format string which can be overriden for localization support. The format must be
22289 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22293 * @cfg {String} altFormats
22294 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22295 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22297 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22305 todayHighlight : false,
22311 keyboardNavigation: true,
22313 calendarWeeks: false,
22315 startDate: -Infinity,
22319 daysOfWeekDisabled: [],
22323 singleMode : false,
22325 UTCDate: function()
22327 return new Date(Date.UTC.apply(Date, arguments));
22330 UTCToday: function()
22332 var today = new Date();
22333 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22336 getDate: function() {
22337 var d = this.getUTCDate();
22338 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22341 getUTCDate: function() {
22345 setDate: function(d) {
22346 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22349 setUTCDate: function(d) {
22351 this.setValue(this.formatDate(this.date));
22354 onRender: function(ct, position)
22357 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
22359 this.language = this.language || 'en';
22360 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
22361 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
22363 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
22364 this.format = this.format || 'm/d/y';
22365 this.isInline = false;
22366 this.isInput = true;
22367 this.component = this.el.select('.add-on', true).first() || false;
22368 this.component = (this.component && this.component.length === 0) ? false : this.component;
22369 this.hasInput = this.component && this.inputEl().length;
22371 if (typeof(this.minViewMode === 'string')) {
22372 switch (this.minViewMode) {
22374 this.minViewMode = 1;
22377 this.minViewMode = 2;
22380 this.minViewMode = 0;
22385 if (typeof(this.viewMode === 'string')) {
22386 switch (this.viewMode) {
22399 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
22401 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
22403 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22405 this.picker().on('mousedown', this.onMousedown, this);
22406 this.picker().on('click', this.onClick, this);
22408 this.picker().addClass('datepicker-dropdown');
22410 this.startViewMode = this.viewMode;
22412 if(this.singleMode){
22413 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22414 v.setVisibilityMode(Roo.Element.DISPLAY);
22418 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22419 v.setStyle('width', '189px');
22423 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22424 if(!this.calendarWeeks){
22429 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22430 v.attr('colspan', function(i, val){
22431 return parseInt(val) + 1;
22436 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22438 this.setStartDate(this.startDate);
22439 this.setEndDate(this.endDate);
22441 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22448 if(this.isInline) {
22453 picker : function()
22455 return this.pickerEl;
22456 // return this.el.select('.datepicker', true).first();
22459 fillDow: function()
22461 var dowCnt = this.weekStart;
22470 if(this.calendarWeeks){
22478 while (dowCnt < this.weekStart + 7) {
22482 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
22486 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
22489 fillMonths: function()
22492 var months = this.picker().select('>.datepicker-months td', true).first();
22494 months.dom.innerHTML = '';
22500 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
22503 months.createChild(month);
22510 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;
22512 if (this.date < this.startDate) {
22513 this.viewDate = new Date(this.startDate);
22514 } else if (this.date > this.endDate) {
22515 this.viewDate = new Date(this.endDate);
22517 this.viewDate = new Date(this.date);
22525 var d = new Date(this.viewDate),
22526 year = d.getUTCFullYear(),
22527 month = d.getUTCMonth(),
22528 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
22529 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
22530 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
22531 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
22532 currentDate = this.date && this.date.valueOf(),
22533 today = this.UTCToday();
22535 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
22537 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22539 // this.picker.select('>tfoot th.today').
22540 // .text(dates[this.language].today)
22541 // .toggle(this.todayBtn !== false);
22543 this.updateNavArrows();
22546 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
22548 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
22550 prevMonth.setUTCDate(day);
22552 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
22554 var nextMonth = new Date(prevMonth);
22556 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
22558 nextMonth = nextMonth.valueOf();
22560 var fillMonths = false;
22562 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
22564 while(prevMonth.valueOf() <= nextMonth) {
22567 if (prevMonth.getUTCDay() === this.weekStart) {
22569 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
22577 if(this.calendarWeeks){
22578 // ISO 8601: First week contains first thursday.
22579 // ISO also states week starts on Monday, but we can be more abstract here.
22581 // Start of current week: based on weekstart/current date
22582 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
22583 // Thursday of this week
22584 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
22585 // First Thursday of year, year from thursday
22586 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
22587 // Calendar week: ms between thursdays, div ms per day, div 7 days
22588 calWeek = (th - yth) / 864e5 / 7 + 1;
22590 fillMonths.cn.push({
22598 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
22600 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
22603 if (this.todayHighlight &&
22604 prevMonth.getUTCFullYear() == today.getFullYear() &&
22605 prevMonth.getUTCMonth() == today.getMonth() &&
22606 prevMonth.getUTCDate() == today.getDate()) {
22607 clsName += ' today';
22610 if (currentDate && prevMonth.valueOf() === currentDate) {
22611 clsName += ' active';
22614 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
22615 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
22616 clsName += ' disabled';
22619 fillMonths.cn.push({
22621 cls: 'day ' + clsName,
22622 html: prevMonth.getDate()
22625 prevMonth.setDate(prevMonth.getDate()+1);
22628 var currentYear = this.date && this.date.getUTCFullYear();
22629 var currentMonth = this.date && this.date.getUTCMonth();
22631 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
22633 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
22634 v.removeClass('active');
22636 if(currentYear === year && k === currentMonth){
22637 v.addClass('active');
22640 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
22641 v.addClass('disabled');
22647 year = parseInt(year/10, 10) * 10;
22649 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
22651 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
22654 for (var i = -1; i < 11; i++) {
22655 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
22657 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
22665 showMode: function(dir)
22668 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
22671 Roo.each(this.picker().select('>div',true).elements, function(v){
22672 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22675 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
22680 if(this.isInline) {
22684 this.picker().removeClass(['bottom', 'top']);
22686 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22688 * place to the top of element!
22692 this.picker().addClass('top');
22693 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22698 this.picker().addClass('bottom');
22700 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22703 parseDate : function(value)
22705 if(!value || value instanceof Date){
22708 var v = Date.parseDate(value, this.format);
22709 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
22710 v = Date.parseDate(value, 'Y-m-d');
22712 if(!v && this.altFormats){
22713 if(!this.altFormatsArray){
22714 this.altFormatsArray = this.altFormats.split("|");
22716 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22717 v = Date.parseDate(value, this.altFormatsArray[i]);
22723 formatDate : function(date, fmt)
22725 return (!date || !(date instanceof Date)) ?
22726 date : date.dateFormat(fmt || this.format);
22729 onFocus : function()
22731 Roo.bootstrap.DateField.superclass.onFocus.call(this);
22735 onBlur : function()
22737 Roo.bootstrap.DateField.superclass.onBlur.call(this);
22739 var d = this.inputEl().getValue();
22746 showPopup : function()
22748 this.picker().show();
22752 this.fireEvent('showpopup', this, this.date);
22755 hidePopup : function()
22757 if(this.isInline) {
22760 this.picker().hide();
22761 this.viewMode = this.startViewMode;
22764 this.fireEvent('hidepopup', this, this.date);
22768 onMousedown: function(e)
22770 e.stopPropagation();
22771 e.preventDefault();
22776 Roo.bootstrap.DateField.superclass.keyup.call(this);
22780 setValue: function(v)
22782 if(this.fireEvent('beforeselect', this, v) !== false){
22783 var d = new Date(this.parseDate(v) ).clearTime();
22785 if(isNaN(d.getTime())){
22786 this.date = this.viewDate = '';
22787 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22791 v = this.formatDate(d);
22793 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
22795 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
22799 this.fireEvent('select', this, this.date);
22803 getValue: function()
22805 return this.formatDate(this.date);
22808 fireKey: function(e)
22810 if (!this.picker().isVisible()){
22811 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22817 var dateChanged = false,
22819 newDate, newViewDate;
22824 e.preventDefault();
22828 if (!this.keyboardNavigation) {
22831 dir = e.keyCode == 37 ? -1 : 1;
22834 newDate = this.moveYear(this.date, dir);
22835 newViewDate = this.moveYear(this.viewDate, dir);
22836 } else if (e.shiftKey){
22837 newDate = this.moveMonth(this.date, dir);
22838 newViewDate = this.moveMonth(this.viewDate, dir);
22840 newDate = new Date(this.date);
22841 newDate.setUTCDate(this.date.getUTCDate() + dir);
22842 newViewDate = new Date(this.viewDate);
22843 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
22845 if (this.dateWithinRange(newDate)){
22846 this.date = newDate;
22847 this.viewDate = newViewDate;
22848 this.setValue(this.formatDate(this.date));
22850 e.preventDefault();
22851 dateChanged = true;
22856 if (!this.keyboardNavigation) {
22859 dir = e.keyCode == 38 ? -1 : 1;
22861 newDate = this.moveYear(this.date, dir);
22862 newViewDate = this.moveYear(this.viewDate, dir);
22863 } else if (e.shiftKey){
22864 newDate = this.moveMonth(this.date, dir);
22865 newViewDate = this.moveMonth(this.viewDate, dir);
22867 newDate = new Date(this.date);
22868 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
22869 newViewDate = new Date(this.viewDate);
22870 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
22872 if (this.dateWithinRange(newDate)){
22873 this.date = newDate;
22874 this.viewDate = newViewDate;
22875 this.setValue(this.formatDate(this.date));
22877 e.preventDefault();
22878 dateChanged = true;
22882 this.setValue(this.formatDate(this.date));
22884 e.preventDefault();
22887 this.setValue(this.formatDate(this.date));
22901 onClick: function(e)
22903 e.stopPropagation();
22904 e.preventDefault();
22906 var target = e.getTarget();
22908 if(target.nodeName.toLowerCase() === 'i'){
22909 target = Roo.get(target).dom.parentNode;
22912 var nodeName = target.nodeName;
22913 var className = target.className;
22914 var html = target.innerHTML;
22915 //Roo.log(nodeName);
22917 switch(nodeName.toLowerCase()) {
22919 switch(className) {
22925 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
22926 switch(this.viewMode){
22928 this.viewDate = this.moveMonth(this.viewDate, dir);
22932 this.viewDate = this.moveYear(this.viewDate, dir);
22938 var date = new Date();
22939 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
22941 this.setValue(this.formatDate(this.date));
22948 if (className.indexOf('disabled') < 0) {
22949 if (!this.viewDate) {
22950 this.viewDate = new Date();
22952 this.viewDate.setUTCDate(1);
22953 if (className.indexOf('month') > -1) {
22954 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
22956 var year = parseInt(html, 10) || 0;
22957 this.viewDate.setUTCFullYear(year);
22961 if(this.singleMode){
22962 this.setValue(this.formatDate(this.viewDate));
22973 //Roo.log(className);
22974 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
22975 var day = parseInt(html, 10) || 1;
22976 var year = (this.viewDate || new Date()).getUTCFullYear(),
22977 month = (this.viewDate || new Date()).getUTCMonth();
22979 if (className.indexOf('old') > -1) {
22986 } else if (className.indexOf('new') > -1) {
22994 //Roo.log([year,month,day]);
22995 this.date = this.UTCDate(year, month, day,0,0,0,0);
22996 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
22998 //Roo.log(this.formatDate(this.date));
22999 this.setValue(this.formatDate(this.date));
23006 setStartDate: function(startDate)
23008 this.startDate = startDate || -Infinity;
23009 if (this.startDate !== -Infinity) {
23010 this.startDate = this.parseDate(this.startDate);
23013 this.updateNavArrows();
23016 setEndDate: function(endDate)
23018 this.endDate = endDate || Infinity;
23019 if (this.endDate !== Infinity) {
23020 this.endDate = this.parseDate(this.endDate);
23023 this.updateNavArrows();
23026 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23028 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23029 if (typeof(this.daysOfWeekDisabled) !== 'object') {
23030 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23032 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23033 return parseInt(d, 10);
23036 this.updateNavArrows();
23039 updateNavArrows: function()
23041 if(this.singleMode){
23045 var d = new Date(this.viewDate),
23046 year = d.getUTCFullYear(),
23047 month = d.getUTCMonth();
23049 Roo.each(this.picker().select('.prev', true).elements, function(v){
23051 switch (this.viewMode) {
23054 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23060 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23067 Roo.each(this.picker().select('.next', true).elements, function(v){
23069 switch (this.viewMode) {
23072 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23078 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23086 moveMonth: function(date, dir)
23091 var new_date = new Date(date.valueOf()),
23092 day = new_date.getUTCDate(),
23093 month = new_date.getUTCMonth(),
23094 mag = Math.abs(dir),
23096 dir = dir > 0 ? 1 : -1;
23099 // If going back one month, make sure month is not current month
23100 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23102 return new_date.getUTCMonth() == month;
23104 // If going forward one month, make sure month is as expected
23105 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23107 return new_date.getUTCMonth() != new_month;
23109 new_month = month + dir;
23110 new_date.setUTCMonth(new_month);
23111 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23112 if (new_month < 0 || new_month > 11) {
23113 new_month = (new_month + 12) % 12;
23116 // For magnitudes >1, move one month at a time...
23117 for (var i=0; i<mag; i++) {
23118 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23119 new_date = this.moveMonth(new_date, dir);
23121 // ...then reset the day, keeping it in the new month
23122 new_month = new_date.getUTCMonth();
23123 new_date.setUTCDate(day);
23125 return new_month != new_date.getUTCMonth();
23128 // Common date-resetting loop -- if date is beyond end of month, make it
23131 new_date.setUTCDate(--day);
23132 new_date.setUTCMonth(new_month);
23137 moveYear: function(date, dir)
23139 return this.moveMonth(date, dir*12);
23142 dateWithinRange: function(date)
23144 return date >= this.startDate && date <= this.endDate;
23150 this.picker().remove();
23153 validateValue : function(value)
23155 if(this.getVisibilityEl().hasClass('hidden')){
23159 if(value.length < 1) {
23160 if(this.allowBlank){
23166 if(value.length < this.minLength){
23169 if(value.length > this.maxLength){
23173 var vt = Roo.form.VTypes;
23174 if(!vt[this.vtype](value, this)){
23178 if(typeof this.validator == "function"){
23179 var msg = this.validator(value);
23185 if(this.regex && !this.regex.test(value)){
23189 if(typeof(this.parseDate(value)) == 'undefined'){
23193 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23197 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23207 this.date = this.viewDate = '';
23209 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
23214 Roo.apply(Roo.bootstrap.DateField, {
23225 html: '<i class="fa fa-arrow-left"/>'
23235 html: '<i class="fa fa-arrow-right"/>'
23277 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23278 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23279 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23280 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23281 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23294 navFnc: 'FullYear',
23299 navFnc: 'FullYear',
23304 Roo.apply(Roo.bootstrap.DateField, {
23308 cls: 'datepicker dropdown-menu roo-dynamic shadow',
23312 cls: 'datepicker-days',
23316 cls: 'table-condensed',
23318 Roo.bootstrap.DateField.head,
23322 Roo.bootstrap.DateField.footer
23329 cls: 'datepicker-months',
23333 cls: 'table-condensed',
23335 Roo.bootstrap.DateField.head,
23336 Roo.bootstrap.DateField.content,
23337 Roo.bootstrap.DateField.footer
23344 cls: 'datepicker-years',
23348 cls: 'table-condensed',
23350 Roo.bootstrap.DateField.head,
23351 Roo.bootstrap.DateField.content,
23352 Roo.bootstrap.DateField.footer
23371 * @class Roo.bootstrap.TimeField
23372 * @extends Roo.bootstrap.Input
23373 * Bootstrap DateField class
23377 * Create a new TimeField
23378 * @param {Object} config The config object
23381 Roo.bootstrap.TimeField = function(config){
23382 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
23386 * Fires when this field show.
23387 * @param {Roo.bootstrap.DateField} thisthis
23388 * @param {Mixed} date The date value
23393 * Fires when this field hide.
23394 * @param {Roo.bootstrap.DateField} this
23395 * @param {Mixed} date The date value
23400 * Fires when select a date.
23401 * @param {Roo.bootstrap.DateField} this
23402 * @param {Mixed} date The date value
23408 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
23411 * @cfg {String} format
23412 * The default time format string which can be overriden for localization support. The format must be
23413 * valid according to {@link Date#parseDate} (defaults to 'H:i').
23417 getAutoCreate : function()
23419 this.after = '<i class="fa far fa-clock"></i>';
23420 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
23424 onRender: function(ct, position)
23427 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
23429 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
23431 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23433 this.pop = this.picker().select('>.datepicker-time',true).first();
23434 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23436 this.picker().on('mousedown', this.onMousedown, this);
23437 this.picker().on('click', this.onClick, this);
23439 this.picker().addClass('datepicker-dropdown');
23444 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23445 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23446 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23447 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23448 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23449 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23453 fireKey: function(e){
23454 if (!this.picker().isVisible()){
23455 if (e.keyCode == 27) { // allow escape to hide and re-show picker
23461 e.preventDefault();
23469 this.onTogglePeriod();
23472 this.onIncrementMinutes();
23475 this.onDecrementMinutes();
23484 onClick: function(e) {
23485 e.stopPropagation();
23486 e.preventDefault();
23489 picker : function()
23491 return this.pickerEl;
23494 fillTime: function()
23496 var time = this.pop.select('tbody', true).first();
23498 time.dom.innerHTML = '';
23513 cls: 'hours-up fa fas fa-chevron-up'
23533 cls: 'minutes-up fa fas fa-chevron-up'
23554 cls: 'timepicker-hour',
23569 cls: 'timepicker-minute',
23584 cls: 'btn btn-primary period',
23606 cls: 'hours-down fa fas fa-chevron-down'
23626 cls: 'minutes-down fa fas fa-chevron-down'
23644 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
23651 var hours = this.time.getHours();
23652 var minutes = this.time.getMinutes();
23665 hours = hours - 12;
23669 hours = '0' + hours;
23673 minutes = '0' + minutes;
23676 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
23677 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
23678 this.pop.select('button', true).first().dom.innerHTML = period;
23684 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
23686 var cls = ['bottom'];
23688 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
23695 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
23699 //this.picker().setXY(20000,20000);
23700 this.picker().addClass(cls.join('-'));
23704 Roo.each(cls, function(c){
23709 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
23710 //_this.picker().setTop(_this.inputEl().getHeight());
23714 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
23716 //_this.picker().setTop(0 - _this.picker().getHeight());
23721 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
23725 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
23733 onFocus : function()
23735 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
23739 onBlur : function()
23741 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
23747 this.picker().show();
23752 this.fireEvent('show', this, this.date);
23757 this.picker().hide();
23760 this.fireEvent('hide', this, this.date);
23763 setTime : function()
23766 this.setValue(this.time.format(this.format));
23768 this.fireEvent('select', this, this.date);
23773 onMousedown: function(e){
23774 e.stopPropagation();
23775 e.preventDefault();
23778 onIncrementHours: function()
23780 Roo.log('onIncrementHours');
23781 this.time = this.time.add(Date.HOUR, 1);
23786 onDecrementHours: function()
23788 Roo.log('onDecrementHours');
23789 this.time = this.time.add(Date.HOUR, -1);
23793 onIncrementMinutes: function()
23795 Roo.log('onIncrementMinutes');
23796 this.time = this.time.add(Date.MINUTE, 1);
23800 onDecrementMinutes: function()
23802 Roo.log('onDecrementMinutes');
23803 this.time = this.time.add(Date.MINUTE, -1);
23807 onTogglePeriod: function()
23809 Roo.log('onTogglePeriod');
23810 this.time = this.time.add(Date.HOUR, 12);
23818 Roo.apply(Roo.bootstrap.TimeField, {
23822 cls: 'datepicker dropdown-menu',
23826 cls: 'datepicker-time',
23830 cls: 'table-condensed',
23859 cls: 'btn btn-info ok',
23887 * @class Roo.bootstrap.MonthField
23888 * @extends Roo.bootstrap.Input
23889 * Bootstrap MonthField class
23891 * @cfg {String} language default en
23894 * Create a new MonthField
23895 * @param {Object} config The config object
23898 Roo.bootstrap.MonthField = function(config){
23899 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
23904 * Fires when this field show.
23905 * @param {Roo.bootstrap.MonthField} this
23906 * @param {Mixed} date The date value
23911 * Fires when this field hide.
23912 * @param {Roo.bootstrap.MonthField} this
23913 * @param {Mixed} date The date value
23918 * Fires when select a date.
23919 * @param {Roo.bootstrap.MonthField} this
23920 * @param {String} oldvalue The old value
23921 * @param {String} newvalue The new value
23927 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
23929 onRender: function(ct, position)
23932 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
23934 this.language = this.language || 'en';
23935 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
23936 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
23938 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
23939 this.isInline = false;
23940 this.isInput = true;
23941 this.component = this.el.select('.add-on', true).first() || false;
23942 this.component = (this.component && this.component.length === 0) ? false : this.component;
23943 this.hasInput = this.component && this.inputEL().length;
23945 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
23947 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23949 this.picker().on('mousedown', this.onMousedown, this);
23950 this.picker().on('click', this.onClick, this);
23952 this.picker().addClass('datepicker-dropdown');
23954 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
23955 v.setStyle('width', '189px');
23962 if(this.isInline) {
23968 setValue: function(v, suppressEvent)
23970 var o = this.getValue();
23972 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
23976 if(suppressEvent !== true){
23977 this.fireEvent('select', this, o, v);
23982 getValue: function()
23987 onClick: function(e)
23989 e.stopPropagation();
23990 e.preventDefault();
23992 var target = e.getTarget();
23994 if(target.nodeName.toLowerCase() === 'i'){
23995 target = Roo.get(target).dom.parentNode;
23998 var nodeName = target.nodeName;
23999 var className = target.className;
24000 var html = target.innerHTML;
24002 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24006 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
24008 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24014 picker : function()
24016 return this.pickerEl;
24019 fillMonths: function()
24022 var months = this.picker().select('>.datepicker-months td', true).first();
24024 months.dom.innerHTML = '';
24030 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
24033 months.createChild(month);
24042 if(typeof(this.vIndex) == 'undefined' && this.value.length){
24043 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
24046 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24047 e.removeClass('active');
24049 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24050 e.addClass('active');
24057 if(this.isInline) {
24061 this.picker().removeClass(['bottom', 'top']);
24063 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24065 * place to the top of element!
24069 this.picker().addClass('top');
24070 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24075 this.picker().addClass('bottom');
24077 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24080 onFocus : function()
24082 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
24086 onBlur : function()
24088 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
24090 var d = this.inputEl().getValue();
24099 this.picker().show();
24100 this.picker().select('>.datepicker-months', true).first().show();
24104 this.fireEvent('show', this, this.date);
24109 if(this.isInline) {
24112 this.picker().hide();
24113 this.fireEvent('hide', this, this.date);
24117 onMousedown: function(e)
24119 e.stopPropagation();
24120 e.preventDefault();
24125 Roo.bootstrap.MonthField.superclass.keyup.call(this);
24129 fireKey: function(e)
24131 if (!this.picker().isVisible()){
24132 if (e.keyCode == 27) {// allow escape to hide and re-show picker
24143 e.preventDefault();
24147 dir = e.keyCode == 37 ? -1 : 1;
24149 this.vIndex = this.vIndex + dir;
24151 if(this.vIndex < 0){
24155 if(this.vIndex > 11){
24159 if(isNaN(this.vIndex)){
24163 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24169 dir = e.keyCode == 38 ? -1 : 1;
24171 this.vIndex = this.vIndex + dir * 4;
24173 if(this.vIndex < 0){
24177 if(this.vIndex > 11){
24181 if(isNaN(this.vIndex)){
24185 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24190 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24191 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24195 e.preventDefault();
24198 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24199 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24215 this.picker().remove();
24220 Roo.apply(Roo.bootstrap.MonthField, {
24239 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24240 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24245 Roo.apply(Roo.bootstrap.MonthField, {
24249 cls: 'datepicker dropdown-menu roo-dynamic',
24253 cls: 'datepicker-months',
24257 cls: 'table-condensed',
24259 Roo.bootstrap.DateField.content
24279 * @class Roo.bootstrap.CheckBox
24280 * @extends Roo.bootstrap.Input
24281 * Bootstrap CheckBox class
24283 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24284 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24285 * @cfg {String} boxLabel The text that appears beside the checkbox
24286 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24287 * @cfg {Boolean} checked initnal the element
24288 * @cfg {Boolean} inline inline the element (default false)
24289 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24290 * @cfg {String} tooltip label tooltip
24293 * Create a new CheckBox
24294 * @param {Object} config The config object
24297 Roo.bootstrap.CheckBox = function(config){
24298 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
24303 * Fires when the element is checked or unchecked.
24304 * @param {Roo.bootstrap.CheckBox} this This input
24305 * @param {Boolean} checked The new checked value
24310 * Fires when the element is click.
24311 * @param {Roo.bootstrap.CheckBox} this This input
24318 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
24320 inputType: 'checkbox',
24329 // checkbox success does not make any sense really..
24334 getAutoCreate : function()
24336 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24342 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24345 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
24351 type : this.inputType,
24352 value : this.inputValue,
24353 cls : 'roo-' + this.inputType, //'form-box',
24354 placeholder : this.placeholder || ''
24358 if(this.inputType != 'radio'){
24362 cls : 'roo-hidden-value',
24363 value : this.checked ? this.inputValue : this.valueOff
24368 if (this.weight) { // Validity check?
24369 cfg.cls += " " + this.inputType + "-" + this.weight;
24372 if (this.disabled) {
24373 input.disabled=true;
24377 input.checked = this.checked;
24382 input.name = this.name;
24384 if(this.inputType != 'radio'){
24385 hidden.name = this.name;
24386 input.name = '_hidden_' + this.name;
24391 input.cls += ' input-' + this.size;
24396 ['xs','sm','md','lg'].map(function(size){
24397 if (settings[size]) {
24398 cfg.cls += ' col-' + size + '-' + settings[size];
24402 var inputblock = input;
24404 if (this.before || this.after) {
24407 cls : 'input-group',
24412 inputblock.cn.push({
24414 cls : 'input-group-addon',
24419 inputblock.cn.push(input);
24421 if(this.inputType != 'radio'){
24422 inputblock.cn.push(hidden);
24426 inputblock.cn.push({
24428 cls : 'input-group-addon',
24434 var boxLabelCfg = false;
24440 //'for': id, // box label is handled by onclick - so no for...
24442 html: this.boxLabel
24445 boxLabelCfg.tooltip = this.tooltip;
24451 if (align ==='left' && this.fieldLabel.length) {
24452 // Roo.log("left and has label");
24457 cls : 'control-label',
24458 html : this.fieldLabel
24469 cfg.cn[1].cn.push(boxLabelCfg);
24472 if(this.labelWidth > 12){
24473 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
24476 if(this.labelWidth < 13 && this.labelmd == 0){
24477 this.labelmd = this.labelWidth;
24480 if(this.labellg > 0){
24481 cfg.cn[0].cls += ' col-lg-' + this.labellg;
24482 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
24485 if(this.labelmd > 0){
24486 cfg.cn[0].cls += ' col-md-' + this.labelmd;
24487 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
24490 if(this.labelsm > 0){
24491 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
24492 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
24495 if(this.labelxs > 0){
24496 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
24497 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
24500 } else if ( this.fieldLabel.length) {
24501 // Roo.log(" label");
24505 tag: this.boxLabel ? 'span' : 'label',
24507 cls: 'control-label box-input-label',
24508 //cls : 'input-group-addon',
24509 html : this.fieldLabel
24516 cfg.cn.push(boxLabelCfg);
24521 // Roo.log(" no label && no align");
24522 cfg.cn = [ inputblock ] ;
24524 cfg.cn.push(boxLabelCfg);
24532 if(this.inputType != 'radio'){
24533 cfg.cn.push(hidden);
24541 * return the real input element.
24543 inputEl: function ()
24545 return this.el.select('input.roo-' + this.inputType,true).first();
24547 hiddenEl: function ()
24549 return this.el.select('input.roo-hidden-value',true).first();
24552 labelEl: function()
24554 return this.el.select('label.control-label',true).first();
24556 /* depricated... */
24560 return this.labelEl();
24563 boxLabelEl: function()
24565 return this.el.select('label.box-label',true).first();
24568 initEvents : function()
24570 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
24572 this.inputEl().on('click', this.onClick, this);
24574 if (this.boxLabel) {
24575 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
24578 this.startValue = this.getValue();
24581 Roo.bootstrap.CheckBox.register(this);
24585 onClick : function(e)
24587 if(this.fireEvent('click', this, e) !== false){
24588 this.setChecked(!this.checked);
24593 setChecked : function(state,suppressEvent)
24595 this.startValue = this.getValue();
24597 if(this.inputType == 'radio'){
24599 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24600 e.dom.checked = false;
24603 this.inputEl().dom.checked = true;
24605 this.inputEl().dom.value = this.inputValue;
24607 if(suppressEvent !== true){
24608 this.fireEvent('check', this, true);
24616 this.checked = state;
24618 this.inputEl().dom.checked = state;
24621 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
24623 if(suppressEvent !== true){
24624 this.fireEvent('check', this, state);
24630 getValue : function()
24632 if(this.inputType == 'radio'){
24633 return this.getGroupValue();
24636 return this.hiddenEl().dom.value;
24640 getGroupValue : function()
24642 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
24646 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
24649 setValue : function(v,suppressEvent)
24651 if(this.inputType == 'radio'){
24652 this.setGroupValue(v, suppressEvent);
24656 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
24661 setGroupValue : function(v, suppressEvent)
24663 this.startValue = this.getValue();
24665 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24666 e.dom.checked = false;
24668 if(e.dom.value == v){
24669 e.dom.checked = true;
24673 if(suppressEvent !== true){
24674 this.fireEvent('check', this, true);
24682 validate : function()
24684 if(this.getVisibilityEl().hasClass('hidden')){
24690 (this.inputType == 'radio' && this.validateRadio()) ||
24691 (this.inputType == 'checkbox' && this.validateCheckbox())
24697 this.markInvalid();
24701 validateRadio : function()
24703 if(this.getVisibilityEl().hasClass('hidden')){
24707 if(this.allowBlank){
24713 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24714 if(!e.dom.checked){
24726 validateCheckbox : function()
24729 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
24730 //return (this.getValue() == this.inputValue) ? true : false;
24733 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24741 for(var i in group){
24742 if(group[i].el.isVisible(true)){
24750 for(var i in group){
24755 r = (group[i].getValue() == group[i].inputValue) ? true : false;
24762 * Mark this field as valid
24764 markValid : function()
24768 this.fireEvent('valid', this);
24770 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24773 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24780 if(this.inputType == 'radio'){
24781 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24782 var fg = e.findParent('.form-group', false, true);
24783 if (Roo.bootstrap.version == 3) {
24784 fg.removeClass([_this.invalidClass, _this.validClass]);
24785 fg.addClass(_this.validClass);
24787 fg.removeClass(['is-valid', 'is-invalid']);
24788 fg.addClass('is-valid');
24796 var fg = this.el.findParent('.form-group', false, true);
24797 if (Roo.bootstrap.version == 3) {
24798 fg.removeClass([this.invalidClass, this.validClass]);
24799 fg.addClass(this.validClass);
24801 fg.removeClass(['is-valid', 'is-invalid']);
24802 fg.addClass('is-valid');
24807 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24813 for(var i in group){
24814 var fg = group[i].el.findParent('.form-group', false, true);
24815 if (Roo.bootstrap.version == 3) {
24816 fg.removeClass([this.invalidClass, this.validClass]);
24817 fg.addClass(this.validClass);
24819 fg.removeClass(['is-valid', 'is-invalid']);
24820 fg.addClass('is-valid');
24826 * Mark this field as invalid
24827 * @param {String} msg The validation message
24829 markInvalid : function(msg)
24831 if(this.allowBlank){
24837 this.fireEvent('invalid', this, msg);
24839 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24842 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24846 label.markInvalid();
24849 if(this.inputType == 'radio'){
24851 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24852 var fg = e.findParent('.form-group', false, true);
24853 if (Roo.bootstrap.version == 3) {
24854 fg.removeClass([_this.invalidClass, _this.validClass]);
24855 fg.addClass(_this.invalidClass);
24857 fg.removeClass(['is-invalid', 'is-valid']);
24858 fg.addClass('is-invalid');
24866 var fg = this.el.findParent('.form-group', false, true);
24867 if (Roo.bootstrap.version == 3) {
24868 fg.removeClass([_this.invalidClass, _this.validClass]);
24869 fg.addClass(_this.invalidClass);
24871 fg.removeClass(['is-invalid', 'is-valid']);
24872 fg.addClass('is-invalid');
24877 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24883 for(var i in group){
24884 var fg = group[i].el.findParent('.form-group', false, true);
24885 if (Roo.bootstrap.version == 3) {
24886 fg.removeClass([_this.invalidClass, _this.validClass]);
24887 fg.addClass(_this.invalidClass);
24889 fg.removeClass(['is-invalid', 'is-valid']);
24890 fg.addClass('is-invalid');
24896 clearInvalid : function()
24898 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
24900 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
24902 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24904 if (label && label.iconEl) {
24905 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
24906 label.iconEl.removeClass(['is-invalid', 'is-valid']);
24910 disable : function()
24912 if(this.inputType != 'radio'){
24913 Roo.bootstrap.CheckBox.superclass.disable.call(this);
24920 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24921 _this.getActionEl().addClass(this.disabledClass);
24922 e.dom.disabled = true;
24926 this.disabled = true;
24927 this.fireEvent("disable", this);
24931 enable : function()
24933 if(this.inputType != 'radio'){
24934 Roo.bootstrap.CheckBox.superclass.enable.call(this);
24941 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24942 _this.getActionEl().removeClass(this.disabledClass);
24943 e.dom.disabled = false;
24947 this.disabled = false;
24948 this.fireEvent("enable", this);
24952 setBoxLabel : function(v)
24957 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24963 Roo.apply(Roo.bootstrap.CheckBox, {
24968 * register a CheckBox Group
24969 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
24971 register : function(checkbox)
24973 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
24974 this.groups[checkbox.groupId] = {};
24977 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
24981 this.groups[checkbox.groupId][checkbox.name] = checkbox;
24985 * fetch a CheckBox Group based on the group ID
24986 * @param {string} the group ID
24987 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
24989 get: function(groupId) {
24990 if (typeof(this.groups[groupId]) == 'undefined') {
24994 return this.groups[groupId] ;
25007 * @class Roo.bootstrap.Radio
25008 * @extends Roo.bootstrap.Component
25009 * Bootstrap Radio class
25010 * @cfg {String} boxLabel - the label associated
25011 * @cfg {String} value - the value of radio
25014 * Create a new Radio
25015 * @param {Object} config The config object
25017 Roo.bootstrap.Radio = function(config){
25018 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
25022 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
25028 getAutoCreate : function()
25032 cls : 'form-group radio',
25037 html : this.boxLabel
25045 initEvents : function()
25047 this.parent().register(this);
25049 this.el.on('click', this.onClick, this);
25053 onClick : function(e)
25055 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25056 this.setChecked(true);
25060 setChecked : function(state, suppressEvent)
25062 this.parent().setValue(this.value, suppressEvent);
25066 setBoxLabel : function(v)
25071 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25086 * @class Roo.bootstrap.SecurePass
25087 * @extends Roo.bootstrap.Input
25088 * Bootstrap SecurePass class
25092 * Create a new SecurePass
25093 * @param {Object} config The config object
25096 Roo.bootstrap.SecurePass = function (config) {
25097 // these go here, so the translation tool can replace them..
25099 PwdEmpty: "Please type a password, and then retype it to confirm.",
25100 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25101 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25102 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25103 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25104 FNInPwd: "Your password can't contain your first name. Please type a different password.",
25105 LNInPwd: "Your password can't contain your last name. Please type a different password.",
25106 TooWeak: "Your password is Too Weak."
25108 this.meterLabel = "Password strength:";
25109 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25110 this.meterClass = [
25111 "roo-password-meter-tooweak",
25112 "roo-password-meter-weak",
25113 "roo-password-meter-medium",
25114 "roo-password-meter-strong",
25115 "roo-password-meter-grey"
25120 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
25123 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
25125 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25127 * PwdEmpty: "Please type a password, and then retype it to confirm.",
25128 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25129 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25130 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25131 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25132 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
25133 * LNInPwd: "Your password can't contain your last name. Please type a different password."
25143 * @cfg {String/Object} Label for the strength meter (defaults to
25144 * 'Password strength:')
25149 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25150 * ['Weak', 'Medium', 'Strong'])
25153 pwdStrengths: false,
25166 initEvents: function ()
25168 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
25170 if (this.el.is('input[type=password]') && Roo.isSafari) {
25171 this.el.on('keydown', this.SafariOnKeyDown, this);
25174 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25177 onRender: function (ct, position)
25179 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
25180 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25181 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25183 this.trigger.createChild({
25188 cls: 'roo-password-meter-grey col-xs-12',
25191 //width: this.meterWidth + 'px'
25195 cls: 'roo-password-meter-text'
25201 if (this.hideTrigger) {
25202 this.trigger.setDisplayed(false);
25204 this.setSize(this.width || '', this.height || '');
25207 onDestroy: function ()
25209 if (this.trigger) {
25210 this.trigger.removeAllListeners();
25211 this.trigger.remove();
25214 this.wrap.remove();
25216 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
25219 checkStrength: function ()
25221 var pwd = this.inputEl().getValue();
25222 if (pwd == this._lastPwd) {
25227 if (this.ClientSideStrongPassword(pwd)) {
25229 } else if (this.ClientSideMediumPassword(pwd)) {
25231 } else if (this.ClientSideWeakPassword(pwd)) {
25237 Roo.log('strength1: ' + strength);
25239 //var pm = this.trigger.child('div/div/div').dom;
25240 var pm = this.trigger.child('div/div');
25241 pm.removeClass(this.meterClass);
25242 pm.addClass(this.meterClass[strength]);
25245 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25247 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25249 this._lastPwd = pwd;
25253 Roo.bootstrap.SecurePass.superclass.reset.call(this);
25255 this._lastPwd = '';
25257 var pm = this.trigger.child('div/div');
25258 pm.removeClass(this.meterClass);
25259 pm.addClass('roo-password-meter-grey');
25262 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25265 this.inputEl().dom.type='password';
25268 validateValue: function (value)
25270 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
25273 if (value.length == 0) {
25274 if (this.allowBlank) {
25275 this.clearInvalid();
25279 this.markInvalid(this.errors.PwdEmpty);
25280 this.errorMsg = this.errors.PwdEmpty;
25288 if (!value.match(/[\x21-\x7e]+/)) {
25289 this.markInvalid(this.errors.PwdBadChar);
25290 this.errorMsg = this.errors.PwdBadChar;
25293 if (value.length < 6) {
25294 this.markInvalid(this.errors.PwdShort);
25295 this.errorMsg = this.errors.PwdShort;
25298 if (value.length > 16) {
25299 this.markInvalid(this.errors.PwdLong);
25300 this.errorMsg = this.errors.PwdLong;
25304 if (this.ClientSideStrongPassword(value)) {
25306 } else if (this.ClientSideMediumPassword(value)) {
25308 } else if (this.ClientSideWeakPassword(value)) {
25315 if (strength < 2) {
25316 //this.markInvalid(this.errors.TooWeak);
25317 this.errorMsg = this.errors.TooWeak;
25322 console.log('strength2: ' + strength);
25324 //var pm = this.trigger.child('div/div/div').dom;
25326 var pm = this.trigger.child('div/div');
25327 pm.removeClass(this.meterClass);
25328 pm.addClass(this.meterClass[strength]);
25330 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25332 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25334 this.errorMsg = '';
25338 CharacterSetChecks: function (type)
25341 this.fResult = false;
25344 isctype: function (character, type)
25347 case this.kCapitalLetter:
25348 if (character >= 'A' && character <= 'Z') {
25353 case this.kSmallLetter:
25354 if (character >= 'a' && character <= 'z') {
25360 if (character >= '0' && character <= '9') {
25365 case this.kPunctuation:
25366 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25377 IsLongEnough: function (pwd, size)
25379 return !(pwd == null || isNaN(size) || pwd.length < size);
25382 SpansEnoughCharacterSets: function (word, nb)
25384 if (!this.IsLongEnough(word, nb))
25389 var characterSetChecks = new Array(
25390 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25391 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25394 for (var index = 0; index < word.length; ++index) {
25395 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25396 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25397 characterSetChecks[nCharSet].fResult = true;
25404 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25405 if (characterSetChecks[nCharSet].fResult) {
25410 if (nCharSets < nb) {
25416 ClientSideStrongPassword: function (pwd)
25418 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25421 ClientSideMediumPassword: function (pwd)
25423 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25426 ClientSideWeakPassword: function (pwd)
25428 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25431 })//<script type="text/javascript">
25434 * Based Ext JS Library 1.1.1
25435 * Copyright(c) 2006-2007, Ext JS, LLC.
25441 * @class Roo.HtmlEditorCore
25442 * @extends Roo.Component
25443 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25445 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25448 Roo.HtmlEditorCore = function(config){
25451 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25456 * @event initialize
25457 * Fires when the editor is fully initialized (including the iframe)
25458 * @param {Roo.HtmlEditorCore} this
25463 * Fires when the editor is first receives the focus. Any insertion must wait
25464 * until after this event.
25465 * @param {Roo.HtmlEditorCore} this
25469 * @event beforesync
25470 * Fires before the textarea is updated with content from the editor iframe. Return false
25471 * to cancel the sync.
25472 * @param {Roo.HtmlEditorCore} this
25473 * @param {String} html
25477 * @event beforepush
25478 * Fires before the iframe editor is updated with content from the textarea. Return false
25479 * to cancel the push.
25480 * @param {Roo.HtmlEditorCore} this
25481 * @param {String} html
25486 * Fires when the textarea is updated with content from the editor iframe.
25487 * @param {Roo.HtmlEditorCore} this
25488 * @param {String} html
25493 * Fires when the iframe editor is updated with content from the textarea.
25494 * @param {Roo.HtmlEditorCore} this
25495 * @param {String} html
25500 * @event editorevent
25501 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25502 * @param {Roo.HtmlEditorCore} this
25508 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
25510 // defaults : white / black...
25511 this.applyBlacklists();
25518 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
25522 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
25528 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
25533 * @cfg {Number} height (in pixels)
25537 * @cfg {Number} width (in pixels)
25542 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25545 stylesheets: false,
25550 // private properties
25551 validationEvent : false,
25553 initialized : false,
25555 sourceEditMode : false,
25556 onFocus : Roo.emptyFn,
25558 hideMode:'offsets',
25562 // blacklist + whitelisted elements..
25569 * Protected method that will not generally be called directly. It
25570 * is called when the editor initializes the iframe with HTML contents. Override this method if you
25571 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25573 getDocMarkup : function(){
25577 // inherit styels from page...??
25578 if (this.stylesheets === false) {
25580 Roo.get(document.head).select('style').each(function(node) {
25581 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25584 Roo.get(document.head).select('link').each(function(node) {
25585 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25588 } else if (!this.stylesheets.length) {
25590 st = '<style type="text/css">' +
25591 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25594 for (var i in this.stylesheets) {
25595 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
25600 st += '<style type="text/css">' +
25601 'IMG { cursor: pointer } ' +
25604 var cls = 'roo-htmleditor-body';
25606 if(this.bodyCls.length){
25607 cls += ' ' + this.bodyCls;
25610 return '<html><head>' + st +
25611 //<style type="text/css">' +
25612 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25614 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
25618 onRender : function(ct, position)
25621 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
25622 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
25625 this.el.dom.style.border = '0 none';
25626 this.el.dom.setAttribute('tabIndex', -1);
25627 this.el.addClass('x-hidden hide');
25631 if(Roo.isIE){ // fix IE 1px bogus margin
25632 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25636 this.frameId = Roo.id();
25640 var iframe = this.owner.wrap.createChild({
25642 cls: 'form-control', // bootstrap..
25644 name: this.frameId,
25645 frameBorder : 'no',
25646 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
25651 this.iframe = iframe.dom;
25653 this.assignDocWin();
25655 this.doc.designMode = 'on';
25658 this.doc.write(this.getDocMarkup());
25662 var task = { // must defer to wait for browser to be ready
25664 //console.log("run task?" + this.doc.readyState);
25665 this.assignDocWin();
25666 if(this.doc.body || this.doc.readyState == 'complete'){
25668 this.doc.designMode="on";
25672 Roo.TaskMgr.stop(task);
25673 this.initEditor.defer(10, this);
25680 Roo.TaskMgr.start(task);
25685 onResize : function(w, h)
25687 Roo.log('resize: ' +w + ',' + h );
25688 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
25692 if(typeof w == 'number'){
25694 this.iframe.style.width = w + 'px';
25696 if(typeof h == 'number'){
25698 this.iframe.style.height = h + 'px';
25700 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
25707 * Toggles the editor between standard and source edit mode.
25708 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25710 toggleSourceEdit : function(sourceEditMode){
25712 this.sourceEditMode = sourceEditMode === true;
25714 if(this.sourceEditMode){
25716 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
25719 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
25720 //this.iframe.className = '';
25723 //this.setSize(this.owner.wrap.getSize());
25724 //this.fireEvent('editmodechange', this, this.sourceEditMode);
25731 * Protected method that will not generally be called directly. If you need/want
25732 * custom HTML cleanup, this is the method you should override.
25733 * @param {String} html The HTML to be cleaned
25734 * return {String} The cleaned HTML
25736 cleanHtml : function(html){
25737 html = String(html);
25738 if(html.length > 5){
25739 if(Roo.isSafari){ // strip safari nonsense
25740 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25743 if(html == ' '){
25750 * HTML Editor -> Textarea
25751 * Protected method that will not generally be called directly. Syncs the contents
25752 * of the editor iframe with the textarea.
25754 syncValue : function(){
25755 if(this.initialized){
25756 var bd = (this.doc.body || this.doc.documentElement);
25757 //this.cleanUpPaste(); -- this is done else where and causes havoc..
25758 var html = bd.innerHTML;
25760 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25761 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25763 html = '<div style="'+m[0]+'">' + html + '</div>';
25766 html = this.cleanHtml(html);
25767 // fix up the special chars.. normaly like back quotes in word...
25768 // however we do not want to do this with chinese..
25769 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
25771 var cc = match.charCodeAt();
25773 // Get the character value, handling surrogate pairs
25774 if (match.length == 2) {
25775 // It's a surrogate pair, calculate the Unicode code point
25776 var high = match.charCodeAt(0) - 0xD800;
25777 var low = match.charCodeAt(1) - 0xDC00;
25778 cc = (high * 0x400) + low + 0x10000;
25780 (cc >= 0x4E00 && cc < 0xA000 ) ||
25781 (cc >= 0x3400 && cc < 0x4E00 ) ||
25782 (cc >= 0xf900 && cc < 0xfb00 )
25787 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
25788 return "&#" + cc + ";";
25795 if(this.owner.fireEvent('beforesync', this, html) !== false){
25796 this.el.dom.value = html;
25797 this.owner.fireEvent('sync', this, html);
25803 * Protected method that will not generally be called directly. Pushes the value of the textarea
25804 * into the iframe editor.
25806 pushValue : function(){
25807 if(this.initialized){
25808 var v = this.el.dom.value.trim();
25810 // if(v.length < 1){
25814 if(this.owner.fireEvent('beforepush', this, v) !== false){
25815 var d = (this.doc.body || this.doc.documentElement);
25817 this.cleanUpPaste();
25818 this.el.dom.value = d.innerHTML;
25819 this.owner.fireEvent('push', this, v);
25825 deferFocus : function(){
25826 this.focus.defer(10, this);
25830 focus : function(){
25831 if(this.win && !this.sourceEditMode){
25838 assignDocWin: function()
25840 var iframe = this.iframe;
25843 this.doc = iframe.contentWindow.document;
25844 this.win = iframe.contentWindow;
25846 // if (!Roo.get(this.frameId)) {
25849 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25850 // this.win = Roo.get(this.frameId).dom.contentWindow;
25852 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25856 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25857 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25862 initEditor : function(){
25863 //console.log("INIT EDITOR");
25864 this.assignDocWin();
25868 this.doc.designMode="on";
25870 this.doc.write(this.getDocMarkup());
25873 var dbody = (this.doc.body || this.doc.documentElement);
25874 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25875 // this copies styles from the containing element into thsi one..
25876 // not sure why we need all of this..
25877 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25879 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25880 //ss['background-attachment'] = 'fixed'; // w3c
25881 dbody.bgProperties = 'fixed'; // ie
25882 //Roo.DomHelper.applyStyles(dbody, ss);
25883 Roo.EventManager.on(this.doc, {
25884 //'mousedown': this.onEditorEvent,
25885 'mouseup': this.onEditorEvent,
25886 'dblclick': this.onEditorEvent,
25887 'click': this.onEditorEvent,
25888 'keyup': this.onEditorEvent,
25893 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25895 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25896 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25898 this.initialized = true;
25900 this.owner.fireEvent('initialize', this);
25905 onDestroy : function(){
25911 //for (var i =0; i < this.toolbars.length;i++) {
25912 // // fixme - ask toolbars for heights?
25913 // this.toolbars[i].onDestroy();
25916 //this.wrap.dom.innerHTML = '';
25917 //this.wrap.remove();
25922 onFirstFocus : function(){
25924 this.assignDocWin();
25927 this.activated = true;
25930 if(Roo.isGecko){ // prevent silly gecko errors
25932 var s = this.win.getSelection();
25933 if(!s.focusNode || s.focusNode.nodeType != 3){
25934 var r = s.getRangeAt(0);
25935 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25940 this.execCmd('useCSS', true);
25941 this.execCmd('styleWithCSS', false);
25944 this.owner.fireEvent('activate', this);
25948 adjustFont: function(btn){
25949 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25950 //if(Roo.isSafari){ // safari
25953 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25954 if(Roo.isSafari){ // safari
25955 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25956 v = (v < 10) ? 10 : v;
25957 v = (v > 48) ? 48 : v;
25958 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25963 v = Math.max(1, v+adjust);
25965 this.execCmd('FontSize', v );
25968 onEditorEvent : function(e)
25970 this.owner.fireEvent('editorevent', this, e);
25971 // this.updateToolbar();
25972 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25975 insertTag : function(tg)
25977 // could be a bit smarter... -> wrap the current selected tRoo..
25978 if (tg.toLowerCase() == 'span' ||
25979 tg.toLowerCase() == 'code' ||
25980 tg.toLowerCase() == 'sup' ||
25981 tg.toLowerCase() == 'sub'
25984 range = this.createRange(this.getSelection());
25985 var wrappingNode = this.doc.createElement(tg.toLowerCase());
25986 wrappingNode.appendChild(range.extractContents());
25987 range.insertNode(wrappingNode);
25994 this.execCmd("formatblock", tg);
25998 insertText : function(txt)
26002 var range = this.createRange();
26003 range.deleteContents();
26004 //alert(Sender.getAttribute('label'));
26006 range.insertNode(this.doc.createTextNode(txt));
26012 * Executes a Midas editor command on the editor document and performs necessary focus and
26013 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26014 * @param {String} cmd The Midas command
26015 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26017 relayCmd : function(cmd, value){
26019 this.execCmd(cmd, value);
26020 this.owner.fireEvent('editorevent', this);
26021 //this.updateToolbar();
26022 this.owner.deferFocus();
26026 * Executes a Midas editor command directly on the editor document.
26027 * For visual commands, you should use {@link #relayCmd} instead.
26028 * <b>This should only be called after the editor is initialized.</b>
26029 * @param {String} cmd The Midas command
26030 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26032 execCmd : function(cmd, value){
26033 this.doc.execCommand(cmd, false, value === undefined ? null : value);
26040 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26042 * @param {String} text | dom node..
26044 insertAtCursor : function(text)
26047 if(!this.activated){
26053 var r = this.doc.selection.createRange();
26064 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26068 // from jquery ui (MIT licenced)
26070 var win = this.win;
26072 if (win.getSelection && win.getSelection().getRangeAt) {
26073 range = win.getSelection().getRangeAt(0);
26074 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26075 range.insertNode(node);
26076 } else if (win.document.selection && win.document.selection.createRange) {
26077 // no firefox support
26078 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26079 win.document.selection.createRange().pasteHTML(txt);
26081 // no firefox support
26082 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26083 this.execCmd('InsertHTML', txt);
26092 mozKeyPress : function(e){
26094 var c = e.getCharCode(), cmd;
26097 c = String.fromCharCode(c).toLowerCase();
26111 this.cleanUpPaste.defer(100, this);
26119 e.preventDefault();
26127 fixKeys : function(){ // load time branching for fastest keydown performance
26129 return function(e){
26130 var k = e.getKey(), r;
26133 r = this.doc.selection.createRange();
26136 r.pasteHTML('    ');
26143 r = this.doc.selection.createRange();
26145 var target = r.parentElement();
26146 if(!target || target.tagName.toLowerCase() != 'li'){
26148 r.pasteHTML('<br />');
26154 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26155 this.cleanUpPaste.defer(100, this);
26161 }else if(Roo.isOpera){
26162 return function(e){
26163 var k = e.getKey();
26167 this.execCmd('InsertHTML','    ');
26170 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26171 this.cleanUpPaste.defer(100, this);
26176 }else if(Roo.isSafari){
26177 return function(e){
26178 var k = e.getKey();
26182 this.execCmd('InsertText','\t');
26186 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26187 this.cleanUpPaste.defer(100, this);
26195 getAllAncestors: function()
26197 var p = this.getSelectedNode();
26200 a.push(p); // push blank onto stack..
26201 p = this.getParentElement();
26205 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26209 a.push(this.doc.body);
26213 lastSelNode : false,
26216 getSelection : function()
26218 this.assignDocWin();
26219 return Roo.isIE ? this.doc.selection : this.win.getSelection();
26222 getSelectedNode: function()
26224 // this may only work on Gecko!!!
26226 // should we cache this!!!!
26231 var range = this.createRange(this.getSelection()).cloneRange();
26234 var parent = range.parentElement();
26236 var testRange = range.duplicate();
26237 testRange.moveToElementText(parent);
26238 if (testRange.inRange(range)) {
26241 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26244 parent = parent.parentElement;
26249 // is ancestor a text element.
26250 var ac = range.commonAncestorContainer;
26251 if (ac.nodeType == 3) {
26252 ac = ac.parentNode;
26255 var ar = ac.childNodes;
26258 var other_nodes = [];
26259 var has_other_nodes = false;
26260 for (var i=0;i<ar.length;i++) {
26261 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
26264 // fullly contained node.
26266 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26271 // probably selected..
26272 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26273 other_nodes.push(ar[i]);
26277 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
26282 has_other_nodes = true;
26284 if (!nodes.length && other_nodes.length) {
26285 nodes= other_nodes;
26287 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26293 createRange: function(sel)
26295 // this has strange effects when using with
26296 // top toolbar - not sure if it's a great idea.
26297 //this.editor.contentWindow.focus();
26298 if (typeof sel != "undefined") {
26300 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26302 return this.doc.createRange();
26305 return this.doc.createRange();
26308 getParentElement: function()
26311 this.assignDocWin();
26312 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26314 var range = this.createRange(sel);
26317 var p = range.commonAncestorContainer;
26318 while (p.nodeType == 3) { // text node
26329 * Range intersection.. the hard stuff...
26333 * [ -- selected range --- ]
26337 * if end is before start or hits it. fail.
26338 * if start is after end or hits it fail.
26340 * if either hits (but other is outside. - then it's not
26346 // @see http://www.thismuchiknow.co.uk/?p=64.
26347 rangeIntersectsNode : function(range, node)
26349 var nodeRange = node.ownerDocument.createRange();
26351 nodeRange.selectNode(node);
26353 nodeRange.selectNodeContents(node);
26356 var rangeStartRange = range.cloneRange();
26357 rangeStartRange.collapse(true);
26359 var rangeEndRange = range.cloneRange();
26360 rangeEndRange.collapse(false);
26362 var nodeStartRange = nodeRange.cloneRange();
26363 nodeStartRange.collapse(true);
26365 var nodeEndRange = nodeRange.cloneRange();
26366 nodeEndRange.collapse(false);
26368 return rangeStartRange.compareBoundaryPoints(
26369 Range.START_TO_START, nodeEndRange) == -1 &&
26370 rangeEndRange.compareBoundaryPoints(
26371 Range.START_TO_START, nodeStartRange) == 1;
26375 rangeCompareNode : function(range, node)
26377 var nodeRange = node.ownerDocument.createRange();
26379 nodeRange.selectNode(node);
26381 nodeRange.selectNodeContents(node);
26385 range.collapse(true);
26387 nodeRange.collapse(true);
26389 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26390 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
26392 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26394 var nodeIsBefore = ss == 1;
26395 var nodeIsAfter = ee == -1;
26397 if (nodeIsBefore && nodeIsAfter) {
26400 if (!nodeIsBefore && nodeIsAfter) {
26401 return 1; //right trailed.
26404 if (nodeIsBefore && !nodeIsAfter) {
26405 return 2; // left trailed.
26411 // private? - in a new class?
26412 cleanUpPaste : function()
26414 // cleans up the whole document..
26415 Roo.log('cleanuppaste');
26417 this.cleanUpChildren(this.doc.body);
26418 var clean = this.cleanWordChars(this.doc.body.innerHTML);
26419 if (clean != this.doc.body.innerHTML) {
26420 this.doc.body.innerHTML = clean;
26425 cleanWordChars : function(input) {// change the chars to hex code
26426 var he = Roo.HtmlEditorCore;
26428 var output = input;
26429 Roo.each(he.swapCodes, function(sw) {
26430 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26432 output = output.replace(swapper, sw[1]);
26439 cleanUpChildren : function (n)
26441 if (!n.childNodes.length) {
26444 for (var i = n.childNodes.length-1; i > -1 ; i--) {
26445 this.cleanUpChild(n.childNodes[i]);
26452 cleanUpChild : function (node)
26455 //console.log(node);
26456 if (node.nodeName == "#text") {
26457 // clean up silly Windows -- stuff?
26460 if (node.nodeName == "#comment") {
26461 node.parentNode.removeChild(node);
26462 // clean up silly Windows -- stuff?
26465 var lcname = node.tagName.toLowerCase();
26466 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26467 // whitelist of tags..
26469 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26471 node.parentNode.removeChild(node);
26476 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
26478 // spans with no attributes - just remove them..
26479 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
26480 remove_keep_children = true;
26483 // remove <a name=....> as rendering on yahoo mailer is borked with this.
26484 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26486 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26487 // remove_keep_children = true;
26490 if (remove_keep_children) {
26491 this.cleanUpChildren(node);
26492 // inserts everything just before this node...
26493 while (node.childNodes.length) {
26494 var cn = node.childNodes[0];
26495 node.removeChild(cn);
26496 node.parentNode.insertBefore(cn, node);
26498 node.parentNode.removeChild(node);
26502 if (!node.attributes || !node.attributes.length) {
26507 this.cleanUpChildren(node);
26511 function cleanAttr(n,v)
26514 if (v.match(/^\./) || v.match(/^\//)) {
26517 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
26520 if (v.match(/^#/)) {
26523 if (v.match(/^\{/)) { // allow template editing.
26526 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26527 node.removeAttribute(n);
26531 var cwhite = this.cwhite;
26532 var cblack = this.cblack;
26534 function cleanStyle(n,v)
26536 if (v.match(/expression/)) { //XSS?? should we even bother..
26537 node.removeAttribute(n);
26541 var parts = v.split(/;/);
26544 Roo.each(parts, function(p) {
26545 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26549 var l = p.split(':').shift().replace(/\s+/g,'');
26550 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26552 if ( cwhite.length && cblack.indexOf(l) > -1) {
26553 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26554 //node.removeAttribute(n);
26558 // only allow 'c whitelisted system attributes'
26559 if ( cwhite.length && cwhite.indexOf(l) < 0) {
26560 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26561 //node.removeAttribute(n);
26571 if (clean.length) {
26572 node.setAttribute(n, clean.join(';'));
26574 node.removeAttribute(n);
26580 for (var i = node.attributes.length-1; i > -1 ; i--) {
26581 var a = node.attributes[i];
26584 if (a.name.toLowerCase().substr(0,2)=='on') {
26585 node.removeAttribute(a.name);
26588 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
26589 node.removeAttribute(a.name);
26592 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
26593 cleanAttr(a.name,a.value); // fixme..
26596 if (a.name == 'style') {
26597 cleanStyle(a.name,a.value);
26600 /// clean up MS crap..
26601 // tecnically this should be a list of valid class'es..
26604 if (a.name == 'class') {
26605 if (a.value.match(/^Mso/)) {
26606 node.removeAttribute('class');
26609 if (a.value.match(/^body$/)) {
26610 node.removeAttribute('class');
26621 this.cleanUpChildren(node);
26627 * Clean up MS wordisms...
26629 cleanWord : function(node)
26632 this.cleanWord(this.doc.body);
26637 node.nodeName == 'SPAN' &&
26638 !node.hasAttributes() &&
26639 node.childNodes.length == 1 &&
26640 node.firstChild.nodeName == "#text"
26642 var textNode = node.firstChild;
26643 node.removeChild(textNode);
26644 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
26645 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26647 node.parentNode.insertBefore(textNode, node);
26648 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
26649 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26651 node.parentNode.removeChild(node);
26654 if (node.nodeName == "#text") {
26655 // clean up silly Windows -- stuff?
26658 if (node.nodeName == "#comment") {
26659 node.parentNode.removeChild(node);
26660 // clean up silly Windows -- stuff?
26664 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26665 node.parentNode.removeChild(node);
26668 //Roo.log(node.tagName);
26669 // remove - but keep children..
26670 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26671 //Roo.log('-- removed');
26672 while (node.childNodes.length) {
26673 var cn = node.childNodes[0];
26674 node.removeChild(cn);
26675 node.parentNode.insertBefore(cn, node);
26676 // move node to parent - and clean it..
26677 this.cleanWord(cn);
26679 node.parentNode.removeChild(node);
26680 /// no need to iterate chidlren = it's got none..
26681 //this.iterateChildren(node, this.cleanWord);
26685 if (node.className.length) {
26687 var cn = node.className.split(/\W+/);
26689 Roo.each(cn, function(cls) {
26690 if (cls.match(/Mso[a-zA-Z]+/)) {
26695 node.className = cna.length ? cna.join(' ') : '';
26697 node.removeAttribute("class");
26701 if (node.hasAttribute("lang")) {
26702 node.removeAttribute("lang");
26705 if (node.hasAttribute("style")) {
26707 var styles = node.getAttribute("style").split(";");
26709 Roo.each(styles, function(s) {
26710 if (!s.match(/:/)) {
26713 var kv = s.split(":");
26714 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26717 // what ever is left... we allow.
26720 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26721 if (!nstyle.length) {
26722 node.removeAttribute('style');
26725 this.iterateChildren(node, this.cleanWord);
26731 * iterateChildren of a Node, calling fn each time, using this as the scole..
26732 * @param {DomNode} node node to iterate children of.
26733 * @param {Function} fn method of this class to call on each item.
26735 iterateChildren : function(node, fn)
26737 if (!node.childNodes.length) {
26740 for (var i = node.childNodes.length-1; i > -1 ; i--) {
26741 fn.call(this, node.childNodes[i])
26747 * cleanTableWidths.
26749 * Quite often pasting from word etc.. results in tables with column and widths.
26750 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26753 cleanTableWidths : function(node)
26758 this.cleanTableWidths(this.doc.body);
26763 if (node.nodeName == "#text" || node.nodeName == "#comment") {
26766 Roo.log(node.tagName);
26767 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
26768 this.iterateChildren(node, this.cleanTableWidths);
26771 if (node.hasAttribute('width')) {
26772 node.removeAttribute('width');
26776 if (node.hasAttribute("style")) {
26779 var styles = node.getAttribute("style").split(";");
26781 Roo.each(styles, function(s) {
26782 if (!s.match(/:/)) {
26785 var kv = s.split(":");
26786 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26789 // what ever is left... we allow.
26792 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26793 if (!nstyle.length) {
26794 node.removeAttribute('style');
26798 this.iterateChildren(node, this.cleanTableWidths);
26806 domToHTML : function(currentElement, depth, nopadtext) {
26808 depth = depth || 0;
26809 nopadtext = nopadtext || false;
26811 if (!currentElement) {
26812 return this.domToHTML(this.doc.body);
26815 //Roo.log(currentElement);
26817 var allText = false;
26818 var nodeName = currentElement.nodeName;
26819 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
26821 if (nodeName == '#text') {
26823 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
26828 if (nodeName != 'BODY') {
26831 // Prints the node tagName, such as <A>, <IMG>, etc
26834 for(i = 0; i < currentElement.attributes.length;i++) {
26836 var aname = currentElement.attributes.item(i).name;
26837 if (!currentElement.attributes.item(i).value.length) {
26840 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
26843 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
26852 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
26855 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
26860 // Traverse the tree
26862 var currentElementChild = currentElement.childNodes.item(i);
26863 var allText = true;
26864 var innerHTML = '';
26866 while (currentElementChild) {
26867 // Formatting code (indent the tree so it looks nice on the screen)
26868 var nopad = nopadtext;
26869 if (lastnode == 'SPAN') {
26873 if (currentElementChild.nodeName == '#text') {
26874 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
26875 toadd = nopadtext ? toadd : toadd.trim();
26876 if (!nopad && toadd.length > 80) {
26877 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
26879 innerHTML += toadd;
26882 currentElementChild = currentElement.childNodes.item(i);
26888 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
26890 // Recursively traverse the tree structure of the child node
26891 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
26892 lastnode = currentElementChild.nodeName;
26894 currentElementChild=currentElement.childNodes.item(i);
26900 // The remaining code is mostly for formatting the tree
26901 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
26906 ret+= "</"+tagName+">";
26912 applyBlacklists : function()
26914 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
26915 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
26919 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
26920 if (b.indexOf(tag) > -1) {
26923 this.white.push(tag);
26927 Roo.each(w, function(tag) {
26928 if (b.indexOf(tag) > -1) {
26931 if (this.white.indexOf(tag) > -1) {
26934 this.white.push(tag);
26939 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
26940 if (w.indexOf(tag) > -1) {
26943 this.black.push(tag);
26947 Roo.each(b, function(tag) {
26948 if (w.indexOf(tag) > -1) {
26951 if (this.black.indexOf(tag) > -1) {
26954 this.black.push(tag);
26959 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
26960 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
26964 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
26965 if (b.indexOf(tag) > -1) {
26968 this.cwhite.push(tag);
26972 Roo.each(w, function(tag) {
26973 if (b.indexOf(tag) > -1) {
26976 if (this.cwhite.indexOf(tag) > -1) {
26979 this.cwhite.push(tag);
26984 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
26985 if (w.indexOf(tag) > -1) {
26988 this.cblack.push(tag);
26992 Roo.each(b, function(tag) {
26993 if (w.indexOf(tag) > -1) {
26996 if (this.cblack.indexOf(tag) > -1) {
26999 this.cblack.push(tag);
27004 setStylesheets : function(stylesheets)
27006 if(typeof(stylesheets) == 'string'){
27007 Roo.get(this.iframe.contentDocument.head).createChild({
27009 rel : 'stylesheet',
27018 Roo.each(stylesheets, function(s) {
27023 Roo.get(_this.iframe.contentDocument.head).createChild({
27025 rel : 'stylesheet',
27034 removeStylesheets : function()
27038 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27043 setStyle : function(style)
27045 Roo.get(this.iframe.contentDocument.head).createChild({
27054 // hide stuff that is not compatible
27068 * @event specialkey
27072 * @cfg {String} fieldClass @hide
27075 * @cfg {String} focusClass @hide
27078 * @cfg {String} autoCreate @hide
27081 * @cfg {String} inputType @hide
27084 * @cfg {String} invalidClass @hide
27087 * @cfg {String} invalidText @hide
27090 * @cfg {String} msgFx @hide
27093 * @cfg {String} validateOnBlur @hide
27097 Roo.HtmlEditorCore.white = [
27098 'area', 'br', 'img', 'input', 'hr', 'wbr',
27100 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
27101 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
27102 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
27103 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
27104 'table', 'ul', 'xmp',
27106 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
27109 'dir', 'menu', 'ol', 'ul', 'dl',
27115 Roo.HtmlEditorCore.black = [
27116 // 'embed', 'object', // enable - backend responsiblity to clean thiese
27118 'base', 'basefont', 'bgsound', 'blink', 'body',
27119 'frame', 'frameset', 'head', 'html', 'ilayer',
27120 'iframe', 'layer', 'link', 'meta', 'object',
27121 'script', 'style' ,'title', 'xml' // clean later..
27123 Roo.HtmlEditorCore.clean = [
27124 'script', 'style', 'title', 'xml'
27126 Roo.HtmlEditorCore.remove = [
27131 Roo.HtmlEditorCore.ablack = [
27135 Roo.HtmlEditorCore.aclean = [
27136 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
27140 Roo.HtmlEditorCore.pwhite= [
27141 'http', 'https', 'mailto'
27144 // white listed style attributes.
27145 Roo.HtmlEditorCore.cwhite= [
27146 // 'text-align', /// default is to allow most things..
27152 // black listed style attributes.
27153 Roo.HtmlEditorCore.cblack= [
27154 // 'font-size' -- this can be set by the project
27158 Roo.HtmlEditorCore.swapCodes =[
27159 [ 8211, "–" ],
27160 [ 8212, "—" ],
27177 * @class Roo.bootstrap.HtmlEditor
27178 * @extends Roo.bootstrap.TextArea
27179 * Bootstrap HtmlEditor class
27182 * Create a new HtmlEditor
27183 * @param {Object} config The config object
27186 Roo.bootstrap.HtmlEditor = function(config){
27187 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
27188 if (!this.toolbars) {
27189 this.toolbars = [];
27192 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27195 * @event initialize
27196 * Fires when the editor is fully initialized (including the iframe)
27197 * @param {HtmlEditor} this
27202 * Fires when the editor is first receives the focus. Any insertion must wait
27203 * until after this event.
27204 * @param {HtmlEditor} this
27208 * @event beforesync
27209 * Fires before the textarea is updated with content from the editor iframe. Return false
27210 * to cancel the sync.
27211 * @param {HtmlEditor} this
27212 * @param {String} html
27216 * @event beforepush
27217 * Fires before the iframe editor is updated with content from the textarea. Return false
27218 * to cancel the push.
27219 * @param {HtmlEditor} this
27220 * @param {String} html
27225 * Fires when the textarea is updated with content from the editor iframe.
27226 * @param {HtmlEditor} this
27227 * @param {String} html
27232 * Fires when the iframe editor is updated with content from the textarea.
27233 * @param {HtmlEditor} this
27234 * @param {String} html
27238 * @event editmodechange
27239 * Fires when the editor switches edit modes
27240 * @param {HtmlEditor} this
27241 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27243 editmodechange: true,
27245 * @event editorevent
27246 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27247 * @param {HtmlEditor} this
27251 * @event firstfocus
27252 * Fires when on first focus - needed by toolbars..
27253 * @param {HtmlEditor} this
27258 * Auto save the htmlEditor value as a file into Events
27259 * @param {HtmlEditor} this
27263 * @event savedpreview
27264 * preview the saved version of htmlEditor
27265 * @param {HtmlEditor} this
27272 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
27276 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27281 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27286 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
27291 * @cfg {Number} height (in pixels)
27295 * @cfg {Number} width (in pixels)
27300 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27303 stylesheets: false,
27308 // private properties
27309 validationEvent : false,
27311 initialized : false,
27314 onFocus : Roo.emptyFn,
27316 hideMode:'offsets',
27318 tbContainer : false,
27322 toolbarContainer :function() {
27323 return this.wrap.select('.x-html-editor-tb',true).first();
27327 * Protected method that will not generally be called directly. It
27328 * is called when the editor creates its toolbar. Override this method if you need to
27329 * add custom toolbar buttons.
27330 * @param {HtmlEditor} editor
27332 createToolbar : function(){
27333 Roo.log('renewing');
27334 Roo.log("create toolbars");
27336 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
27337 this.toolbars[0].render(this.toolbarContainer());
27341 // if (!editor.toolbars || !editor.toolbars.length) {
27342 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
27345 // for (var i =0 ; i < editor.toolbars.length;i++) {
27346 // editor.toolbars[i] = Roo.factory(
27347 // typeof(editor.toolbars[i]) == 'string' ?
27348 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
27349 // Roo.bootstrap.HtmlEditor);
27350 // editor.toolbars[i].init(editor);
27356 onRender : function(ct, position)
27358 // Roo.log("Call onRender: " + this.xtype);
27360 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
27362 this.wrap = this.inputEl().wrap({
27363 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27366 this.editorcore.onRender(ct, position);
27368 if (this.resizable) {
27369 this.resizeEl = new Roo.Resizable(this.wrap, {
27373 minHeight : this.height,
27374 height: this.height,
27375 handles : this.resizable,
27378 resize : function(r, w, h) {
27379 _t.onResize(w,h); // -something
27385 this.createToolbar(this);
27388 if(!this.width && this.resizable){
27389 this.setSize(this.wrap.getSize());
27391 if (this.resizeEl) {
27392 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27393 // should trigger onReize..
27399 onResize : function(w, h)
27401 Roo.log('resize: ' +w + ',' + h );
27402 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
27406 if(this.inputEl() ){
27407 if(typeof w == 'number'){
27408 var aw = w - this.wrap.getFrameWidth('lr');
27409 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27412 if(typeof h == 'number'){
27413 var tbh = -11; // fixme it needs to tool bar size!
27414 for (var i =0; i < this.toolbars.length;i++) {
27415 // fixme - ask toolbars for heights?
27416 tbh += this.toolbars[i].el.getHeight();
27417 //if (this.toolbars[i].footer) {
27418 // tbh += this.toolbars[i].footer.el.getHeight();
27426 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27427 ah -= 5; // knock a few pixes off for look..
27428 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27432 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27433 this.editorcore.onResize(ew,eh);
27438 * Toggles the editor between standard and source edit mode.
27439 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27441 toggleSourceEdit : function(sourceEditMode)
27443 this.editorcore.toggleSourceEdit(sourceEditMode);
27445 if(this.editorcore.sourceEditMode){
27446 Roo.log('editor - showing textarea');
27449 // Roo.log(this.syncValue());
27451 this.inputEl().removeClass(['hide', 'x-hidden']);
27452 this.inputEl().dom.removeAttribute('tabIndex');
27453 this.inputEl().focus();
27455 Roo.log('editor - hiding textarea');
27457 // Roo.log(this.pushValue());
27460 this.inputEl().addClass(['hide', 'x-hidden']);
27461 this.inputEl().dom.setAttribute('tabIndex', -1);
27462 //this.deferFocus();
27465 if(this.resizable){
27466 this.setSize(this.wrap.getSize());
27469 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27472 // private (for BoxComponent)
27473 adjustSize : Roo.BoxComponent.prototype.adjustSize,
27475 // private (for BoxComponent)
27476 getResizeEl : function(){
27480 // private (for BoxComponent)
27481 getPositionEl : function(){
27486 initEvents : function(){
27487 this.originalValue = this.getValue();
27491 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27494 // markInvalid : Roo.emptyFn,
27496 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27499 // clearInvalid : Roo.emptyFn,
27501 setValue : function(v){
27502 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
27503 this.editorcore.pushValue();
27508 deferFocus : function(){
27509 this.focus.defer(10, this);
27513 focus : function(){
27514 this.editorcore.focus();
27520 onDestroy : function(){
27526 for (var i =0; i < this.toolbars.length;i++) {
27527 // fixme - ask toolbars for heights?
27528 this.toolbars[i].onDestroy();
27531 this.wrap.dom.innerHTML = '';
27532 this.wrap.remove();
27537 onFirstFocus : function(){
27538 //Roo.log("onFirstFocus");
27539 this.editorcore.onFirstFocus();
27540 for (var i =0; i < this.toolbars.length;i++) {
27541 this.toolbars[i].onFirstFocus();
27547 syncValue : function()
27549 this.editorcore.syncValue();
27552 pushValue : function()
27554 this.editorcore.pushValue();
27558 // hide stuff that is not compatible
27572 * @event specialkey
27576 * @cfg {String} fieldClass @hide
27579 * @cfg {String} focusClass @hide
27582 * @cfg {String} autoCreate @hide
27585 * @cfg {String} inputType @hide
27589 * @cfg {String} invalidText @hide
27592 * @cfg {String} msgFx @hide
27595 * @cfg {String} validateOnBlur @hide
27604 Roo.namespace('Roo.bootstrap.htmleditor');
27606 * @class Roo.bootstrap.HtmlEditorToolbar1
27612 new Roo.bootstrap.HtmlEditor({
27615 new Roo.bootstrap.HtmlEditorToolbar1({
27616 disable : { fonts: 1 , format: 1, ..., ... , ...],
27622 * @cfg {Object} disable List of elements to disable..
27623 * @cfg {Array} btns List of additional buttons.
27627 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27630 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
27633 Roo.apply(this, config);
27635 // default disabled, based on 'good practice'..
27636 this.disable = this.disable || {};
27637 Roo.applyIf(this.disable, {
27640 specialElements : true
27642 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
27644 this.editor = config.editor;
27645 this.editorcore = config.editor.editorcore;
27647 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
27649 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27650 // dont call parent... till later.
27652 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
27657 editorcore : false,
27662 "h1","h2","h3","h4","h5","h6",
27664 "abbr", "acronym", "address", "cite", "samp", "var",
27668 onRender : function(ct, position)
27670 // Roo.log("Call onRender: " + this.xtype);
27672 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
27674 this.el.dom.style.marginBottom = '0';
27676 var editorcore = this.editorcore;
27677 var editor= this.editor;
27680 var btn = function(id,cmd , toggle, handler, html){
27682 var event = toggle ? 'toggle' : 'click';
27687 xns: Roo.bootstrap,
27691 enableToggle:toggle !== false,
27693 pressed : toggle ? false : null,
27696 a.listeners[toggle ? 'toggle' : 'click'] = function() {
27697 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
27703 // var cb_box = function...
27708 xns: Roo.bootstrap,
27713 xns: Roo.bootstrap,
27717 Roo.each(this.formats, function(f) {
27718 style.menu.items.push({
27720 xns: Roo.bootstrap,
27721 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
27726 editorcore.insertTag(this.tagname);
27733 children.push(style);
27735 btn('bold',false,true);
27736 btn('italic',false,true);
27737 btn('align-left', 'justifyleft',true);
27738 btn('align-center', 'justifycenter',true);
27739 btn('align-right' , 'justifyright',true);
27740 btn('link', false, false, function(btn) {
27741 //Roo.log("create link?");
27742 var url = prompt(this.createLinkText, this.defaultLinkValue);
27743 if(url && url != 'http:/'+'/'){
27744 this.editorcore.relayCmd('createlink', url);
27747 btn('list','insertunorderedlist',true);
27748 btn('pencil', false,true, function(btn){
27750 this.toggleSourceEdit(btn.pressed);
27753 if (this.editor.btns.length > 0) {
27754 for (var i = 0; i<this.editor.btns.length; i++) {
27755 children.push(this.editor.btns[i]);
27763 xns: Roo.bootstrap,
27768 xns: Roo.bootstrap,
27773 cog.menu.items.push({
27775 xns: Roo.bootstrap,
27776 html : Clean styles,
27781 editorcore.insertTag(this.tagname);
27790 this.xtype = 'NavSimplebar';
27792 for(var i=0;i< children.length;i++) {
27794 this.buttons.add(this.addxtypeChild(children[i]));
27798 editor.on('editorevent', this.updateToolbar, this);
27800 onBtnClick : function(id)
27802 this.editorcore.relayCmd(id);
27803 this.editorcore.focus();
27807 * Protected method that will not generally be called directly. It triggers
27808 * a toolbar update by reading the markup state of the current selection in the editor.
27810 updateToolbar: function(){
27812 if(!this.editorcore.activated){
27813 this.editor.onFirstFocus(); // is this neeed?
27817 var btns = this.buttons;
27818 var doc = this.editorcore.doc;
27819 btns.get('bold').setActive(doc.queryCommandState('bold'));
27820 btns.get('italic').setActive(doc.queryCommandState('italic'));
27821 //btns.get('underline').setActive(doc.queryCommandState('underline'));
27823 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
27824 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
27825 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
27827 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
27828 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
27831 var ans = this.editorcore.getAllAncestors();
27832 if (this.formatCombo) {
27835 var store = this.formatCombo.store;
27836 this.formatCombo.setValue("");
27837 for (var i =0; i < ans.length;i++) {
27838 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27840 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27848 // hides menus... - so this cant be on a menu...
27849 Roo.bootstrap.MenuMgr.hideAll();
27851 Roo.bootstrap.MenuMgr.hideAll();
27852 //this.editorsyncValue();
27854 onFirstFocus: function() {
27855 this.buttons.each(function(item){
27859 toggleSourceEdit : function(sourceEditMode){
27862 if(sourceEditMode){
27863 Roo.log("disabling buttons");
27864 this.buttons.each( function(item){
27865 if(item.cmd != 'pencil'){
27871 Roo.log("enabling buttons");
27872 if(this.editorcore.initialized){
27873 this.buttons.each( function(item){
27879 Roo.log("calling toggole on editor");
27880 // tell the editor that it's been pressed..
27881 this.editor.toggleSourceEdit(sourceEditMode);
27895 * @class Roo.bootstrap.Markdown
27896 * @extends Roo.bootstrap.TextArea
27897 * Bootstrap Showdown editable area
27898 * @cfg {string} content
27901 * Create a new Showdown
27904 Roo.bootstrap.Markdown = function(config){
27905 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
27909 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
27913 initEvents : function()
27916 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
27917 this.markdownEl = this.el.createChild({
27918 cls : 'roo-markdown-area'
27920 this.inputEl().addClass('d-none');
27921 if (this.getValue() == '') {
27922 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
27925 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
27927 this.markdownEl.on('click', this.toggleTextEdit, this);
27928 this.on('blur', this.toggleTextEdit, this);
27929 this.on('specialkey', this.resizeTextArea, this);
27932 toggleTextEdit : function()
27934 var sh = this.markdownEl.getHeight();
27935 this.inputEl().addClass('d-none');
27936 this.markdownEl.addClass('d-none');
27937 if (!this.editing) {
27939 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
27940 this.inputEl().removeClass('d-none');
27941 this.inputEl().focus();
27942 this.editing = true;
27945 // show showdown...
27946 this.updateMarkdown();
27947 this.markdownEl.removeClass('d-none');
27948 this.editing = false;
27951 updateMarkdown : function()
27953 if (this.getValue() == '') {
27954 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
27958 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
27961 resizeTextArea: function () {
27964 Roo.log([sh, this.getValue().split("\n").length * 30]);
27965 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
27967 setValue : function(val)
27969 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
27970 if (!this.editing) {
27971 this.updateMarkdown();
27977 if (!this.editing) {
27978 this.toggleTextEdit();
27986 * Ext JS Library 1.1.1
27987 * Copyright(c) 2006-2007, Ext JS, LLC.
27989 * Originally Released Under LGPL - original licence link has changed is not relivant.
27992 * <script type="text/javascript">
27996 * @class Roo.bootstrap.PagingToolbar
27997 * @extends Roo.bootstrap.NavSimplebar
27998 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28000 * Create a new PagingToolbar
28001 * @param {Object} config The config object
28002 * @param {Roo.data.Store} store
28004 Roo.bootstrap.PagingToolbar = function(config)
28006 // old args format still supported... - xtype is prefered..
28007 // created from xtype...
28009 this.ds = config.dataSource;
28011 if (config.store && !this.ds) {
28012 this.store= Roo.factory(config.store, Roo.data);
28013 this.ds = this.store;
28014 this.ds.xmodule = this.xmodule || false;
28017 this.toolbarItems = [];
28018 if (config.items) {
28019 this.toolbarItems = config.items;
28022 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28027 this.bind(this.ds);
28030 if (Roo.bootstrap.version == 4) {
28031 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28033 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
28038 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
28040 * @cfg {Roo.data.Store} dataSource
28041 * The underlying data store providing the paged data
28044 * @cfg {String/HTMLElement/Element} container
28045 * container The id or element that will contain the toolbar
28048 * @cfg {Boolean} displayInfo
28049 * True to display the displayMsg (defaults to false)
28052 * @cfg {Number} pageSize
28053 * The number of records to display per page (defaults to 20)
28057 * @cfg {String} displayMsg
28058 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28060 displayMsg : 'Displaying {0} - {1} of {2}',
28062 * @cfg {String} emptyMsg
28063 * The message to display when no records are found (defaults to "No data to display")
28065 emptyMsg : 'No data to display',
28067 * Customizable piece of the default paging text (defaults to "Page")
28070 beforePageText : "Page",
28072 * Customizable piece of the default paging text (defaults to "of %0")
28075 afterPageText : "of {0}",
28077 * Customizable piece of the default paging text (defaults to "First Page")
28080 firstText : "First Page",
28082 * Customizable piece of the default paging text (defaults to "Previous Page")
28085 prevText : "Previous Page",
28087 * Customizable piece of the default paging text (defaults to "Next Page")
28090 nextText : "Next Page",
28092 * Customizable piece of the default paging text (defaults to "Last Page")
28095 lastText : "Last Page",
28097 * Customizable piece of the default paging text (defaults to "Refresh")
28100 refreshText : "Refresh",
28104 onRender : function(ct, position)
28106 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28107 this.navgroup.parentId = this.id;
28108 this.navgroup.onRender(this.el, null);
28109 // add the buttons to the navgroup
28111 if(this.displayInfo){
28112 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28113 this.displayEl = this.el.select('.x-paging-info', true).first();
28114 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28115 // this.displayEl = navel.el.select('span',true).first();
28121 Roo.each(_this.buttons, function(e){ // this might need to use render????
28122 Roo.factory(e).render(_this.el);
28126 Roo.each(_this.toolbarItems, function(e) {
28127 _this.navgroup.addItem(e);
28131 this.first = this.navgroup.addItem({
28132 tooltip: this.firstText,
28133 cls: "prev btn-outline-secondary",
28134 html : ' <i class="fa fa-step-backward"></i>',
28136 preventDefault: true,
28137 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28140 this.prev = this.navgroup.addItem({
28141 tooltip: this.prevText,
28142 cls: "prev btn-outline-secondary",
28143 html : ' <i class="fa fa-backward"></i>',
28145 preventDefault: true,
28146 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
28148 //this.addSeparator();
28151 var field = this.navgroup.addItem( {
28153 cls : 'x-paging-position btn-outline-secondary',
28155 html : this.beforePageText +
28156 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28157 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
28160 this.field = field.el.select('input', true).first();
28161 this.field.on("keydown", this.onPagingKeydown, this);
28162 this.field.on("focus", function(){this.dom.select();});
28165 this.afterTextEl = field.el.select('.x-paging-after',true).first();
28166 //this.field.setHeight(18);
28167 //this.addSeparator();
28168 this.next = this.navgroup.addItem({
28169 tooltip: this.nextText,
28170 cls: "next btn-outline-secondary",
28171 html : ' <i class="fa fa-forward"></i>',
28173 preventDefault: true,
28174 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
28176 this.last = this.navgroup.addItem({
28177 tooltip: this.lastText,
28178 html : ' <i class="fa fa-step-forward"></i>',
28179 cls: "next btn-outline-secondary",
28181 preventDefault: true,
28182 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
28184 //this.addSeparator();
28185 this.loading = this.navgroup.addItem({
28186 tooltip: this.refreshText,
28187 cls: "btn-outline-secondary",
28188 html : ' <i class="fa fa-refresh"></i>',
28189 preventDefault: true,
28190 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28196 updateInfo : function(){
28197 if(this.displayEl){
28198 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28199 var msg = count == 0 ?
28203 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
28205 this.displayEl.update(msg);
28210 onLoad : function(ds, r, o)
28212 this.cursor = o.params && o.params.start ? o.params.start : 0;
28214 var d = this.getPageData(),
28219 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28220 this.field.dom.value = ap;
28221 this.first.setDisabled(ap == 1);
28222 this.prev.setDisabled(ap == 1);
28223 this.next.setDisabled(ap == ps);
28224 this.last.setDisabled(ap == ps);
28225 this.loading.enable();
28230 getPageData : function(){
28231 var total = this.ds.getTotalCount();
28234 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28235 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28240 onLoadError : function(){
28241 this.loading.enable();
28245 onPagingKeydown : function(e){
28246 var k = e.getKey();
28247 var d = this.getPageData();
28249 var v = this.field.dom.value, pageNum;
28250 if(!v || isNaN(pageNum = parseInt(v, 10))){
28251 this.field.dom.value = d.activePage;
28254 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28255 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28258 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))
28260 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28261 this.field.dom.value = pageNum;
28262 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28265 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28267 var v = this.field.dom.value, pageNum;
28268 var increment = (e.shiftKey) ? 10 : 1;
28269 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28272 if(!v || isNaN(pageNum = parseInt(v, 10))) {
28273 this.field.dom.value = d.activePage;
28276 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28278 this.field.dom.value = parseInt(v, 10) + increment;
28279 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28280 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28287 beforeLoad : function(){
28289 this.loading.disable();
28294 onClick : function(which){
28303 ds.load({params:{start: 0, limit: this.pageSize}});
28306 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28309 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28312 var total = ds.getTotalCount();
28313 var extra = total % this.pageSize;
28314 var lastStart = extra ? (total - extra) : total-this.pageSize;
28315 ds.load({params:{start: lastStart, limit: this.pageSize}});
28318 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28324 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28325 * @param {Roo.data.Store} store The data store to unbind
28327 unbind : function(ds){
28328 ds.un("beforeload", this.beforeLoad, this);
28329 ds.un("load", this.onLoad, this);
28330 ds.un("loadexception", this.onLoadError, this);
28331 ds.un("remove", this.updateInfo, this);
28332 ds.un("add", this.updateInfo, this);
28333 this.ds = undefined;
28337 * Binds the paging toolbar to the specified {@link Roo.data.Store}
28338 * @param {Roo.data.Store} store The data store to bind
28340 bind : function(ds){
28341 ds.on("beforeload", this.beforeLoad, this);
28342 ds.on("load", this.onLoad, this);
28343 ds.on("loadexception", this.onLoadError, this);
28344 ds.on("remove", this.updateInfo, this);
28345 ds.on("add", this.updateInfo, this);
28356 * @class Roo.bootstrap.MessageBar
28357 * @extends Roo.bootstrap.Component
28358 * Bootstrap MessageBar class
28359 * @cfg {String} html contents of the MessageBar
28360 * @cfg {String} weight (info | success | warning | danger) default info
28361 * @cfg {String} beforeClass insert the bar before the given class
28362 * @cfg {Boolean} closable (true | false) default false
28363 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28366 * Create a new Element
28367 * @param {Object} config The config object
28370 Roo.bootstrap.MessageBar = function(config){
28371 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28374 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
28380 beforeClass: 'bootstrap-sticky-wrap',
28382 getAutoCreate : function(){
28386 cls: 'alert alert-dismissable alert-' + this.weight,
28391 html: this.html || ''
28397 cfg.cls += ' alert-messages-fixed';
28411 onRender : function(ct, position)
28413 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28416 var cfg = Roo.apply({}, this.getAutoCreate());
28420 cfg.cls += ' ' + this.cls;
28423 cfg.style = this.style;
28425 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28427 this.el.setVisibilityMode(Roo.Element.DISPLAY);
28430 this.el.select('>button.close').on('click', this.hide, this);
28436 if (!this.rendered) {
28442 this.fireEvent('show', this);
28448 if (!this.rendered) {
28454 this.fireEvent('hide', this);
28457 update : function()
28459 // var e = this.el.dom.firstChild;
28461 // if(this.closable){
28462 // e = e.nextSibling;
28465 // e.data = this.html || '';
28467 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28483 * @class Roo.bootstrap.Graph
28484 * @extends Roo.bootstrap.Component
28485 * Bootstrap Graph class
28489 @cfg {String} graphtype bar | vbar | pie
28490 @cfg {number} g_x coodinator | centre x (pie)
28491 @cfg {number} g_y coodinator | centre y (pie)
28492 @cfg {number} g_r radius (pie)
28493 @cfg {number} g_height height of the chart (respected by all elements in the set)
28494 @cfg {number} g_width width of the chart (respected by all elements in the set)
28495 @cfg {Object} title The title of the chart
28498 -opts (object) options for the chart
28500 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28501 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28503 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.
28504 o stacked (boolean) whether or not to tread values as in a stacked bar chart
28506 o stretch (boolean)
28508 -opts (object) options for the pie
28511 o startAngle (number)
28512 o endAngle (number)
28516 * Create a new Input
28517 * @param {Object} config The config object
28520 Roo.bootstrap.Graph = function(config){
28521 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28527 * The img click event for the img.
28528 * @param {Roo.EventObject} e
28534 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
28545 //g_colors: this.colors,
28552 getAutoCreate : function(){
28563 onRender : function(ct,position){
28566 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28568 if (typeof(Raphael) == 'undefined') {
28569 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28573 this.raphael = Raphael(this.el.dom);
28575 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28576 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28577 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28578 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28580 r.text(160, 10, "Single Series Chart").attr(txtattr);
28581 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28582 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28583 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28585 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28586 r.barchart(330, 10, 300, 220, data1);
28587 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28588 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28591 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28592 // r.barchart(30, 30, 560, 250, xdata, {
28593 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28594 // axis : "0 0 1 1",
28595 // axisxlabels : xdata
28596 // //yvalues : cols,
28599 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28601 // this.load(null,xdata,{
28602 // axis : "0 0 1 1",
28603 // axisxlabels : xdata
28608 load : function(graphtype,xdata,opts)
28610 this.raphael.clear();
28612 graphtype = this.graphtype;
28617 var r = this.raphael,
28618 fin = function () {
28619 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28621 fout = function () {
28622 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28624 pfin = function() {
28625 this.sector.stop();
28626 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28629 this.label[0].stop();
28630 this.label[0].attr({ r: 7.5 });
28631 this.label[1].attr({ "font-weight": 800 });
28634 pfout = function() {
28635 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28638 this.label[0].animate({ r: 5 }, 500, "bounce");
28639 this.label[1].attr({ "font-weight": 400 });
28645 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28648 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28651 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
28652 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28654 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28661 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28666 setTitle: function(o)
28671 initEvents: function() {
28674 this.el.on('click', this.onClick, this);
28678 onClick : function(e)
28680 Roo.log('img onclick');
28681 this.fireEvent('click', this, e);
28693 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28696 * @class Roo.bootstrap.dash.NumberBox
28697 * @extends Roo.bootstrap.Component
28698 * Bootstrap NumberBox class
28699 * @cfg {String} headline Box headline
28700 * @cfg {String} content Box content
28701 * @cfg {String} icon Box icon
28702 * @cfg {String} footer Footer text
28703 * @cfg {String} fhref Footer href
28706 * Create a new NumberBox
28707 * @param {Object} config The config object
28711 Roo.bootstrap.dash.NumberBox = function(config){
28712 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28716 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
28725 getAutoCreate : function(){
28729 cls : 'small-box ',
28737 cls : 'roo-headline',
28738 html : this.headline
28742 cls : 'roo-content',
28743 html : this.content
28757 cls : 'ion ' + this.icon
28766 cls : 'small-box-footer',
28767 href : this.fhref || '#',
28771 cfg.cn.push(footer);
28778 onRender : function(ct,position){
28779 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28786 setHeadline: function (value)
28788 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28791 setFooter: function (value, href)
28793 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28796 this.el.select('a.small-box-footer',true).first().attr('href', href);
28801 setContent: function (value)
28803 this.el.select('.roo-content',true).first().dom.innerHTML = value;
28806 initEvents: function()
28820 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28823 * @class Roo.bootstrap.dash.TabBox
28824 * @extends Roo.bootstrap.Component
28825 * Bootstrap TabBox class
28826 * @cfg {String} title Title of the TabBox
28827 * @cfg {String} icon Icon of the TabBox
28828 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28829 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28832 * Create a new TabBox
28833 * @param {Object} config The config object
28837 Roo.bootstrap.dash.TabBox = function(config){
28838 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28843 * When a pane is added
28844 * @param {Roo.bootstrap.dash.TabPane} pane
28848 * @event activatepane
28849 * When a pane is activated
28850 * @param {Roo.bootstrap.dash.TabPane} pane
28852 "activatepane" : true
28860 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28865 tabScrollable : false,
28867 getChildContainer : function()
28869 return this.el.select('.tab-content', true).first();
28872 getAutoCreate : function(){
28876 cls: 'pull-left header',
28884 cls: 'fa ' + this.icon
28890 cls: 'nav nav-tabs pull-right',
28896 if(this.tabScrollable){
28903 cls: 'nav nav-tabs pull-right',
28914 cls: 'nav-tabs-custom',
28919 cls: 'tab-content no-padding',
28927 initEvents : function()
28929 //Roo.log('add add pane handler');
28930 this.on('addpane', this.onAddPane, this);
28933 * Updates the box title
28934 * @param {String} html to set the title to.
28936 setTitle : function(value)
28938 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28940 onAddPane : function(pane)
28942 this.panes.push(pane);
28943 //Roo.log('addpane');
28945 // tabs are rendere left to right..
28946 if(!this.showtabs){
28950 var ctr = this.el.select('.nav-tabs', true).first();
28953 var existing = ctr.select('.nav-tab',true);
28954 var qty = existing.getCount();;
28957 var tab = ctr.createChild({
28959 cls : 'nav-tab' + (qty ? '' : ' active'),
28967 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28970 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28972 pane.el.addClass('active');
28977 onTabClick : function(ev,un,ob,pane)
28979 //Roo.log('tab - prev default');
28980 ev.preventDefault();
28983 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28984 pane.tab.addClass('active');
28985 //Roo.log(pane.title);
28986 this.getChildContainer().select('.tab-pane',true).removeClass('active');
28987 // technically we should have a deactivate event.. but maybe add later.
28988 // and it should not de-activate the selected tab...
28989 this.fireEvent('activatepane', pane);
28990 pane.el.addClass('active');
28991 pane.fireEvent('activate');
28996 getActivePane : function()
28999 Roo.each(this.panes, function(p) {
29000 if(p.el.hasClass('active')){
29021 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29023 * @class Roo.bootstrap.TabPane
29024 * @extends Roo.bootstrap.Component
29025 * Bootstrap TabPane class
29026 * @cfg {Boolean} active (false | true) Default false
29027 * @cfg {String} title title of panel
29031 * Create a new TabPane
29032 * @param {Object} config The config object
29035 Roo.bootstrap.dash.TabPane = function(config){
29036 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29042 * When a pane is activated
29043 * @param {Roo.bootstrap.dash.TabPane} pane
29050 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
29055 // the tabBox that this is attached to.
29058 getAutoCreate : function()
29066 cfg.cls += ' active';
29071 initEvents : function()
29073 //Roo.log('trigger add pane handler');
29074 this.parent().fireEvent('addpane', this)
29078 * Updates the tab title
29079 * @param {String} html to set the title to.
29081 setTitle: function(str)
29087 this.tab.select('a', true).first().dom.innerHTML = str;
29104 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29107 * @class Roo.bootstrap.menu.Menu
29108 * @extends Roo.bootstrap.Component
29109 * Bootstrap Menu class - container for Menu
29110 * @cfg {String} html Text of the menu
29111 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
29112 * @cfg {String} icon Font awesome icon
29113 * @cfg {String} pos Menu align to (top | bottom) default bottom
29117 * Create a new Menu
29118 * @param {Object} config The config object
29122 Roo.bootstrap.menu.Menu = function(config){
29123 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
29127 * @event beforeshow
29128 * Fires before this menu is displayed
29129 * @param {Roo.bootstrap.menu.Menu} this
29133 * @event beforehide
29134 * Fires before this menu is hidden
29135 * @param {Roo.bootstrap.menu.Menu} this
29140 * Fires after this menu is displayed
29141 * @param {Roo.bootstrap.menu.Menu} this
29146 * Fires after this menu is hidden
29147 * @param {Roo.bootstrap.menu.Menu} this
29152 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
29153 * @param {Roo.bootstrap.menu.Menu} this
29154 * @param {Roo.EventObject} e
29161 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
29165 weight : 'default',
29170 getChildContainer : function() {
29171 if(this.isSubMenu){
29175 return this.el.select('ul.dropdown-menu', true).first();
29178 getAutoCreate : function()
29183 cls : 'roo-menu-text',
29191 cls : 'fa ' + this.icon
29202 cls : 'dropdown-button btn btn-' + this.weight,
29207 cls : 'dropdown-toggle btn btn-' + this.weight,
29217 cls : 'dropdown-menu'
29223 if(this.pos == 'top'){
29224 cfg.cls += ' dropup';
29227 if(this.isSubMenu){
29230 cls : 'dropdown-menu'
29237 onRender : function(ct, position)
29239 this.isSubMenu = ct.hasClass('dropdown-submenu');
29241 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
29244 initEvents : function()
29246 if(this.isSubMenu){
29250 this.hidden = true;
29252 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
29253 this.triggerEl.on('click', this.onTriggerPress, this);
29255 this.buttonEl = this.el.select('button.dropdown-button', true).first();
29256 this.buttonEl.on('click', this.onClick, this);
29262 if(this.isSubMenu){
29266 return this.el.select('ul.dropdown-menu', true).first();
29269 onClick : function(e)
29271 this.fireEvent("click", this, e);
29274 onTriggerPress : function(e)
29276 if (this.isVisible()) {
29283 isVisible : function(){
29284 return !this.hidden;
29289 this.fireEvent("beforeshow", this);
29291 this.hidden = false;
29292 this.el.addClass('open');
29294 Roo.get(document).on("mouseup", this.onMouseUp, this);
29296 this.fireEvent("show", this);
29303 this.fireEvent("beforehide", this);
29305 this.hidden = true;
29306 this.el.removeClass('open');
29308 Roo.get(document).un("mouseup", this.onMouseUp);
29310 this.fireEvent("hide", this);
29313 onMouseUp : function()
29327 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29330 * @class Roo.bootstrap.menu.Item
29331 * @extends Roo.bootstrap.Component
29332 * Bootstrap MenuItem class
29333 * @cfg {Boolean} submenu (true | false) default false
29334 * @cfg {String} html text of the item
29335 * @cfg {String} href the link
29336 * @cfg {Boolean} disable (true | false) default false
29337 * @cfg {Boolean} preventDefault (true | false) default true
29338 * @cfg {String} icon Font awesome icon
29339 * @cfg {String} pos Submenu align to (left | right) default right
29343 * Create a new Item
29344 * @param {Object} config The config object
29348 Roo.bootstrap.menu.Item = function(config){
29349 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
29353 * Fires when the mouse is hovering over this menu
29354 * @param {Roo.bootstrap.menu.Item} this
29355 * @param {Roo.EventObject} e
29360 * Fires when the mouse exits this menu
29361 * @param {Roo.bootstrap.menu.Item} this
29362 * @param {Roo.EventObject} e
29368 * The raw click event for the entire grid.
29369 * @param {Roo.EventObject} e
29375 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
29380 preventDefault: true,
29385 getAutoCreate : function()
29390 cls : 'roo-menu-item-text',
29398 cls : 'fa ' + this.icon
29407 href : this.href || '#',
29414 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
29418 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
29420 if(this.pos == 'left'){
29421 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
29428 initEvents : function()
29430 this.el.on('mouseover', this.onMouseOver, this);
29431 this.el.on('mouseout', this.onMouseOut, this);
29433 this.el.select('a', true).first().on('click', this.onClick, this);
29437 onClick : function(e)
29439 if(this.preventDefault){
29440 e.preventDefault();
29443 this.fireEvent("click", this, e);
29446 onMouseOver : function(e)
29448 if(this.submenu && this.pos == 'left'){
29449 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29452 this.fireEvent("mouseover", this, e);
29455 onMouseOut : function(e)
29457 this.fireEvent("mouseout", this, e);
29469 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29472 * @class Roo.bootstrap.menu.Separator
29473 * @extends Roo.bootstrap.Component
29474 * Bootstrap Separator class
29477 * Create a new Separator
29478 * @param {Object} config The config object
29482 Roo.bootstrap.menu.Separator = function(config){
29483 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29486 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
29488 getAutoCreate : function(){
29491 cls: 'dropdown-divider divider'
29509 * @class Roo.bootstrap.Tooltip
29510 * Bootstrap Tooltip class
29511 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29512 * to determine which dom element triggers the tooltip.
29514 * It needs to add support for additional attributes like tooltip-position
29517 * Create a new Toolti
29518 * @param {Object} config The config object
29521 Roo.bootstrap.Tooltip = function(config){
29522 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29524 this.alignment = Roo.bootstrap.Tooltip.alignment;
29526 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29527 this.alignment = config.alignment;
29532 Roo.apply(Roo.bootstrap.Tooltip, {
29534 * @function init initialize tooltip monitoring.
29538 currentTip : false,
29539 currentRegion : false,
29545 Roo.get(document).on('mouseover', this.enter ,this);
29546 Roo.get(document).on('mouseout', this.leave, this);
29549 this.currentTip = new Roo.bootstrap.Tooltip();
29552 enter : function(ev)
29554 var dom = ev.getTarget();
29556 //Roo.log(['enter',dom]);
29557 var el = Roo.fly(dom);
29558 if (this.currentEl) {
29560 //Roo.log(this.currentEl);
29561 //Roo.log(this.currentEl.contains(dom));
29562 if (this.currentEl == el) {
29565 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29571 if (this.currentTip.el) {
29572 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29576 if(!el || el.dom == document){
29582 if (!el.attr('tooltip')) {
29583 pel = el.findParent("[tooltip]");
29585 bindEl = Roo.get(pel);
29591 // you can not look for children, as if el is the body.. then everythign is the child..
29592 if (!pel && !el.attr('tooltip')) { //
29593 if (!el.select("[tooltip]").elements.length) {
29596 // is the mouse over this child...?
29597 bindEl = el.select("[tooltip]").first();
29598 var xy = ev.getXY();
29599 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29600 //Roo.log("not in region.");
29603 //Roo.log("child element over..");
29606 this.currentEl = el;
29607 this.currentTip.bind(bindEl);
29608 this.currentRegion = Roo.lib.Region.getRegion(dom);
29609 this.currentTip.enter();
29612 leave : function(ev)
29614 var dom = ev.getTarget();
29615 //Roo.log(['leave',dom]);
29616 if (!this.currentEl) {
29621 if (dom != this.currentEl.dom) {
29624 var xy = ev.getXY();
29625 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
29628 // only activate leave if mouse cursor is outside... bounding box..
29633 if (this.currentTip) {
29634 this.currentTip.leave();
29636 //Roo.log('clear currentEl');
29637 this.currentEl = false;
29642 'left' : ['r-l', [-2,0], 'right'],
29643 'right' : ['l-r', [2,0], 'left'],
29644 'bottom' : ['t-b', [0,2], 'top'],
29645 'top' : [ 'b-t', [0,-2], 'bottom']
29651 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
29656 delay : null, // can be { show : 300 , hide: 500}
29660 hoverState : null, //???
29662 placement : 'bottom',
29666 getAutoCreate : function(){
29673 cls : 'tooltip-arrow arrow'
29676 cls : 'tooltip-inner'
29683 bind : function(el)
29688 initEvents : function()
29690 this.arrowEl = this.el.select('.arrow', true).first();
29691 this.innerEl = this.el.select('.tooltip-inner', true).first();
29694 enter : function () {
29696 if (this.timeout != null) {
29697 clearTimeout(this.timeout);
29700 this.hoverState = 'in';
29701 //Roo.log("enter - show");
29702 if (!this.delay || !this.delay.show) {
29707 this.timeout = setTimeout(function () {
29708 if (_t.hoverState == 'in') {
29711 }, this.delay.show);
29715 clearTimeout(this.timeout);
29717 this.hoverState = 'out';
29718 if (!this.delay || !this.delay.hide) {
29724 this.timeout = setTimeout(function () {
29725 //Roo.log("leave - timeout");
29727 if (_t.hoverState == 'out') {
29729 Roo.bootstrap.Tooltip.currentEl = false;
29734 show : function (msg)
29737 this.render(document.body);
29740 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29742 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29744 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29746 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29747 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29749 var placement = typeof this.placement == 'function' ?
29750 this.placement.call(this, this.el, on_el) :
29753 var autoToken = /\s?auto?\s?/i;
29754 var autoPlace = autoToken.test(placement);
29756 placement = placement.replace(autoToken, '') || 'top';
29760 //this.el.setXY([0,0]);
29762 //this.el.dom.style.display='block';
29764 //this.el.appendTo(on_el);
29766 var p = this.getPosition();
29767 var box = this.el.getBox();
29773 var align = this.alignment[placement];
29775 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29777 if(placement == 'top' || placement == 'bottom'){
29779 placement = 'right';
29782 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29783 placement = 'left';
29786 var scroll = Roo.select('body', true).first().getScroll();
29788 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29792 align = this.alignment[placement];
29794 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29798 var elems = document.getElementsByTagName('div');
29799 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29800 for (var i = 0; i < elems.length; i++) {
29801 var zindex = Number.parseInt(
29802 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29805 if (zindex > highest) {
29812 this.el.dom.style.zIndex = highest;
29814 this.el.alignTo(this.bindEl, align[0],align[1]);
29815 //var arrow = this.el.select('.arrow',true).first();
29816 //arrow.set(align[2],
29818 this.el.addClass(placement);
29819 this.el.addClass("bs-tooltip-"+ placement);
29821 this.el.addClass('in fade show');
29823 this.hoverState = null;
29825 if (this.el.hasClass('fade')) {
29840 //this.el.setXY([0,0]);
29841 this.el.removeClass(['show', 'in']);
29857 * @class Roo.bootstrap.LocationPicker
29858 * @extends Roo.bootstrap.Component
29859 * Bootstrap LocationPicker class
29860 * @cfg {Number} latitude Position when init default 0
29861 * @cfg {Number} longitude Position when init default 0
29862 * @cfg {Number} zoom default 15
29863 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29864 * @cfg {Boolean} mapTypeControl default false
29865 * @cfg {Boolean} disableDoubleClickZoom default false
29866 * @cfg {Boolean} scrollwheel default true
29867 * @cfg {Boolean} streetViewControl default false
29868 * @cfg {Number} radius default 0
29869 * @cfg {String} locationName
29870 * @cfg {Boolean} draggable default true
29871 * @cfg {Boolean} enableAutocomplete default false
29872 * @cfg {Boolean} enableReverseGeocode default true
29873 * @cfg {String} markerTitle
29876 * Create a new LocationPicker
29877 * @param {Object} config The config object
29881 Roo.bootstrap.LocationPicker = function(config){
29883 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29888 * Fires when the picker initialized.
29889 * @param {Roo.bootstrap.LocationPicker} this
29890 * @param {Google Location} location
29894 * @event positionchanged
29895 * Fires when the picker position changed.
29896 * @param {Roo.bootstrap.LocationPicker} this
29897 * @param {Google Location} location
29899 positionchanged : true,
29902 * Fires when the map resize.
29903 * @param {Roo.bootstrap.LocationPicker} this
29908 * Fires when the map show.
29909 * @param {Roo.bootstrap.LocationPicker} this
29914 * Fires when the map hide.
29915 * @param {Roo.bootstrap.LocationPicker} this
29920 * Fires when click the map.
29921 * @param {Roo.bootstrap.LocationPicker} this
29922 * @param {Map event} e
29926 * @event mapRightClick
29927 * Fires when right click the map.
29928 * @param {Roo.bootstrap.LocationPicker} this
29929 * @param {Map event} e
29931 mapRightClick : true,
29933 * @event markerClick
29934 * Fires when click the marker.
29935 * @param {Roo.bootstrap.LocationPicker} this
29936 * @param {Map event} e
29938 markerClick : true,
29940 * @event markerRightClick
29941 * Fires when right click the marker.
29942 * @param {Roo.bootstrap.LocationPicker} this
29943 * @param {Map event} e
29945 markerRightClick : true,
29947 * @event OverlayViewDraw
29948 * Fires when OverlayView Draw
29949 * @param {Roo.bootstrap.LocationPicker} this
29951 OverlayViewDraw : true,
29953 * @event OverlayViewOnAdd
29954 * Fires when OverlayView Draw
29955 * @param {Roo.bootstrap.LocationPicker} this
29957 OverlayViewOnAdd : true,
29959 * @event OverlayViewOnRemove
29960 * Fires when OverlayView Draw
29961 * @param {Roo.bootstrap.LocationPicker} this
29963 OverlayViewOnRemove : true,
29965 * @event OverlayViewShow
29966 * Fires when OverlayView Draw
29967 * @param {Roo.bootstrap.LocationPicker} this
29968 * @param {Pixel} cpx
29970 OverlayViewShow : true,
29972 * @event OverlayViewHide
29973 * Fires when OverlayView Draw
29974 * @param {Roo.bootstrap.LocationPicker} this
29976 OverlayViewHide : true,
29978 * @event loadexception
29979 * Fires when load google lib failed.
29980 * @param {Roo.bootstrap.LocationPicker} this
29982 loadexception : true
29987 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
29989 gMapContext: false,
29995 mapTypeControl: false,
29996 disableDoubleClickZoom: false,
29998 streetViewControl: false,
30002 enableAutocomplete: false,
30003 enableReverseGeocode: true,
30006 getAutoCreate: function()
30011 cls: 'roo-location-picker'
30017 initEvents: function(ct, position)
30019 if(!this.el.getWidth() || this.isApplied()){
30023 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30028 initial: function()
30030 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30031 this.fireEvent('loadexception', this);
30035 if(!this.mapTypeId){
30036 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30039 this.gMapContext = this.GMapContext();
30041 this.initOverlayView();
30043 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30047 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30048 _this.setPosition(_this.gMapContext.marker.position);
30051 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30052 _this.fireEvent('mapClick', this, event);
30056 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30057 _this.fireEvent('mapRightClick', this, event);
30061 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30062 _this.fireEvent('markerClick', this, event);
30066 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30067 _this.fireEvent('markerRightClick', this, event);
30071 this.setPosition(this.gMapContext.location);
30073 this.fireEvent('initial', this, this.gMapContext.location);
30076 initOverlayView: function()
30080 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30084 _this.fireEvent('OverlayViewDraw', _this);
30089 _this.fireEvent('OverlayViewOnAdd', _this);
30092 onRemove: function()
30094 _this.fireEvent('OverlayViewOnRemove', _this);
30097 show: function(cpx)
30099 _this.fireEvent('OverlayViewShow', _this, cpx);
30104 _this.fireEvent('OverlayViewHide', _this);
30110 fromLatLngToContainerPixel: function(event)
30112 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30115 isApplied: function()
30117 return this.getGmapContext() == false ? false : true;
30120 getGmapContext: function()
30122 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30125 GMapContext: function()
30127 var position = new google.maps.LatLng(this.latitude, this.longitude);
30129 var _map = new google.maps.Map(this.el.dom, {
30132 mapTypeId: this.mapTypeId,
30133 mapTypeControl: this.mapTypeControl,
30134 disableDoubleClickZoom: this.disableDoubleClickZoom,
30135 scrollwheel: this.scrollwheel,
30136 streetViewControl: this.streetViewControl,
30137 locationName: this.locationName,
30138 draggable: this.draggable,
30139 enableAutocomplete: this.enableAutocomplete,
30140 enableReverseGeocode: this.enableReverseGeocode
30143 var _marker = new google.maps.Marker({
30144 position: position,
30146 title: this.markerTitle,
30147 draggable: this.draggable
30154 location: position,
30155 radius: this.radius,
30156 locationName: this.locationName,
30157 addressComponents: {
30158 formatted_address: null,
30159 addressLine1: null,
30160 addressLine2: null,
30162 streetNumber: null,
30166 stateOrProvince: null
30169 domContainer: this.el.dom,
30170 geodecoder: new google.maps.Geocoder()
30174 drawCircle: function(center, radius, options)
30176 if (this.gMapContext.circle != null) {
30177 this.gMapContext.circle.setMap(null);
30181 options = Roo.apply({}, options, {
30182 strokeColor: "#0000FF",
30183 strokeOpacity: .35,
30185 fillColor: "#0000FF",
30189 options.map = this.gMapContext.map;
30190 options.radius = radius;
30191 options.center = center;
30192 this.gMapContext.circle = new google.maps.Circle(options);
30193 return this.gMapContext.circle;
30199 setPosition: function(location)
30201 this.gMapContext.location = location;
30202 this.gMapContext.marker.setPosition(location);
30203 this.gMapContext.map.panTo(location);
30204 this.drawCircle(location, this.gMapContext.radius, {});
30208 if (this.gMapContext.settings.enableReverseGeocode) {
30209 this.gMapContext.geodecoder.geocode({
30210 latLng: this.gMapContext.location
30211 }, function(results, status) {
30213 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30214 _this.gMapContext.locationName = results[0].formatted_address;
30215 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30217 _this.fireEvent('positionchanged', this, location);
30224 this.fireEvent('positionchanged', this, location);
30229 google.maps.event.trigger(this.gMapContext.map, "resize");
30231 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30233 this.fireEvent('resize', this);
30236 setPositionByLatLng: function(latitude, longitude)
30238 this.setPosition(new google.maps.LatLng(latitude, longitude));
30241 getCurrentPosition: function()
30244 latitude: this.gMapContext.location.lat(),
30245 longitude: this.gMapContext.location.lng()
30249 getAddressName: function()
30251 return this.gMapContext.locationName;
30254 getAddressComponents: function()
30256 return this.gMapContext.addressComponents;
30259 address_component_from_google_geocode: function(address_components)
30263 for (var i = 0; i < address_components.length; i++) {
30264 var component = address_components[i];
30265 if (component.types.indexOf("postal_code") >= 0) {
30266 result.postalCode = component.short_name;
30267 } else if (component.types.indexOf("street_number") >= 0) {
30268 result.streetNumber = component.short_name;
30269 } else if (component.types.indexOf("route") >= 0) {
30270 result.streetName = component.short_name;
30271 } else if (component.types.indexOf("neighborhood") >= 0) {
30272 result.city = component.short_name;
30273 } else if (component.types.indexOf("locality") >= 0) {
30274 result.city = component.short_name;
30275 } else if (component.types.indexOf("sublocality") >= 0) {
30276 result.district = component.short_name;
30277 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30278 result.stateOrProvince = component.short_name;
30279 } else if (component.types.indexOf("country") >= 0) {
30280 result.country = component.short_name;
30284 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30285 result.addressLine2 = "";
30289 setZoomLevel: function(zoom)
30291 this.gMapContext.map.setZoom(zoom);
30304 this.fireEvent('show', this);
30315 this.fireEvent('hide', this);
30320 Roo.apply(Roo.bootstrap.LocationPicker, {
30322 OverlayView : function(map, options)
30324 options = options || {};
30331 * @class Roo.bootstrap.Alert
30332 * @extends Roo.bootstrap.Component
30333 * Bootstrap Alert class - shows an alert area box
30335 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30336 Enter a valid email address
30339 * @cfg {String} title The title of alert
30340 * @cfg {String} html The content of alert
30341 * @cfg {String} weight (success|info|warning|danger) Weight of the message
30342 * @cfg {String} fa font-awesomeicon
30343 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30344 * @cfg {Boolean} close true to show a x closer
30348 * Create a new alert
30349 * @param {Object} config The config object
30353 Roo.bootstrap.Alert = function(config){
30354 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30358 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
30364 faicon: false, // BC
30368 getAutoCreate : function()
30380 style : this.close ? '' : 'display:none'
30384 cls : 'roo-alert-icon'
30389 cls : 'roo-alert-title',
30394 cls : 'roo-alert-text',
30401 cfg.cn[0].cls += ' fa ' + this.faicon;
30404 cfg.cn[0].cls += ' fa ' + this.fa;
30408 cfg.cls += ' alert-' + this.weight;
30414 initEvents: function()
30416 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30417 this.titleEl = this.el.select('.roo-alert-title',true).first();
30418 this.iconEl = this.el.select('.roo-alert-icon',true).first();
30419 this.htmlEl = this.el.select('.roo-alert-text',true).first();
30420 if (this.seconds > 0) {
30421 this.hide.defer(this.seconds, this);
30425 * Set the Title Message HTML
30426 * @param {String} html
30428 setTitle : function(str)
30430 this.titleEl.dom.innerHTML = str;
30434 * Set the Body Message HTML
30435 * @param {String} html
30437 setHtml : function(str)
30439 this.htmlEl.dom.innerHTML = str;
30442 * Set the Weight of the alert
30443 * @param {String} (success|info|warning|danger) weight
30446 setWeight : function(weight)
30449 this.el.removeClass('alert-' + this.weight);
30452 this.weight = weight;
30454 this.el.addClass('alert-' + this.weight);
30457 * Set the Icon of the alert
30458 * @param {String} see fontawsome names (name without the 'fa-' bit)
30460 setIcon : function(icon)
30463 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30466 this.faicon = icon;
30468 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30493 * @class Roo.bootstrap.UploadCropbox
30494 * @extends Roo.bootstrap.Component
30495 * Bootstrap UploadCropbox class
30496 * @cfg {String} emptyText show when image has been loaded
30497 * @cfg {String} rotateNotify show when image too small to rotate
30498 * @cfg {Number} errorTimeout default 3000
30499 * @cfg {Number} minWidth default 300
30500 * @cfg {Number} minHeight default 300
30501 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30502 * @cfg {Boolean} isDocument (true|false) default false
30503 * @cfg {String} url action url
30504 * @cfg {String} paramName default 'imageUpload'
30505 * @cfg {String} method default POST
30506 * @cfg {Boolean} loadMask (true|false) default true
30507 * @cfg {Boolean} loadingText default 'Loading...'
30510 * Create a new UploadCropbox
30511 * @param {Object} config The config object
30514 Roo.bootstrap.UploadCropbox = function(config){
30515 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30519 * @event beforeselectfile
30520 * Fire before select file
30521 * @param {Roo.bootstrap.UploadCropbox} this
30523 "beforeselectfile" : true,
30526 * Fire after initEvent
30527 * @param {Roo.bootstrap.UploadCropbox} this
30532 * Fire after initEvent
30533 * @param {Roo.bootstrap.UploadCropbox} this
30534 * @param {String} data
30539 * Fire when preparing the file data
30540 * @param {Roo.bootstrap.UploadCropbox} this
30541 * @param {Object} file
30546 * Fire when get exception
30547 * @param {Roo.bootstrap.UploadCropbox} this
30548 * @param {XMLHttpRequest} xhr
30550 "exception" : true,
30552 * @event beforeloadcanvas
30553 * Fire before load the canvas
30554 * @param {Roo.bootstrap.UploadCropbox} this
30555 * @param {String} src
30557 "beforeloadcanvas" : true,
30560 * Fire when trash image
30561 * @param {Roo.bootstrap.UploadCropbox} this
30566 * Fire when download the image
30567 * @param {Roo.bootstrap.UploadCropbox} this
30571 * @event footerbuttonclick
30572 * Fire when footerbuttonclick
30573 * @param {Roo.bootstrap.UploadCropbox} this
30574 * @param {String} type
30576 "footerbuttonclick" : true,
30580 * @param {Roo.bootstrap.UploadCropbox} this
30585 * Fire when rotate the image
30586 * @param {Roo.bootstrap.UploadCropbox} this
30587 * @param {String} pos
30592 * Fire when inspect the file
30593 * @param {Roo.bootstrap.UploadCropbox} this
30594 * @param {Object} file
30599 * Fire when xhr upload the file
30600 * @param {Roo.bootstrap.UploadCropbox} this
30601 * @param {Object} data
30606 * Fire when arrange the file data
30607 * @param {Roo.bootstrap.UploadCropbox} this
30608 * @param {Object} formData
30613 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30616 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
30618 emptyText : 'Click to upload image',
30619 rotateNotify : 'Image is too small to rotate',
30620 errorTimeout : 3000,
30634 cropType : 'image/jpeg',
30636 canvasLoaded : false,
30637 isDocument : false,
30639 paramName : 'imageUpload',
30641 loadingText : 'Loading...',
30644 getAutoCreate : function()
30648 cls : 'roo-upload-cropbox',
30652 cls : 'roo-upload-cropbox-selector',
30657 cls : 'roo-upload-cropbox-body',
30658 style : 'cursor:pointer',
30662 cls : 'roo-upload-cropbox-preview'
30666 cls : 'roo-upload-cropbox-thumb'
30670 cls : 'roo-upload-cropbox-empty-notify',
30671 html : this.emptyText
30675 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30676 html : this.rotateNotify
30682 cls : 'roo-upload-cropbox-footer',
30685 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30695 onRender : function(ct, position)
30697 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30699 if (this.buttons.length) {
30701 Roo.each(this.buttons, function(bb) {
30703 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30705 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30711 this.maskEl = this.el;
30715 initEvents : function()
30717 this.urlAPI = (window.createObjectURL && window) ||
30718 (window.URL && URL.revokeObjectURL && URL) ||
30719 (window.webkitURL && webkitURL);
30721 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30722 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30724 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30725 this.selectorEl.hide();
30727 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30728 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30730 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30731 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30732 this.thumbEl.hide();
30734 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30735 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30737 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30738 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30739 this.errorEl.hide();
30741 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30742 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30743 this.footerEl.hide();
30745 this.setThumbBoxSize();
30751 this.fireEvent('initial', this);
30758 window.addEventListener("resize", function() { _this.resize(); } );
30760 this.bodyEl.on('click', this.beforeSelectFile, this);
30763 this.bodyEl.on('touchstart', this.onTouchStart, this);
30764 this.bodyEl.on('touchmove', this.onTouchMove, this);
30765 this.bodyEl.on('touchend', this.onTouchEnd, this);
30769 this.bodyEl.on('mousedown', this.onMouseDown, this);
30770 this.bodyEl.on('mousemove', this.onMouseMove, this);
30771 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30772 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30773 Roo.get(document).on('mouseup', this.onMouseUp, this);
30776 this.selectorEl.on('change', this.onFileSelected, this);
30782 this.baseScale = 1;
30784 this.baseRotate = 1;
30785 this.dragable = false;
30786 this.pinching = false;
30789 this.cropData = false;
30790 this.notifyEl.dom.innerHTML = this.emptyText;
30792 this.selectorEl.dom.value = '';
30796 resize : function()
30798 if(this.fireEvent('resize', this) != false){
30799 this.setThumbBoxPosition();
30800 this.setCanvasPosition();
30804 onFooterButtonClick : function(e, el, o, type)
30807 case 'rotate-left' :
30808 this.onRotateLeft(e);
30810 case 'rotate-right' :
30811 this.onRotateRight(e);
30814 this.beforeSelectFile(e);
30829 this.fireEvent('footerbuttonclick', this, type);
30832 beforeSelectFile : function(e)
30834 e.preventDefault();
30836 if(this.fireEvent('beforeselectfile', this) != false){
30837 this.selectorEl.dom.click();
30841 onFileSelected : function(e)
30843 e.preventDefault();
30845 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30849 var file = this.selectorEl.dom.files[0];
30851 if(this.fireEvent('inspect', this, file) != false){
30852 this.prepare(file);
30857 trash : function(e)
30859 this.fireEvent('trash', this);
30862 download : function(e)
30864 this.fireEvent('download', this);
30867 loadCanvas : function(src)
30869 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30873 this.imageEl = document.createElement('img');
30877 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30879 this.imageEl.src = src;
30883 onLoadCanvas : function()
30885 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30886 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30888 this.bodyEl.un('click', this.beforeSelectFile, this);
30890 this.notifyEl.hide();
30891 this.thumbEl.show();
30892 this.footerEl.show();
30894 this.baseRotateLevel();
30896 if(this.isDocument){
30897 this.setThumbBoxSize();
30900 this.setThumbBoxPosition();
30902 this.baseScaleLevel();
30908 this.canvasLoaded = true;
30911 this.maskEl.unmask();
30916 setCanvasPosition : function()
30918 if(!this.canvasEl){
30922 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30923 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30925 this.previewEl.setLeft(pw);
30926 this.previewEl.setTop(ph);
30930 onMouseDown : function(e)
30934 this.dragable = true;
30935 this.pinching = false;
30937 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30938 this.dragable = false;
30942 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30943 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30947 onMouseMove : function(e)
30951 if(!this.canvasLoaded){
30955 if (!this.dragable){
30959 var minX = Math.ceil(this.thumbEl.getLeft(true));
30960 var minY = Math.ceil(this.thumbEl.getTop(true));
30962 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30963 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30965 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30966 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30968 x = x - this.mouseX;
30969 y = y - this.mouseY;
30971 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30972 var bgY = Math.ceil(y + this.previewEl.getTop(true));
30974 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30975 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30977 this.previewEl.setLeft(bgX);
30978 this.previewEl.setTop(bgY);
30980 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30981 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30984 onMouseUp : function(e)
30988 this.dragable = false;
30991 onMouseWheel : function(e)
30995 this.startScale = this.scale;
30997 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30999 if(!this.zoomable()){
31000 this.scale = this.startScale;
31009 zoomable : function()
31011 var minScale = this.thumbEl.getWidth() / this.minWidth;
31013 if(this.minWidth < this.minHeight){
31014 minScale = this.thumbEl.getHeight() / this.minHeight;
31017 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31018 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31022 (this.rotate == 0 || this.rotate == 180) &&
31024 width > this.imageEl.OriginWidth ||
31025 height > this.imageEl.OriginHeight ||
31026 (width < this.minWidth && height < this.minHeight)
31034 (this.rotate == 90 || this.rotate == 270) &&
31036 width > this.imageEl.OriginWidth ||
31037 height > this.imageEl.OriginHeight ||
31038 (width < this.minHeight && height < this.minWidth)
31045 !this.isDocument &&
31046 (this.rotate == 0 || this.rotate == 180) &&
31048 width < this.minWidth ||
31049 width > this.imageEl.OriginWidth ||
31050 height < this.minHeight ||
31051 height > this.imageEl.OriginHeight
31058 !this.isDocument &&
31059 (this.rotate == 90 || this.rotate == 270) &&
31061 width < this.minHeight ||
31062 width > this.imageEl.OriginWidth ||
31063 height < this.minWidth ||
31064 height > this.imageEl.OriginHeight
31074 onRotateLeft : function(e)
31076 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31078 var minScale = this.thumbEl.getWidth() / this.minWidth;
31080 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31081 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31083 this.startScale = this.scale;
31085 while (this.getScaleLevel() < minScale){
31087 this.scale = this.scale + 1;
31089 if(!this.zoomable()){
31094 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31095 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31100 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31107 this.scale = this.startScale;
31109 this.onRotateFail();
31114 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31116 if(this.isDocument){
31117 this.setThumbBoxSize();
31118 this.setThumbBoxPosition();
31119 this.setCanvasPosition();
31124 this.fireEvent('rotate', this, 'left');
31128 onRotateRight : function(e)
31130 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31132 var minScale = this.thumbEl.getWidth() / this.minWidth;
31134 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31135 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31137 this.startScale = this.scale;
31139 while (this.getScaleLevel() < minScale){
31141 this.scale = this.scale + 1;
31143 if(!this.zoomable()){
31148 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31149 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31154 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31161 this.scale = this.startScale;
31163 this.onRotateFail();
31168 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31170 if(this.isDocument){
31171 this.setThumbBoxSize();
31172 this.setThumbBoxPosition();
31173 this.setCanvasPosition();
31178 this.fireEvent('rotate', this, 'right');
31181 onRotateFail : function()
31183 this.errorEl.show(true);
31187 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31192 this.previewEl.dom.innerHTML = '';
31194 var canvasEl = document.createElement("canvas");
31196 var contextEl = canvasEl.getContext("2d");
31198 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31199 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31200 var center = this.imageEl.OriginWidth / 2;
31202 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31203 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31204 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31205 center = this.imageEl.OriginHeight / 2;
31208 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31210 contextEl.translate(center, center);
31211 contextEl.rotate(this.rotate * Math.PI / 180);
31213 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31215 this.canvasEl = document.createElement("canvas");
31217 this.contextEl = this.canvasEl.getContext("2d");
31219 switch (this.rotate) {
31222 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31223 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31225 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31230 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31231 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31233 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31234 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);
31238 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31243 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31244 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31246 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31247 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);
31251 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);
31256 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31257 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31259 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31260 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31264 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);
31271 this.previewEl.appendChild(this.canvasEl);
31273 this.setCanvasPosition();
31278 if(!this.canvasLoaded){
31282 var imageCanvas = document.createElement("canvas");
31284 var imageContext = imageCanvas.getContext("2d");
31286 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31287 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31289 var center = imageCanvas.width / 2;
31291 imageContext.translate(center, center);
31293 imageContext.rotate(this.rotate * Math.PI / 180);
31295 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31297 var canvas = document.createElement("canvas");
31299 var context = canvas.getContext("2d");
31301 canvas.width = this.minWidth;
31302 canvas.height = this.minHeight;
31304 switch (this.rotate) {
31307 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31308 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31310 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31311 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31313 var targetWidth = this.minWidth - 2 * x;
31314 var targetHeight = this.minHeight - 2 * y;
31318 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31319 scale = targetWidth / width;
31322 if(x > 0 && y == 0){
31323 scale = targetHeight / height;
31326 if(x > 0 && y > 0){
31327 scale = targetWidth / width;
31329 if(width < height){
31330 scale = targetHeight / height;
31334 context.scale(scale, scale);
31336 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31337 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31339 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31340 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31342 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31347 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31348 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31350 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31351 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31353 var targetWidth = this.minWidth - 2 * x;
31354 var targetHeight = this.minHeight - 2 * y;
31358 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31359 scale = targetWidth / width;
31362 if(x > 0 && y == 0){
31363 scale = targetHeight / height;
31366 if(x > 0 && y > 0){
31367 scale = targetWidth / width;
31369 if(width < height){
31370 scale = targetHeight / height;
31374 context.scale(scale, scale);
31376 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31377 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31379 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31380 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31382 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31384 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31389 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31390 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31392 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31393 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31395 var targetWidth = this.minWidth - 2 * x;
31396 var targetHeight = this.minHeight - 2 * y;
31400 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31401 scale = targetWidth / width;
31404 if(x > 0 && y == 0){
31405 scale = targetHeight / height;
31408 if(x > 0 && y > 0){
31409 scale = targetWidth / width;
31411 if(width < height){
31412 scale = targetHeight / height;
31416 context.scale(scale, scale);
31418 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31419 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31421 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31422 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31424 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31425 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31427 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31432 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31433 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31435 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31436 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31438 var targetWidth = this.minWidth - 2 * x;
31439 var targetHeight = this.minHeight - 2 * y;
31443 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31444 scale = targetWidth / width;
31447 if(x > 0 && y == 0){
31448 scale = targetHeight / height;
31451 if(x > 0 && y > 0){
31452 scale = targetWidth / width;
31454 if(width < height){
31455 scale = targetHeight / height;
31459 context.scale(scale, scale);
31461 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31462 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31464 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31465 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31467 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31469 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31476 this.cropData = canvas.toDataURL(this.cropType);
31478 if(this.fireEvent('crop', this, this.cropData) !== false){
31479 this.process(this.file, this.cropData);
31486 setThumbBoxSize : function()
31490 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31491 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31492 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31494 this.minWidth = width;
31495 this.minHeight = height;
31497 if(this.rotate == 90 || this.rotate == 270){
31498 this.minWidth = height;
31499 this.minHeight = width;
31504 width = Math.ceil(this.minWidth * height / this.minHeight);
31506 if(this.minWidth > this.minHeight){
31508 height = Math.ceil(this.minHeight * width / this.minWidth);
31511 this.thumbEl.setStyle({
31512 width : width + 'px',
31513 height : height + 'px'
31520 setThumbBoxPosition : function()
31522 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31523 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31525 this.thumbEl.setLeft(x);
31526 this.thumbEl.setTop(y);
31530 baseRotateLevel : function()
31532 this.baseRotate = 1;
31535 typeof(this.exif) != 'undefined' &&
31536 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31537 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31539 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31542 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31546 baseScaleLevel : function()
31550 if(this.isDocument){
31552 if(this.baseRotate == 6 || this.baseRotate == 8){
31554 height = this.thumbEl.getHeight();
31555 this.baseScale = height / this.imageEl.OriginWidth;
31557 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31558 width = this.thumbEl.getWidth();
31559 this.baseScale = width / this.imageEl.OriginHeight;
31565 height = this.thumbEl.getHeight();
31566 this.baseScale = height / this.imageEl.OriginHeight;
31568 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31569 width = this.thumbEl.getWidth();
31570 this.baseScale = width / this.imageEl.OriginWidth;
31576 if(this.baseRotate == 6 || this.baseRotate == 8){
31578 width = this.thumbEl.getHeight();
31579 this.baseScale = width / this.imageEl.OriginHeight;
31581 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31582 height = this.thumbEl.getWidth();
31583 this.baseScale = height / this.imageEl.OriginHeight;
31586 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31587 height = this.thumbEl.getWidth();
31588 this.baseScale = height / this.imageEl.OriginHeight;
31590 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31591 width = this.thumbEl.getHeight();
31592 this.baseScale = width / this.imageEl.OriginWidth;
31599 width = this.thumbEl.getWidth();
31600 this.baseScale = width / this.imageEl.OriginWidth;
31602 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31603 height = this.thumbEl.getHeight();
31604 this.baseScale = height / this.imageEl.OriginHeight;
31607 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31609 height = this.thumbEl.getHeight();
31610 this.baseScale = height / this.imageEl.OriginHeight;
31612 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31613 width = this.thumbEl.getWidth();
31614 this.baseScale = width / this.imageEl.OriginWidth;
31622 getScaleLevel : function()
31624 return this.baseScale * Math.pow(1.1, this.scale);
31627 onTouchStart : function(e)
31629 if(!this.canvasLoaded){
31630 this.beforeSelectFile(e);
31634 var touches = e.browserEvent.touches;
31640 if(touches.length == 1){
31641 this.onMouseDown(e);
31645 if(touches.length != 2){
31651 for(var i = 0, finger; finger = touches[i]; i++){
31652 coords.push(finger.pageX, finger.pageY);
31655 var x = Math.pow(coords[0] - coords[2], 2);
31656 var y = Math.pow(coords[1] - coords[3], 2);
31658 this.startDistance = Math.sqrt(x + y);
31660 this.startScale = this.scale;
31662 this.pinching = true;
31663 this.dragable = false;
31667 onTouchMove : function(e)
31669 if(!this.pinching && !this.dragable){
31673 var touches = e.browserEvent.touches;
31680 this.onMouseMove(e);
31686 for(var i = 0, finger; finger = touches[i]; i++){
31687 coords.push(finger.pageX, finger.pageY);
31690 var x = Math.pow(coords[0] - coords[2], 2);
31691 var y = Math.pow(coords[1] - coords[3], 2);
31693 this.endDistance = Math.sqrt(x + y);
31695 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31697 if(!this.zoomable()){
31698 this.scale = this.startScale;
31706 onTouchEnd : function(e)
31708 this.pinching = false;
31709 this.dragable = false;
31713 process : function(file, crop)
31716 this.maskEl.mask(this.loadingText);
31719 this.xhr = new XMLHttpRequest();
31721 file.xhr = this.xhr;
31723 this.xhr.open(this.method, this.url, true);
31726 "Accept": "application/json",
31727 "Cache-Control": "no-cache",
31728 "X-Requested-With": "XMLHttpRequest"
31731 for (var headerName in headers) {
31732 var headerValue = headers[headerName];
31734 this.xhr.setRequestHeader(headerName, headerValue);
31740 this.xhr.onload = function()
31742 _this.xhrOnLoad(_this.xhr);
31745 this.xhr.onerror = function()
31747 _this.xhrOnError(_this.xhr);
31750 var formData = new FormData();
31752 formData.append('returnHTML', 'NO');
31755 formData.append('crop', crop);
31758 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31759 formData.append(this.paramName, file, file.name);
31762 if(typeof(file.filename) != 'undefined'){
31763 formData.append('filename', file.filename);
31766 if(typeof(file.mimetype) != 'undefined'){
31767 formData.append('mimetype', file.mimetype);
31770 if(this.fireEvent('arrange', this, formData) != false){
31771 this.xhr.send(formData);
31775 xhrOnLoad : function(xhr)
31778 this.maskEl.unmask();
31781 if (xhr.readyState !== 4) {
31782 this.fireEvent('exception', this, xhr);
31786 var response = Roo.decode(xhr.responseText);
31788 if(!response.success){
31789 this.fireEvent('exception', this, xhr);
31793 var response = Roo.decode(xhr.responseText);
31795 this.fireEvent('upload', this, response);
31799 xhrOnError : function()
31802 this.maskEl.unmask();
31805 Roo.log('xhr on error');
31807 var response = Roo.decode(xhr.responseText);
31813 prepare : function(file)
31816 this.maskEl.mask(this.loadingText);
31822 if(typeof(file) === 'string'){
31823 this.loadCanvas(file);
31827 if(!file || !this.urlAPI){
31832 this.cropType = file.type;
31836 if(this.fireEvent('prepare', this, this.file) != false){
31838 var reader = new FileReader();
31840 reader.onload = function (e) {
31841 if (e.target.error) {
31842 Roo.log(e.target.error);
31846 var buffer = e.target.result,
31847 dataView = new DataView(buffer),
31849 maxOffset = dataView.byteLength - 4,
31853 if (dataView.getUint16(0) === 0xffd8) {
31854 while (offset < maxOffset) {
31855 markerBytes = dataView.getUint16(offset);
31857 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31858 markerLength = dataView.getUint16(offset + 2) + 2;
31859 if (offset + markerLength > dataView.byteLength) {
31860 Roo.log('Invalid meta data: Invalid segment size.');
31864 if(markerBytes == 0xffe1){
31865 _this.parseExifData(
31872 offset += markerLength;
31882 var url = _this.urlAPI.createObjectURL(_this.file);
31884 _this.loadCanvas(url);
31889 reader.readAsArrayBuffer(this.file);
31895 parseExifData : function(dataView, offset, length)
31897 var tiffOffset = offset + 10,
31901 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31902 // No Exif data, might be XMP data instead
31906 // Check for the ASCII code for "Exif" (0x45786966):
31907 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31908 // No Exif data, might be XMP data instead
31911 if (tiffOffset + 8 > dataView.byteLength) {
31912 Roo.log('Invalid Exif data: Invalid segment size.');
31915 // Check for the two null bytes:
31916 if (dataView.getUint16(offset + 8) !== 0x0000) {
31917 Roo.log('Invalid Exif data: Missing byte alignment offset.');
31920 // Check the byte alignment:
31921 switch (dataView.getUint16(tiffOffset)) {
31923 littleEndian = true;
31926 littleEndian = false;
31929 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31932 // Check for the TIFF tag marker (0x002A):
31933 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31934 Roo.log('Invalid Exif data: Missing TIFF marker.');
31937 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31938 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31940 this.parseExifTags(
31943 tiffOffset + dirOffset,
31948 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31953 if (dirOffset + 6 > dataView.byteLength) {
31954 Roo.log('Invalid Exif data: Invalid directory offset.');
31957 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31958 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31959 if (dirEndOffset + 4 > dataView.byteLength) {
31960 Roo.log('Invalid Exif data: Invalid directory size.');
31963 for (i = 0; i < tagsNumber; i += 1) {
31967 dirOffset + 2 + 12 * i, // tag offset
31971 // Return the offset to the next directory:
31972 return dataView.getUint32(dirEndOffset, littleEndian);
31975 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
31977 var tag = dataView.getUint16(offset, littleEndian);
31979 this.exif[tag] = this.getExifValue(
31983 dataView.getUint16(offset + 2, littleEndian), // tag type
31984 dataView.getUint32(offset + 4, littleEndian), // tag length
31989 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31991 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
32000 Roo.log('Invalid Exif data: Invalid tag type.');
32004 tagSize = tagType.size * length;
32005 // Determine if the value is contained in the dataOffset bytes,
32006 // or if the value at the dataOffset is a pointer to the actual data:
32007 dataOffset = tagSize > 4 ?
32008 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32009 if (dataOffset + tagSize > dataView.byteLength) {
32010 Roo.log('Invalid Exif data: Invalid data offset.');
32013 if (length === 1) {
32014 return tagType.getValue(dataView, dataOffset, littleEndian);
32017 for (i = 0; i < length; i += 1) {
32018 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32021 if (tagType.ascii) {
32023 // Concatenate the chars:
32024 for (i = 0; i < values.length; i += 1) {
32026 // Ignore the terminating NULL byte(s):
32027 if (c === '\u0000') {
32039 Roo.apply(Roo.bootstrap.UploadCropbox, {
32041 'Orientation': 0x0112
32045 1: 0, //'top-left',
32047 3: 180, //'bottom-right',
32048 // 4: 'bottom-left',
32050 6: 90, //'right-top',
32051 // 7: 'right-bottom',
32052 8: 270 //'left-bottom'
32056 // byte, 8-bit unsigned int:
32058 getValue: function (dataView, dataOffset) {
32059 return dataView.getUint8(dataOffset);
32063 // ascii, 8-bit byte:
32065 getValue: function (dataView, dataOffset) {
32066 return String.fromCharCode(dataView.getUint8(dataOffset));
32071 // short, 16 bit int:
32073 getValue: function (dataView, dataOffset, littleEndian) {
32074 return dataView.getUint16(dataOffset, littleEndian);
32078 // long, 32 bit int:
32080 getValue: function (dataView, dataOffset, littleEndian) {
32081 return dataView.getUint32(dataOffset, littleEndian);
32085 // rational = two long values, first is numerator, second is denominator:
32087 getValue: function (dataView, dataOffset, littleEndian) {
32088 return dataView.getUint32(dataOffset, littleEndian) /
32089 dataView.getUint32(dataOffset + 4, littleEndian);
32093 // slong, 32 bit signed int:
32095 getValue: function (dataView, dataOffset, littleEndian) {
32096 return dataView.getInt32(dataOffset, littleEndian);
32100 // srational, two slongs, first is numerator, second is denominator:
32102 getValue: function (dataView, dataOffset, littleEndian) {
32103 return dataView.getInt32(dataOffset, littleEndian) /
32104 dataView.getInt32(dataOffset + 4, littleEndian);
32114 cls : 'btn-group roo-upload-cropbox-rotate-left',
32115 action : 'rotate-left',
32119 cls : 'btn btn-default',
32120 html : '<i class="fa fa-undo"></i>'
32126 cls : 'btn-group roo-upload-cropbox-picture',
32127 action : 'picture',
32131 cls : 'btn btn-default',
32132 html : '<i class="fa fa-picture-o"></i>'
32138 cls : 'btn-group roo-upload-cropbox-rotate-right',
32139 action : 'rotate-right',
32143 cls : 'btn btn-default',
32144 html : '<i class="fa fa-repeat"></i>'
32152 cls : 'btn-group roo-upload-cropbox-rotate-left',
32153 action : 'rotate-left',
32157 cls : 'btn btn-default',
32158 html : '<i class="fa fa-undo"></i>'
32164 cls : 'btn-group roo-upload-cropbox-download',
32165 action : 'download',
32169 cls : 'btn btn-default',
32170 html : '<i class="fa fa-download"></i>'
32176 cls : 'btn-group roo-upload-cropbox-crop',
32181 cls : 'btn btn-default',
32182 html : '<i class="fa fa-crop"></i>'
32188 cls : 'btn-group roo-upload-cropbox-trash',
32193 cls : 'btn btn-default',
32194 html : '<i class="fa fa-trash"></i>'
32200 cls : 'btn-group roo-upload-cropbox-rotate-right',
32201 action : 'rotate-right',
32205 cls : 'btn btn-default',
32206 html : '<i class="fa fa-repeat"></i>'
32214 cls : 'btn-group roo-upload-cropbox-rotate-left',
32215 action : 'rotate-left',
32219 cls : 'btn btn-default',
32220 html : '<i class="fa fa-undo"></i>'
32226 cls : 'btn-group roo-upload-cropbox-rotate-right',
32227 action : 'rotate-right',
32231 cls : 'btn btn-default',
32232 html : '<i class="fa fa-repeat"></i>'
32245 * @class Roo.bootstrap.DocumentManager
32246 * @extends Roo.bootstrap.Component
32247 * Bootstrap DocumentManager class
32248 * @cfg {String} paramName default 'imageUpload'
32249 * @cfg {String} toolTipName default 'filename'
32250 * @cfg {String} method default POST
32251 * @cfg {String} url action url
32252 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32253 * @cfg {Boolean} multiple multiple upload default true
32254 * @cfg {Number} thumbSize default 300
32255 * @cfg {String} fieldLabel
32256 * @cfg {Number} labelWidth default 4
32257 * @cfg {String} labelAlign (left|top) default left
32258 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32259 * @cfg {Number} labellg set the width of label (1-12)
32260 * @cfg {Number} labelmd set the width of label (1-12)
32261 * @cfg {Number} labelsm set the width of label (1-12)
32262 * @cfg {Number} labelxs set the width of label (1-12)
32265 * Create a new DocumentManager
32266 * @param {Object} config The config object
32269 Roo.bootstrap.DocumentManager = function(config){
32270 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32273 this.delegates = [];
32278 * Fire when initial the DocumentManager
32279 * @param {Roo.bootstrap.DocumentManager} this
32284 * inspect selected file
32285 * @param {Roo.bootstrap.DocumentManager} this
32286 * @param {File} file
32291 * Fire when xhr load exception
32292 * @param {Roo.bootstrap.DocumentManager} this
32293 * @param {XMLHttpRequest} xhr
32295 "exception" : true,
32297 * @event afterupload
32298 * Fire when xhr load exception
32299 * @param {Roo.bootstrap.DocumentManager} this
32300 * @param {XMLHttpRequest} xhr
32302 "afterupload" : true,
32305 * prepare the form data
32306 * @param {Roo.bootstrap.DocumentManager} this
32307 * @param {Object} formData
32312 * Fire when remove the file
32313 * @param {Roo.bootstrap.DocumentManager} this
32314 * @param {Object} file
32319 * Fire after refresh the file
32320 * @param {Roo.bootstrap.DocumentManager} this
32325 * Fire after click the image
32326 * @param {Roo.bootstrap.DocumentManager} this
32327 * @param {Object} file
32332 * Fire when upload a image and editable set to true
32333 * @param {Roo.bootstrap.DocumentManager} this
32334 * @param {Object} file
32338 * @event beforeselectfile
32339 * Fire before select file
32340 * @param {Roo.bootstrap.DocumentManager} this
32342 "beforeselectfile" : true,
32345 * Fire before process file
32346 * @param {Roo.bootstrap.DocumentManager} this
32347 * @param {Object} file
32351 * @event previewrendered
32352 * Fire when preview rendered
32353 * @param {Roo.bootstrap.DocumentManager} this
32354 * @param {Object} file
32356 "previewrendered" : true,
32359 "previewResize" : true
32364 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
32373 paramName : 'imageUpload',
32374 toolTipName : 'filename',
32377 labelAlign : 'left',
32387 getAutoCreate : function()
32389 var managerWidget = {
32391 cls : 'roo-document-manager',
32395 cls : 'roo-document-manager-selector',
32400 cls : 'roo-document-manager-uploader',
32404 cls : 'roo-document-manager-upload-btn',
32405 html : '<i class="fa fa-plus"></i>'
32416 cls : 'column col-md-12',
32421 if(this.fieldLabel.length){
32426 cls : 'column col-md-12',
32427 html : this.fieldLabel
32431 cls : 'column col-md-12',
32436 if(this.labelAlign == 'left'){
32441 html : this.fieldLabel
32450 if(this.labelWidth > 12){
32451 content[0].style = "width: " + this.labelWidth + 'px';
32454 if(this.labelWidth < 13 && this.labelmd == 0){
32455 this.labelmd = this.labelWidth;
32458 if(this.labellg > 0){
32459 content[0].cls += ' col-lg-' + this.labellg;
32460 content[1].cls += ' col-lg-' + (12 - this.labellg);
32463 if(this.labelmd > 0){
32464 content[0].cls += ' col-md-' + this.labelmd;
32465 content[1].cls += ' col-md-' + (12 - this.labelmd);
32468 if(this.labelsm > 0){
32469 content[0].cls += ' col-sm-' + this.labelsm;
32470 content[1].cls += ' col-sm-' + (12 - this.labelsm);
32473 if(this.labelxs > 0){
32474 content[0].cls += ' col-xs-' + this.labelxs;
32475 content[1].cls += ' col-xs-' + (12 - this.labelxs);
32483 cls : 'row clearfix',
32491 initEvents : function()
32493 this.managerEl = this.el.select('.roo-document-manager', true).first();
32494 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32496 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32497 this.selectorEl.hide();
32500 this.selectorEl.attr('multiple', 'multiple');
32503 this.selectorEl.on('change', this.onFileSelected, this);
32505 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32506 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32508 this.uploader.on('click', this.onUploaderClick, this);
32510 this.renderProgressDialog();
32514 window.addEventListener("resize", function() { _this.refresh(); } );
32516 this.fireEvent('initial', this);
32519 renderProgressDialog : function()
32523 this.progressDialog = new Roo.bootstrap.Modal({
32524 cls : 'roo-document-manager-progress-dialog',
32525 allow_close : false,
32536 btnclick : function() {
32537 _this.uploadCancel();
32543 this.progressDialog.render(Roo.get(document.body));
32545 this.progress = new Roo.bootstrap.Progress({
32546 cls : 'roo-document-manager-progress',
32551 this.progress.render(this.progressDialog.getChildContainer());
32553 this.progressBar = new Roo.bootstrap.ProgressBar({
32554 cls : 'roo-document-manager-progress-bar',
32557 aria_valuemax : 12,
32561 this.progressBar.render(this.progress.getChildContainer());
32564 onUploaderClick : function(e)
32566 e.preventDefault();
32568 if(this.fireEvent('beforeselectfile', this) != false){
32569 this.selectorEl.dom.click();
32574 onFileSelected : function(e)
32576 e.preventDefault();
32578 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32582 Roo.each(this.selectorEl.dom.files, function(file){
32583 if(this.fireEvent('inspect', this, file) != false){
32584 this.files.push(file);
32594 this.selectorEl.dom.value = '';
32596 if(!this.files || !this.files.length){
32600 if(this.boxes > 0 && this.files.length > this.boxes){
32601 this.files = this.files.slice(0, this.boxes);
32604 this.uploader.show();
32606 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32607 this.uploader.hide();
32616 Roo.each(this.files, function(file){
32618 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32619 var f = this.renderPreview(file);
32624 if(file.type.indexOf('image') != -1){
32625 this.delegates.push(
32627 _this.process(file);
32628 }).createDelegate(this)
32636 _this.process(file);
32637 }).createDelegate(this)
32642 this.files = files;
32644 this.delegates = this.delegates.concat(docs);
32646 if(!this.delegates.length){
32651 this.progressBar.aria_valuemax = this.delegates.length;
32658 arrange : function()
32660 if(!this.delegates.length){
32661 this.progressDialog.hide();
32666 var delegate = this.delegates.shift();
32668 this.progressDialog.show();
32670 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32672 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32677 refresh : function()
32679 this.uploader.show();
32681 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32682 this.uploader.hide();
32685 Roo.isTouch ? this.closable(false) : this.closable(true);
32687 this.fireEvent('refresh', this);
32690 onRemove : function(e, el, o)
32692 e.preventDefault();
32694 this.fireEvent('remove', this, o);
32698 remove : function(o)
32702 Roo.each(this.files, function(file){
32703 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32712 this.files = files;
32719 Roo.each(this.files, function(file){
32724 file.target.remove();
32733 onClick : function(e, el, o)
32735 e.preventDefault();
32737 this.fireEvent('click', this, o);
32741 closable : function(closable)
32743 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32745 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32757 xhrOnLoad : function(xhr)
32759 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32763 if (xhr.readyState !== 4) {
32765 this.fireEvent('exception', this, xhr);
32769 var response = Roo.decode(xhr.responseText);
32771 if(!response.success){
32773 this.fireEvent('exception', this, xhr);
32777 var file = this.renderPreview(response.data);
32779 this.files.push(file);
32783 this.fireEvent('afterupload', this, xhr);
32787 xhrOnError : function(xhr)
32789 Roo.log('xhr on error');
32791 var response = Roo.decode(xhr.responseText);
32798 process : function(file)
32800 if(this.fireEvent('process', this, file) !== false){
32801 if(this.editable && file.type.indexOf('image') != -1){
32802 this.fireEvent('edit', this, file);
32806 this.uploadStart(file, false);
32813 uploadStart : function(file, crop)
32815 this.xhr = new XMLHttpRequest();
32817 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32822 file.xhr = this.xhr;
32824 this.managerEl.createChild({
32826 cls : 'roo-document-manager-loading',
32830 tooltip : file.name,
32831 cls : 'roo-document-manager-thumb',
32832 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32838 this.xhr.open(this.method, this.url, true);
32841 "Accept": "application/json",
32842 "Cache-Control": "no-cache",
32843 "X-Requested-With": "XMLHttpRequest"
32846 for (var headerName in headers) {
32847 var headerValue = headers[headerName];
32849 this.xhr.setRequestHeader(headerName, headerValue);
32855 this.xhr.onload = function()
32857 _this.xhrOnLoad(_this.xhr);
32860 this.xhr.onerror = function()
32862 _this.xhrOnError(_this.xhr);
32865 var formData = new FormData();
32867 formData.append('returnHTML', 'NO');
32870 formData.append('crop', crop);
32873 formData.append(this.paramName, file, file.name);
32880 if(this.fireEvent('prepare', this, formData, options) != false){
32882 if(options.manually){
32886 this.xhr.send(formData);
32890 this.uploadCancel();
32893 uploadCancel : function()
32899 this.delegates = [];
32901 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32908 renderPreview : function(file)
32910 if(typeof(file.target) != 'undefined' && file.target){
32914 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32916 var previewEl = this.managerEl.createChild({
32918 cls : 'roo-document-manager-preview',
32922 tooltip : file[this.toolTipName],
32923 cls : 'roo-document-manager-thumb',
32924 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32929 html : '<i class="fa fa-times-circle"></i>'
32934 var close = previewEl.select('button.close', true).first();
32936 close.on('click', this.onRemove, this, file);
32938 file.target = previewEl;
32940 var image = previewEl.select('img', true).first();
32944 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32946 image.on('click', this.onClick, this, file);
32948 this.fireEvent('previewrendered', this, file);
32954 onPreviewLoad : function(file, image)
32956 if(typeof(file.target) == 'undefined' || !file.target){
32960 var width = image.dom.naturalWidth || image.dom.width;
32961 var height = image.dom.naturalHeight || image.dom.height;
32963 if(!this.previewResize) {
32967 if(width > height){
32968 file.target.addClass('wide');
32972 file.target.addClass('tall');
32977 uploadFromSource : function(file, crop)
32979 this.xhr = new XMLHttpRequest();
32981 this.managerEl.createChild({
32983 cls : 'roo-document-manager-loading',
32987 tooltip : file.name,
32988 cls : 'roo-document-manager-thumb',
32989 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32995 this.xhr.open(this.method, this.url, true);
32998 "Accept": "application/json",
32999 "Cache-Control": "no-cache",
33000 "X-Requested-With": "XMLHttpRequest"
33003 for (var headerName in headers) {
33004 var headerValue = headers[headerName];
33006 this.xhr.setRequestHeader(headerName, headerValue);
33012 this.xhr.onload = function()
33014 _this.xhrOnLoad(_this.xhr);
33017 this.xhr.onerror = function()
33019 _this.xhrOnError(_this.xhr);
33022 var formData = new FormData();
33024 formData.append('returnHTML', 'NO');
33026 formData.append('crop', crop);
33028 if(typeof(file.filename) != 'undefined'){
33029 formData.append('filename', file.filename);
33032 if(typeof(file.mimetype) != 'undefined'){
33033 formData.append('mimetype', file.mimetype);
33038 if(this.fireEvent('prepare', this, formData) != false){
33039 this.xhr.send(formData);
33049 * @class Roo.bootstrap.DocumentViewer
33050 * @extends Roo.bootstrap.Component
33051 * Bootstrap DocumentViewer class
33052 * @cfg {Boolean} showDownload (true|false) show download button (default true)
33053 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33056 * Create a new DocumentViewer
33057 * @param {Object} config The config object
33060 Roo.bootstrap.DocumentViewer = function(config){
33061 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33066 * Fire after initEvent
33067 * @param {Roo.bootstrap.DocumentViewer} this
33073 * @param {Roo.bootstrap.DocumentViewer} this
33078 * Fire after download button
33079 * @param {Roo.bootstrap.DocumentViewer} this
33084 * Fire after trash button
33085 * @param {Roo.bootstrap.DocumentViewer} this
33092 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
33094 showDownload : true,
33098 getAutoCreate : function()
33102 cls : 'roo-document-viewer',
33106 cls : 'roo-document-viewer-body',
33110 cls : 'roo-document-viewer-thumb',
33114 cls : 'roo-document-viewer-image'
33122 cls : 'roo-document-viewer-footer',
33125 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33129 cls : 'btn-group roo-document-viewer-download',
33133 cls : 'btn btn-default',
33134 html : '<i class="fa fa-download"></i>'
33140 cls : 'btn-group roo-document-viewer-trash',
33144 cls : 'btn btn-default',
33145 html : '<i class="fa fa-trash"></i>'
33158 initEvents : function()
33160 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33161 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33163 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33164 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33166 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33167 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33169 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33170 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33172 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33173 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33175 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33176 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33178 this.bodyEl.on('click', this.onClick, this);
33179 this.downloadBtn.on('click', this.onDownload, this);
33180 this.trashBtn.on('click', this.onTrash, this);
33182 this.downloadBtn.hide();
33183 this.trashBtn.hide();
33185 if(this.showDownload){
33186 this.downloadBtn.show();
33189 if(this.showTrash){
33190 this.trashBtn.show();
33193 if(!this.showDownload && !this.showTrash) {
33194 this.footerEl.hide();
33199 initial : function()
33201 this.fireEvent('initial', this);
33205 onClick : function(e)
33207 e.preventDefault();
33209 this.fireEvent('click', this);
33212 onDownload : function(e)
33214 e.preventDefault();
33216 this.fireEvent('download', this);
33219 onTrash : function(e)
33221 e.preventDefault();
33223 this.fireEvent('trash', this);
33235 * @class Roo.bootstrap.NavProgressBar
33236 * @extends Roo.bootstrap.Component
33237 * Bootstrap NavProgressBar class
33240 * Create a new nav progress bar
33241 * @param {Object} config The config object
33244 Roo.bootstrap.NavProgressBar = function(config){
33245 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
33247 this.bullets = this.bullets || [];
33249 // Roo.bootstrap.NavProgressBar.register(this);
33253 * Fires when the active item changes
33254 * @param {Roo.bootstrap.NavProgressBar} this
33255 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
33256 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
33263 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
33268 getAutoCreate : function()
33270 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
33274 cls : 'roo-navigation-bar-group',
33278 cls : 'roo-navigation-top-bar'
33282 cls : 'roo-navigation-bullets-bar',
33286 cls : 'roo-navigation-bar'
33293 cls : 'roo-navigation-bottom-bar'
33303 initEvents: function()
33308 onRender : function(ct, position)
33310 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33312 if(this.bullets.length){
33313 Roo.each(this.bullets, function(b){
33322 addItem : function(cfg)
33324 var item = new Roo.bootstrap.NavProgressItem(cfg);
33326 item.parentId = this.id;
33327 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
33330 var top = new Roo.bootstrap.Element({
33332 cls : 'roo-navigation-bar-text'
33335 var bottom = new Roo.bootstrap.Element({
33337 cls : 'roo-navigation-bar-text'
33340 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
33341 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
33343 var topText = new Roo.bootstrap.Element({
33345 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
33348 var bottomText = new Roo.bootstrap.Element({
33350 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
33353 topText.onRender(top.el, null);
33354 bottomText.onRender(bottom.el, null);
33357 item.bottomEl = bottom;
33360 this.barItems.push(item);
33365 getActive : function()
33367 var active = false;
33369 Roo.each(this.barItems, function(v){
33371 if (!v.isActive()) {
33383 setActiveItem : function(item)
33387 Roo.each(this.barItems, function(v){
33388 if (v.rid == item.rid) {
33392 if (v.isActive()) {
33393 v.setActive(false);
33398 item.setActive(true);
33400 this.fireEvent('changed', this, item, prev);
33403 getBarItem: function(rid)
33407 Roo.each(this.barItems, function(e) {
33408 if (e.rid != rid) {
33419 indexOfItem : function(item)
33423 Roo.each(this.barItems, function(v, i){
33425 if (v.rid != item.rid) {
33436 setActiveNext : function()
33438 var i = this.indexOfItem(this.getActive());
33440 if (i > this.barItems.length) {
33444 this.setActiveItem(this.barItems[i+1]);
33447 setActivePrev : function()
33449 var i = this.indexOfItem(this.getActive());
33455 this.setActiveItem(this.barItems[i-1]);
33458 format : function()
33460 if(!this.barItems.length){
33464 var width = 100 / this.barItems.length;
33466 Roo.each(this.barItems, function(i){
33467 i.el.setStyle('width', width + '%');
33468 i.topEl.el.setStyle('width', width + '%');
33469 i.bottomEl.el.setStyle('width', width + '%');
33478 * Nav Progress Item
33483 * @class Roo.bootstrap.NavProgressItem
33484 * @extends Roo.bootstrap.Component
33485 * Bootstrap NavProgressItem class
33486 * @cfg {String} rid the reference id
33487 * @cfg {Boolean} active (true|false) Is item active default false
33488 * @cfg {Boolean} disabled (true|false) Is item active default false
33489 * @cfg {String} html
33490 * @cfg {String} position (top|bottom) text position default bottom
33491 * @cfg {String} icon show icon instead of number
33494 * Create a new NavProgressItem
33495 * @param {Object} config The config object
33497 Roo.bootstrap.NavProgressItem = function(config){
33498 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33503 * The raw click event for the entire grid.
33504 * @param {Roo.bootstrap.NavProgressItem} this
33505 * @param {Roo.EventObject} e
33512 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
33518 position : 'bottom',
33521 getAutoCreate : function()
33523 var iconCls = 'roo-navigation-bar-item-icon';
33525 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33529 cls: 'roo-navigation-bar-item',
33539 cfg.cls += ' active';
33542 cfg.cls += ' disabled';
33548 disable : function()
33550 this.setDisabled(true);
33553 enable : function()
33555 this.setDisabled(false);
33558 initEvents: function()
33560 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33562 this.iconEl.on('click', this.onClick, this);
33565 onClick : function(e)
33567 e.preventDefault();
33573 if(this.fireEvent('click', this, e) === false){
33577 this.parent().setActiveItem(this);
33580 isActive: function ()
33582 return this.active;
33585 setActive : function(state)
33587 if(this.active == state){
33591 this.active = state;
33594 this.el.addClass('active');
33598 this.el.removeClass('active');
33603 setDisabled : function(state)
33605 if(this.disabled == state){
33609 this.disabled = state;
33612 this.el.addClass('disabled');
33616 this.el.removeClass('disabled');
33619 tooltipEl : function()
33621 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33634 * @class Roo.bootstrap.FieldLabel
33635 * @extends Roo.bootstrap.Component
33636 * Bootstrap FieldLabel class
33637 * @cfg {String} html contents of the element
33638 * @cfg {String} tag tag of the element default label
33639 * @cfg {String} cls class of the element
33640 * @cfg {String} target label target
33641 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33642 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33643 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33644 * @cfg {String} iconTooltip default "This field is required"
33645 * @cfg {String} indicatorpos (left|right) default left
33648 * Create a new FieldLabel
33649 * @param {Object} config The config object
33652 Roo.bootstrap.FieldLabel = function(config){
33653 Roo.bootstrap.Element.superclass.constructor.call(this, config);
33658 * Fires after the field has been marked as invalid.
33659 * @param {Roo.form.FieldLabel} this
33660 * @param {String} msg The validation message
33665 * Fires after the field has been validated with no errors.
33666 * @param {Roo.form.FieldLabel} this
33672 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
33679 invalidClass : 'has-warning',
33680 validClass : 'has-success',
33681 iconTooltip : 'This field is required',
33682 indicatorpos : 'left',
33684 getAutoCreate : function(){
33687 if (!this.allowBlank) {
33693 cls : 'roo-bootstrap-field-label ' + this.cls,
33698 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33699 tooltip : this.iconTooltip
33708 if(this.indicatorpos == 'right'){
33711 cls : 'roo-bootstrap-field-label ' + this.cls,
33720 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33721 tooltip : this.iconTooltip
33730 initEvents: function()
33732 Roo.bootstrap.Element.superclass.initEvents.call(this);
33734 this.indicator = this.indicatorEl();
33736 if(this.indicator){
33737 this.indicator.removeClass('visible');
33738 this.indicator.addClass('invisible');
33741 Roo.bootstrap.FieldLabel.register(this);
33744 indicatorEl : function()
33746 var indicator = this.el.select('i.roo-required-indicator',true).first();
33757 * Mark this field as valid
33759 markValid : function()
33761 if(this.indicator){
33762 this.indicator.removeClass('visible');
33763 this.indicator.addClass('invisible');
33765 if (Roo.bootstrap.version == 3) {
33766 this.el.removeClass(this.invalidClass);
33767 this.el.addClass(this.validClass);
33769 this.el.removeClass('is-invalid');
33770 this.el.addClass('is-valid');
33774 this.fireEvent('valid', this);
33778 * Mark this field as invalid
33779 * @param {String} msg The validation message
33781 markInvalid : function(msg)
33783 if(this.indicator){
33784 this.indicator.removeClass('invisible');
33785 this.indicator.addClass('visible');
33787 if (Roo.bootstrap.version == 3) {
33788 this.el.removeClass(this.validClass);
33789 this.el.addClass(this.invalidClass);
33791 this.el.removeClass('is-valid');
33792 this.el.addClass('is-invalid');
33796 this.fireEvent('invalid', this, msg);
33802 Roo.apply(Roo.bootstrap.FieldLabel, {
33807 * register a FieldLabel Group
33808 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33810 register : function(label)
33812 if(this.groups.hasOwnProperty(label.target)){
33816 this.groups[label.target] = label;
33820 * fetch a FieldLabel Group based on the target
33821 * @param {string} target
33822 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33824 get: function(target) {
33825 if (typeof(this.groups[target]) == 'undefined') {
33829 return this.groups[target] ;
33838 * page DateSplitField.
33844 * @class Roo.bootstrap.DateSplitField
33845 * @extends Roo.bootstrap.Component
33846 * Bootstrap DateSplitField class
33847 * @cfg {string} fieldLabel - the label associated
33848 * @cfg {Number} labelWidth set the width of label (0-12)
33849 * @cfg {String} labelAlign (top|left)
33850 * @cfg {Boolean} dayAllowBlank (true|false) default false
33851 * @cfg {Boolean} monthAllowBlank (true|false) default false
33852 * @cfg {Boolean} yearAllowBlank (true|false) default false
33853 * @cfg {string} dayPlaceholder
33854 * @cfg {string} monthPlaceholder
33855 * @cfg {string} yearPlaceholder
33856 * @cfg {string} dayFormat default 'd'
33857 * @cfg {string} monthFormat default 'm'
33858 * @cfg {string} yearFormat default 'Y'
33859 * @cfg {Number} labellg set the width of label (1-12)
33860 * @cfg {Number} labelmd set the width of label (1-12)
33861 * @cfg {Number} labelsm set the width of label (1-12)
33862 * @cfg {Number} labelxs set the width of label (1-12)
33866 * Create a new DateSplitField
33867 * @param {Object} config The config object
33870 Roo.bootstrap.DateSplitField = function(config){
33871 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33877 * getting the data of years
33878 * @param {Roo.bootstrap.DateSplitField} this
33879 * @param {Object} years
33884 * getting the data of days
33885 * @param {Roo.bootstrap.DateSplitField} this
33886 * @param {Object} days
33891 * Fires after the field has been marked as invalid.
33892 * @param {Roo.form.Field} this
33893 * @param {String} msg The validation message
33898 * Fires after the field has been validated with no errors.
33899 * @param {Roo.form.Field} this
33905 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33908 labelAlign : 'top',
33910 dayAllowBlank : false,
33911 monthAllowBlank : false,
33912 yearAllowBlank : false,
33913 dayPlaceholder : '',
33914 monthPlaceholder : '',
33915 yearPlaceholder : '',
33919 isFormField : true,
33925 getAutoCreate : function()
33929 cls : 'row roo-date-split-field-group',
33934 cls : 'form-hidden-field roo-date-split-field-group-value',
33940 var labelCls = 'col-md-12';
33941 var contentCls = 'col-md-4';
33943 if(this.fieldLabel){
33947 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33951 html : this.fieldLabel
33956 if(this.labelAlign == 'left'){
33958 if(this.labelWidth > 12){
33959 label.style = "width: " + this.labelWidth + 'px';
33962 if(this.labelWidth < 13 && this.labelmd == 0){
33963 this.labelmd = this.labelWidth;
33966 if(this.labellg > 0){
33967 labelCls = ' col-lg-' + this.labellg;
33968 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33971 if(this.labelmd > 0){
33972 labelCls = ' col-md-' + this.labelmd;
33973 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33976 if(this.labelsm > 0){
33977 labelCls = ' col-sm-' + this.labelsm;
33978 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33981 if(this.labelxs > 0){
33982 labelCls = ' col-xs-' + this.labelxs;
33983 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33987 label.cls += ' ' + labelCls;
33989 cfg.cn.push(label);
33992 Roo.each(['day', 'month', 'year'], function(t){
33995 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
34002 inputEl: function ()
34004 return this.el.select('.roo-date-split-field-group-value', true).first();
34007 onRender : function(ct, position)
34011 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
34013 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
34015 this.dayField = new Roo.bootstrap.ComboBox({
34016 allowBlank : this.dayAllowBlank,
34017 alwaysQuery : true,
34018 displayField : 'value',
34021 forceSelection : true,
34023 placeholder : this.dayPlaceholder,
34024 selectOnFocus : true,
34025 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34026 triggerAction : 'all',
34028 valueField : 'value',
34029 store : new Roo.data.SimpleStore({
34030 data : (function() {
34032 _this.fireEvent('days', _this, days);
34035 fields : [ 'value' ]
34038 select : function (_self, record, index)
34040 _this.setValue(_this.getValue());
34045 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
34047 this.monthField = new Roo.bootstrap.MonthField({
34048 after : '<i class=\"fa fa-calendar\"></i>',
34049 allowBlank : this.monthAllowBlank,
34050 placeholder : this.monthPlaceholder,
34053 render : function (_self)
34055 this.el.select('span.input-group-addon', true).first().on('click', function(e){
34056 e.preventDefault();
34060 select : function (_self, oldvalue, newvalue)
34062 _this.setValue(_this.getValue());
34067 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
34069 this.yearField = new Roo.bootstrap.ComboBox({
34070 allowBlank : this.yearAllowBlank,
34071 alwaysQuery : true,
34072 displayField : 'value',
34075 forceSelection : true,
34077 placeholder : this.yearPlaceholder,
34078 selectOnFocus : true,
34079 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34080 triggerAction : 'all',
34082 valueField : 'value',
34083 store : new Roo.data.SimpleStore({
34084 data : (function() {
34086 _this.fireEvent('years', _this, years);
34089 fields : [ 'value' ]
34092 select : function (_self, record, index)
34094 _this.setValue(_this.getValue());
34099 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
34102 setValue : function(v, format)
34104 this.inputEl.dom.value = v;
34106 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
34108 var d = Date.parseDate(v, f);
34115 this.setDay(d.format(this.dayFormat));
34116 this.setMonth(d.format(this.monthFormat));
34117 this.setYear(d.format(this.yearFormat));
34124 setDay : function(v)
34126 this.dayField.setValue(v);
34127 this.inputEl.dom.value = this.getValue();
34132 setMonth : function(v)
34134 this.monthField.setValue(v, true);
34135 this.inputEl.dom.value = this.getValue();
34140 setYear : function(v)
34142 this.yearField.setValue(v);
34143 this.inputEl.dom.value = this.getValue();
34148 getDay : function()
34150 return this.dayField.getValue();
34153 getMonth : function()
34155 return this.monthField.getValue();
34158 getYear : function()
34160 return this.yearField.getValue();
34163 getValue : function()
34165 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
34167 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
34177 this.inputEl.dom.value = '';
34182 validate : function()
34184 var d = this.dayField.validate();
34185 var m = this.monthField.validate();
34186 var y = this.yearField.validate();
34191 (!this.dayAllowBlank && !d) ||
34192 (!this.monthAllowBlank && !m) ||
34193 (!this.yearAllowBlank && !y)
34198 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
34207 this.markInvalid();
34212 markValid : function()
34215 var label = this.el.select('label', true).first();
34216 var icon = this.el.select('i.fa-star', true).first();
34222 this.fireEvent('valid', this);
34226 * Mark this field as invalid
34227 * @param {String} msg The validation message
34229 markInvalid : function(msg)
34232 var label = this.el.select('label', true).first();
34233 var icon = this.el.select('i.fa-star', true).first();
34235 if(label && !icon){
34236 this.el.select('.roo-date-split-field-label', true).createChild({
34238 cls : 'text-danger fa fa-lg fa-star',
34239 tooltip : 'This field is required',
34240 style : 'margin-right:5px;'
34244 this.fireEvent('invalid', this, msg);
34247 clearInvalid : function()
34249 var label = this.el.select('label', true).first();
34250 var icon = this.el.select('i.fa-star', true).first();
34256 this.fireEvent('valid', this);
34259 getName: function()
34269 * http://masonry.desandro.com
34271 * The idea is to render all the bricks based on vertical width...
34273 * The original code extends 'outlayer' - we might need to use that....
34279 * @class Roo.bootstrap.LayoutMasonry
34280 * @extends Roo.bootstrap.Component
34281 * Bootstrap Layout Masonry class
34284 * Create a new Element
34285 * @param {Object} config The config object
34288 Roo.bootstrap.LayoutMasonry = function(config){
34290 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34294 Roo.bootstrap.LayoutMasonry.register(this);
34300 * Fire after layout the items
34301 * @param {Roo.bootstrap.LayoutMasonry} this
34302 * @param {Roo.EventObject} e
34309 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
34312 * @cfg {Boolean} isLayoutInstant = no animation?
34314 isLayoutInstant : false, // needed?
34317 * @cfg {Number} boxWidth width of the columns
34322 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
34327 * @cfg {Number} padWidth padding below box..
34332 * @cfg {Number} gutter gutter width..
34337 * @cfg {Number} maxCols maximum number of columns
34343 * @cfg {Boolean} isAutoInitial defalut true
34345 isAutoInitial : true,
34350 * @cfg {Boolean} isHorizontal defalut false
34352 isHorizontal : false,
34354 currentSize : null,
34360 bricks: null, //CompositeElement
34364 _isLayoutInited : false,
34366 // isAlternative : false, // only use for vertical layout...
34369 * @cfg {Number} alternativePadWidth padding below box..
34371 alternativePadWidth : 50,
34373 selectedBrick : [],
34375 getAutoCreate : function(){
34377 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34381 cls: 'blog-masonary-wrapper ' + this.cls,
34383 cls : 'mas-boxes masonary'
34390 getChildContainer: function( )
34392 if (this.boxesEl) {
34393 return this.boxesEl;
34396 this.boxesEl = this.el.select('.mas-boxes').first();
34398 return this.boxesEl;
34402 initEvents : function()
34406 if(this.isAutoInitial){
34407 Roo.log('hook children rendered');
34408 this.on('childrenrendered', function() {
34409 Roo.log('children rendered');
34415 initial : function()
34417 this.selectedBrick = [];
34419 this.currentSize = this.el.getBox(true);
34421 Roo.EventManager.onWindowResize(this.resize, this);
34423 if(!this.isAutoInitial){
34431 //this.layout.defer(500,this);
34435 resize : function()
34437 var cs = this.el.getBox(true);
34440 this.currentSize.width == cs.width &&
34441 this.currentSize.x == cs.x &&
34442 this.currentSize.height == cs.height &&
34443 this.currentSize.y == cs.y
34445 Roo.log("no change in with or X or Y");
34449 this.currentSize = cs;
34455 layout : function()
34457 this._resetLayout();
34459 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34461 this.layoutItems( isInstant );
34463 this._isLayoutInited = true;
34465 this.fireEvent('layout', this);
34469 _resetLayout : function()
34471 if(this.isHorizontal){
34472 this.horizontalMeasureColumns();
34476 this.verticalMeasureColumns();
34480 verticalMeasureColumns : function()
34482 this.getContainerWidth();
34484 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34485 // this.colWidth = Math.floor(this.containerWidth * 0.8);
34489 var boxWidth = this.boxWidth + this.padWidth;
34491 if(this.containerWidth < this.boxWidth){
34492 boxWidth = this.containerWidth
34495 var containerWidth = this.containerWidth;
34497 var cols = Math.floor(containerWidth / boxWidth);
34499 this.cols = Math.max( cols, 1 );
34501 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34503 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34505 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34507 this.colWidth = boxWidth + avail - this.padWidth;
34509 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34510 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
34513 horizontalMeasureColumns : function()
34515 this.getContainerWidth();
34517 var boxWidth = this.boxWidth;
34519 if(this.containerWidth < boxWidth){
34520 boxWidth = this.containerWidth;
34523 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34525 this.el.setHeight(boxWidth);
34529 getContainerWidth : function()
34531 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34534 layoutItems : function( isInstant )
34536 Roo.log(this.bricks);
34538 var items = Roo.apply([], this.bricks);
34540 if(this.isHorizontal){
34541 this._horizontalLayoutItems( items , isInstant );
34545 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34546 // this._verticalAlternativeLayoutItems( items , isInstant );
34550 this._verticalLayoutItems( items , isInstant );
34554 _verticalLayoutItems : function ( items , isInstant)
34556 if ( !items || !items.length ) {
34561 ['xs', 'xs', 'xs', 'tall'],
34562 ['xs', 'xs', 'tall'],
34563 ['xs', 'xs', 'sm'],
34564 ['xs', 'xs', 'xs'],
34570 ['sm', 'xs', 'xs'],
34574 ['tall', 'xs', 'xs', 'xs'],
34575 ['tall', 'xs', 'xs'],
34587 Roo.each(items, function(item, k){
34589 switch (item.size) {
34590 // these layouts take up a full box,
34601 boxes.push([item]);
34624 var filterPattern = function(box, length)
34632 var pattern = box.slice(0, length);
34636 Roo.each(pattern, function(i){
34637 format.push(i.size);
34640 Roo.each(standard, function(s){
34642 if(String(s) != String(format)){
34651 if(!match && length == 1){
34656 filterPattern(box, length - 1);
34660 queue.push(pattern);
34662 box = box.slice(length, box.length);
34664 filterPattern(box, 4);
34670 Roo.each(boxes, function(box, k){
34676 if(box.length == 1){
34681 filterPattern(box, 4);
34685 this._processVerticalLayoutQueue( queue, isInstant );
34689 // _verticalAlternativeLayoutItems : function( items , isInstant )
34691 // if ( !items || !items.length ) {
34695 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
34699 _horizontalLayoutItems : function ( items , isInstant)
34701 if ( !items || !items.length || items.length < 3) {
34707 var eItems = items.slice(0, 3);
34709 items = items.slice(3, items.length);
34712 ['xs', 'xs', 'xs', 'wide'],
34713 ['xs', 'xs', 'wide'],
34714 ['xs', 'xs', 'sm'],
34715 ['xs', 'xs', 'xs'],
34721 ['sm', 'xs', 'xs'],
34725 ['wide', 'xs', 'xs', 'xs'],
34726 ['wide', 'xs', 'xs'],
34739 Roo.each(items, function(item, k){
34741 switch (item.size) {
34752 boxes.push([item]);
34776 var filterPattern = function(box, length)
34784 var pattern = box.slice(0, length);
34788 Roo.each(pattern, function(i){
34789 format.push(i.size);
34792 Roo.each(standard, function(s){
34794 if(String(s) != String(format)){
34803 if(!match && length == 1){
34808 filterPattern(box, length - 1);
34812 queue.push(pattern);
34814 box = box.slice(length, box.length);
34816 filterPattern(box, 4);
34822 Roo.each(boxes, function(box, k){
34828 if(box.length == 1){
34833 filterPattern(box, 4);
34840 var pos = this.el.getBox(true);
34844 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34846 var hit_end = false;
34848 Roo.each(queue, function(box){
34852 Roo.each(box, function(b){
34854 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34864 Roo.each(box, function(b){
34866 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34869 mx = Math.max(mx, b.x);
34873 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34877 Roo.each(box, function(b){
34879 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34893 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34896 /** Sets position of item in DOM
34897 * @param {Element} item
34898 * @param {Number} x - horizontal position
34899 * @param {Number} y - vertical position
34900 * @param {Boolean} isInstant - disables transitions
34902 _processVerticalLayoutQueue : function( queue, isInstant )
34904 var pos = this.el.getBox(true);
34909 for (var i = 0; i < this.cols; i++){
34913 Roo.each(queue, function(box, k){
34915 var col = k % this.cols;
34917 Roo.each(box, function(b,kk){
34919 b.el.position('absolute');
34921 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34922 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34924 if(b.size == 'md-left' || b.size == 'md-right'){
34925 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34926 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34929 b.el.setWidth(width);
34930 b.el.setHeight(height);
34932 b.el.select('iframe',true).setSize(width,height);
34936 for (var i = 0; i < this.cols; i++){
34938 if(maxY[i] < maxY[col]){
34943 col = Math.min(col, i);
34947 x = pos.x + col * (this.colWidth + this.padWidth);
34951 var positions = [];
34953 switch (box.length){
34955 positions = this.getVerticalOneBoxColPositions(x, y, box);
34958 positions = this.getVerticalTwoBoxColPositions(x, y, box);
34961 positions = this.getVerticalThreeBoxColPositions(x, y, box);
34964 positions = this.getVerticalFourBoxColPositions(x, y, box);
34970 Roo.each(box, function(b,kk){
34972 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34974 var sz = b.el.getSize();
34976 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34984 for (var i = 0; i < this.cols; i++){
34985 mY = Math.max(mY, maxY[i]);
34988 this.el.setHeight(mY - pos.y);
34992 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34994 // var pos = this.el.getBox(true);
34997 // var maxX = pos.right;
34999 // var maxHeight = 0;
35001 // Roo.each(items, function(item, k){
35005 // item.el.position('absolute');
35007 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
35009 // item.el.setWidth(width);
35011 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
35013 // item.el.setHeight(height);
35016 // item.el.setXY([x, y], isInstant ? false : true);
35018 // item.el.setXY([maxX - width, y], isInstant ? false : true);
35021 // y = y + height + this.alternativePadWidth;
35023 // maxHeight = maxHeight + height + this.alternativePadWidth;
35027 // this.el.setHeight(maxHeight);
35031 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
35033 var pos = this.el.getBox(true);
35038 var maxX = pos.right;
35040 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
35042 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
35044 Roo.each(queue, function(box, k){
35046 Roo.each(box, function(b, kk){
35048 b.el.position('absolute');
35050 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35051 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35053 if(b.size == 'md-left' || b.size == 'md-right'){
35054 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35055 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35058 b.el.setWidth(width);
35059 b.el.setHeight(height);
35067 var positions = [];
35069 switch (box.length){
35071 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
35074 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
35077 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
35080 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
35086 Roo.each(box, function(b,kk){
35088 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35090 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
35098 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
35100 Roo.each(eItems, function(b,k){
35102 b.size = (k == 0) ? 'sm' : 'xs';
35103 b.x = (k == 0) ? 2 : 1;
35104 b.y = (k == 0) ? 2 : 1;
35106 b.el.position('absolute');
35108 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35110 b.el.setWidth(width);
35112 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35114 b.el.setHeight(height);
35118 var positions = [];
35121 x : maxX - this.unitWidth * 2 - this.gutter,
35126 x : maxX - this.unitWidth,
35127 y : minY + (this.unitWidth + this.gutter) * 2
35131 x : maxX - this.unitWidth * 3 - this.gutter * 2,
35135 Roo.each(eItems, function(b,k){
35137 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
35143 getVerticalOneBoxColPositions : function(x, y, box)
35147 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
35149 if(box[0].size == 'md-left'){
35153 if(box[0].size == 'md-right'){
35158 x : x + (this.unitWidth + this.gutter) * rand,
35165 getVerticalTwoBoxColPositions : function(x, y, box)
35169 if(box[0].size == 'xs'){
35173 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
35177 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
35191 x : x + (this.unitWidth + this.gutter) * 2,
35192 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
35199 getVerticalThreeBoxColPositions : function(x, y, box)
35203 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35211 x : x + (this.unitWidth + this.gutter) * 1,
35216 x : x + (this.unitWidth + this.gutter) * 2,
35224 if(box[0].size == 'xs' && box[1].size == 'xs'){
35233 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
35237 x : x + (this.unitWidth + this.gutter) * 1,
35251 x : x + (this.unitWidth + this.gutter) * 2,
35256 x : x + (this.unitWidth + this.gutter) * 2,
35257 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
35264 getVerticalFourBoxColPositions : function(x, y, box)
35268 if(box[0].size == 'xs'){
35277 y : y + (this.unitHeight + this.gutter) * 1
35282 y : y + (this.unitHeight + this.gutter) * 2
35286 x : x + (this.unitWidth + this.gutter) * 1,
35300 x : x + (this.unitWidth + this.gutter) * 2,
35305 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35306 y : y + (this.unitHeight + this.gutter) * 1
35310 x : x + (this.unitWidth + this.gutter) * 2,
35311 y : y + (this.unitWidth + this.gutter) * 2
35318 getHorizontalOneBoxColPositions : function(maxX, minY, box)
35322 if(box[0].size == 'md-left'){
35324 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35331 if(box[0].size == 'md-right'){
35333 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35334 y : minY + (this.unitWidth + this.gutter) * 1
35340 var rand = Math.floor(Math.random() * (4 - box[0].y));
35343 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35344 y : minY + (this.unitWidth + this.gutter) * rand
35351 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35355 if(box[0].size == 'xs'){
35358 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35363 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35364 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35372 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35377 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35378 y : minY + (this.unitWidth + this.gutter) * 2
35385 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35389 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35392 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35397 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35398 y : minY + (this.unitWidth + this.gutter) * 1
35402 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35403 y : minY + (this.unitWidth + this.gutter) * 2
35410 if(box[0].size == 'xs' && box[1].size == 'xs'){
35413 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35418 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35423 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35424 y : minY + (this.unitWidth + this.gutter) * 1
35432 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35437 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35438 y : minY + (this.unitWidth + this.gutter) * 2
35442 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35443 y : minY + (this.unitWidth + this.gutter) * 2
35450 getHorizontalFourBoxColPositions : function(maxX, minY, box)
35454 if(box[0].size == 'xs'){
35457 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35462 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35467 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),
35472 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35473 y : minY + (this.unitWidth + this.gutter) * 1
35481 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35486 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35487 y : minY + (this.unitWidth + this.gutter) * 2
35491 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35492 y : minY + (this.unitWidth + this.gutter) * 2
35496 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),
35497 y : minY + (this.unitWidth + this.gutter) * 2
35505 * remove a Masonry Brick
35506 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35508 removeBrick : function(brick_id)
35514 for (var i = 0; i<this.bricks.length; i++) {
35515 if (this.bricks[i].id == brick_id) {
35516 this.bricks.splice(i,1);
35517 this.el.dom.removeChild(Roo.get(brick_id).dom);
35524 * adds a Masonry Brick
35525 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35527 addBrick : function(cfg)
35529 var cn = new Roo.bootstrap.MasonryBrick(cfg);
35530 //this.register(cn);
35531 cn.parentId = this.id;
35532 cn.render(this.el);
35537 * register a Masonry Brick
35538 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35541 register : function(brick)
35543 this.bricks.push(brick);
35544 brick.masonryId = this.id;
35548 * clear all the Masonry Brick
35550 clearAll : function()
35553 //this.getChildContainer().dom.innerHTML = "";
35554 this.el.dom.innerHTML = '';
35557 getSelected : function()
35559 if (!this.selectedBrick) {
35563 return this.selectedBrick;
35567 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35571 * register a Masonry Layout
35572 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35575 register : function(layout)
35577 this.groups[layout.id] = layout;
35580 * fetch a Masonry Layout based on the masonry layout ID
35581 * @param {string} the masonry layout to add
35582 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35585 get: function(layout_id) {
35586 if (typeof(this.groups[layout_id]) == 'undefined') {
35589 return this.groups[layout_id] ;
35601 * http://masonry.desandro.com
35603 * The idea is to render all the bricks based on vertical width...
35605 * The original code extends 'outlayer' - we might need to use that....
35611 * @class Roo.bootstrap.LayoutMasonryAuto
35612 * @extends Roo.bootstrap.Component
35613 * Bootstrap Layout Masonry class
35616 * Create a new Element
35617 * @param {Object} config The config object
35620 Roo.bootstrap.LayoutMasonryAuto = function(config){
35621 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35624 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
35627 * @cfg {Boolean} isFitWidth - resize the width..
35629 isFitWidth : false, // options..
35631 * @cfg {Boolean} isOriginLeft = left align?
35633 isOriginLeft : true,
35635 * @cfg {Boolean} isOriginTop = top align?
35637 isOriginTop : false,
35639 * @cfg {Boolean} isLayoutInstant = no animation?
35641 isLayoutInstant : false, // needed?
35643 * @cfg {Boolean} isResizingContainer = not sure if this is used..
35645 isResizingContainer : true,
35647 * @cfg {Number} columnWidth width of the columns
35653 * @cfg {Number} maxCols maximum number of columns
35658 * @cfg {Number} padHeight padding below box..
35664 * @cfg {Boolean} isAutoInitial defalut true
35667 isAutoInitial : true,
35673 initialColumnWidth : 0,
35674 currentSize : null,
35676 colYs : null, // array.
35683 bricks: null, //CompositeElement
35684 cols : 0, // array?
35685 // element : null, // wrapped now this.el
35686 _isLayoutInited : null,
35689 getAutoCreate : function(){
35693 cls: 'blog-masonary-wrapper ' + this.cls,
35695 cls : 'mas-boxes masonary'
35702 getChildContainer: function( )
35704 if (this.boxesEl) {
35705 return this.boxesEl;
35708 this.boxesEl = this.el.select('.mas-boxes').first();
35710 return this.boxesEl;
35714 initEvents : function()
35718 if(this.isAutoInitial){
35719 Roo.log('hook children rendered');
35720 this.on('childrenrendered', function() {
35721 Roo.log('children rendered');
35728 initial : function()
35730 this.reloadItems();
35732 this.currentSize = this.el.getBox(true);
35734 /// was window resize... - let's see if this works..
35735 Roo.EventManager.onWindowResize(this.resize, this);
35737 if(!this.isAutoInitial){
35742 this.layout.defer(500,this);
35745 reloadItems: function()
35747 this.bricks = this.el.select('.masonry-brick', true);
35749 this.bricks.each(function(b) {
35750 //Roo.log(b.getSize());
35751 if (!b.attr('originalwidth')) {
35752 b.attr('originalwidth', b.getSize().width);
35757 Roo.log(this.bricks.elements.length);
35760 resize : function()
35763 var cs = this.el.getBox(true);
35765 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35766 Roo.log("no change in with or X");
35769 this.currentSize = cs;
35773 layout : function()
35776 this._resetLayout();
35777 //this._manageStamps();
35779 // don't animate first layout
35780 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35781 this.layoutItems( isInstant );
35783 // flag for initalized
35784 this._isLayoutInited = true;
35787 layoutItems : function( isInstant )
35789 //var items = this._getItemsForLayout( this.items );
35790 // original code supports filtering layout items.. we just ignore it..
35792 this._layoutItems( this.bricks , isInstant );
35794 this._postLayout();
35796 _layoutItems : function ( items , isInstant)
35798 //this.fireEvent( 'layout', this, items );
35801 if ( !items || !items.elements.length ) {
35802 // no items, emit event with empty array
35807 items.each(function(item) {
35808 Roo.log("layout item");
35810 // get x/y object from method
35811 var position = this._getItemLayoutPosition( item );
35813 position.item = item;
35814 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35815 queue.push( position );
35818 this._processLayoutQueue( queue );
35820 /** Sets position of item in DOM
35821 * @param {Element} item
35822 * @param {Number} x - horizontal position
35823 * @param {Number} y - vertical position
35824 * @param {Boolean} isInstant - disables transitions
35826 _processLayoutQueue : function( queue )
35828 for ( var i=0, len = queue.length; i < len; i++ ) {
35829 var obj = queue[i];
35830 obj.item.position('absolute');
35831 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35837 * Any logic you want to do after each layout,
35838 * i.e. size the container
35840 _postLayout : function()
35842 this.resizeContainer();
35845 resizeContainer : function()
35847 if ( !this.isResizingContainer ) {
35850 var size = this._getContainerSize();
35852 this.el.setSize(size.width,size.height);
35853 this.boxesEl.setSize(size.width,size.height);
35859 _resetLayout : function()
35861 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35862 this.colWidth = this.el.getWidth();
35863 //this.gutter = this.el.getWidth();
35865 this.measureColumns();
35871 this.colYs.push( 0 );
35877 measureColumns : function()
35879 this.getContainerWidth();
35880 // if columnWidth is 0, default to outerWidth of first item
35881 if ( !this.columnWidth ) {
35882 var firstItem = this.bricks.first();
35883 Roo.log(firstItem);
35884 this.columnWidth = this.containerWidth;
35885 if (firstItem && firstItem.attr('originalwidth') ) {
35886 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35888 // columnWidth fall back to item of first element
35889 Roo.log("set column width?");
35890 this.initialColumnWidth = this.columnWidth ;
35892 // if first elem has no width, default to size of container
35897 if (this.initialColumnWidth) {
35898 this.columnWidth = this.initialColumnWidth;
35903 // column width is fixed at the top - however if container width get's smaller we should
35906 // this bit calcs how man columns..
35908 var columnWidth = this.columnWidth += this.gutter;
35910 // calculate columns
35911 var containerWidth = this.containerWidth + this.gutter;
35913 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35914 // fix rounding errors, typically with gutters
35915 var excess = columnWidth - containerWidth % columnWidth;
35918 // if overshoot is less than a pixel, round up, otherwise floor it
35919 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35920 cols = Math[ mathMethod ]( cols );
35921 this.cols = Math.max( cols, 1 );
35922 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35924 // padding positioning..
35925 var totalColWidth = this.cols * this.columnWidth;
35926 var padavail = this.containerWidth - totalColWidth;
35927 // so for 2 columns - we need 3 'pads'
35929 var padNeeded = (1+this.cols) * this.padWidth;
35931 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35933 this.columnWidth += padExtra
35934 //this.padWidth = Math.floor(padavail / ( this.cols));
35936 // adjust colum width so that padding is fixed??
35938 // we have 3 columns ... total = width * 3
35939 // we have X left over... that should be used by
35941 //if (this.expandC) {
35949 getContainerWidth : function()
35951 /* // container is parent if fit width
35952 var container = this.isFitWidth ? this.element.parentNode : this.element;
35953 // check that this.size and size are there
35954 // IE8 triggers resize on body size change, so they might not be
35956 var size = getSize( container ); //FIXME
35957 this.containerWidth = size && size.innerWidth; //FIXME
35960 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
35964 _getItemLayoutPosition : function( item ) // what is item?
35966 // we resize the item to our columnWidth..
35968 item.setWidth(this.columnWidth);
35969 item.autoBoxAdjust = false;
35971 var sz = item.getSize();
35973 // how many columns does this brick span
35974 var remainder = this.containerWidth % this.columnWidth;
35976 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35977 // round if off by 1 pixel, otherwise use ceil
35978 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
35979 colSpan = Math.min( colSpan, this.cols );
35981 // normally this should be '1' as we dont' currently allow multi width columns..
35983 var colGroup = this._getColGroup( colSpan );
35984 // get the minimum Y value from the columns
35985 var minimumY = Math.min.apply( Math, colGroup );
35986 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35988 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35990 // position the brick
35992 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35993 y: this.currentSize.y + minimumY + this.padHeight
35997 // apply setHeight to necessary columns
35998 var setHeight = minimumY + sz.height + this.padHeight;
35999 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
36001 var setSpan = this.cols + 1 - colGroup.length;
36002 for ( var i = 0; i < setSpan; i++ ) {
36003 this.colYs[ shortColIndex + i ] = setHeight ;
36010 * @param {Number} colSpan - number of columns the element spans
36011 * @returns {Array} colGroup
36013 _getColGroup : function( colSpan )
36015 if ( colSpan < 2 ) {
36016 // if brick spans only one column, use all the column Ys
36021 // how many different places could this brick fit horizontally
36022 var groupCount = this.cols + 1 - colSpan;
36023 // for each group potential horizontal position
36024 for ( var i = 0; i < groupCount; i++ ) {
36025 // make an array of colY values for that one group
36026 var groupColYs = this.colYs.slice( i, i + colSpan );
36027 // and get the max value of the array
36028 colGroup[i] = Math.max.apply( Math, groupColYs );
36033 _manageStamp : function( stamp )
36035 var stampSize = stamp.getSize();
36036 var offset = stamp.getBox();
36037 // get the columns that this stamp affects
36038 var firstX = this.isOriginLeft ? offset.x : offset.right;
36039 var lastX = firstX + stampSize.width;
36040 var firstCol = Math.floor( firstX / this.columnWidth );
36041 firstCol = Math.max( 0, firstCol );
36043 var lastCol = Math.floor( lastX / this.columnWidth );
36044 // lastCol should not go over if multiple of columnWidth #425
36045 lastCol -= lastX % this.columnWidth ? 0 : 1;
36046 lastCol = Math.min( this.cols - 1, lastCol );
36048 // set colYs to bottom of the stamp
36049 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
36052 for ( var i = firstCol; i <= lastCol; i++ ) {
36053 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
36058 _getContainerSize : function()
36060 this.maxY = Math.max.apply( Math, this.colYs );
36065 if ( this.isFitWidth ) {
36066 size.width = this._getContainerFitWidth();
36072 _getContainerFitWidth : function()
36074 var unusedCols = 0;
36075 // count unused columns
36078 if ( this.colYs[i] !== 0 ) {
36083 // fit container to columns that have been used
36084 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
36087 needsResizeLayout : function()
36089 var previousWidth = this.containerWidth;
36090 this.getContainerWidth();
36091 return previousWidth !== this.containerWidth;
36106 * @class Roo.bootstrap.MasonryBrick
36107 * @extends Roo.bootstrap.Component
36108 * Bootstrap MasonryBrick class
36111 * Create a new MasonryBrick
36112 * @param {Object} config The config object
36115 Roo.bootstrap.MasonryBrick = function(config){
36117 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
36119 Roo.bootstrap.MasonryBrick.register(this);
36125 * When a MasonryBrick is clcik
36126 * @param {Roo.bootstrap.MasonryBrick} this
36127 * @param {Roo.EventObject} e
36133 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
36136 * @cfg {String} title
36140 * @cfg {String} html
36144 * @cfg {String} bgimage
36148 * @cfg {String} videourl
36152 * @cfg {String} cls
36156 * @cfg {String} href
36160 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
36165 * @cfg {String} placetitle (center|bottom)
36170 * @cfg {Boolean} isFitContainer defalut true
36172 isFitContainer : true,
36175 * @cfg {Boolean} preventDefault defalut false
36177 preventDefault : false,
36180 * @cfg {Boolean} inverse defalut false
36182 maskInverse : false,
36184 getAutoCreate : function()
36186 if(!this.isFitContainer){
36187 return this.getSplitAutoCreate();
36190 var cls = 'masonry-brick masonry-brick-full';
36192 if(this.href.length){
36193 cls += ' masonry-brick-link';
36196 if(this.bgimage.length){
36197 cls += ' masonry-brick-image';
36200 if(this.maskInverse){
36201 cls += ' mask-inverse';
36204 if(!this.html.length && !this.maskInverse && !this.videourl.length){
36205 cls += ' enable-mask';
36209 cls += ' masonry-' + this.size + '-brick';
36212 if(this.placetitle.length){
36214 switch (this.placetitle) {
36216 cls += ' masonry-center-title';
36219 cls += ' masonry-bottom-title';
36226 if(!this.html.length && !this.bgimage.length){
36227 cls += ' masonry-center-title';
36230 if(!this.html.length && this.bgimage.length){
36231 cls += ' masonry-bottom-title';
36236 cls += ' ' + this.cls;
36240 tag: (this.href.length) ? 'a' : 'div',
36245 cls: 'masonry-brick-mask'
36249 cls: 'masonry-brick-paragraph',
36255 if(this.href.length){
36256 cfg.href = this.href;
36259 var cn = cfg.cn[1].cn;
36261 if(this.title.length){
36264 cls: 'masonry-brick-title',
36269 if(this.html.length){
36272 cls: 'masonry-brick-text',
36277 if (!this.title.length && !this.html.length) {
36278 cfg.cn[1].cls += ' hide';
36281 if(this.bgimage.length){
36284 cls: 'masonry-brick-image-view',
36289 if(this.videourl.length){
36290 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36291 // youtube support only?
36294 cls: 'masonry-brick-image-view',
36297 allowfullscreen : true
36305 getSplitAutoCreate : function()
36307 var cls = 'masonry-brick masonry-brick-split';
36309 if(this.href.length){
36310 cls += ' masonry-brick-link';
36313 if(this.bgimage.length){
36314 cls += ' masonry-brick-image';
36318 cls += ' masonry-' + this.size + '-brick';
36321 switch (this.placetitle) {
36323 cls += ' masonry-center-title';
36326 cls += ' masonry-bottom-title';
36329 if(!this.bgimage.length){
36330 cls += ' masonry-center-title';
36333 if(this.bgimage.length){
36334 cls += ' masonry-bottom-title';
36340 cls += ' ' + this.cls;
36344 tag: (this.href.length) ? 'a' : 'div',
36349 cls: 'masonry-brick-split-head',
36353 cls: 'masonry-brick-paragraph',
36360 cls: 'masonry-brick-split-body',
36366 if(this.href.length){
36367 cfg.href = this.href;
36370 if(this.title.length){
36371 cfg.cn[0].cn[0].cn.push({
36373 cls: 'masonry-brick-title',
36378 if(this.html.length){
36379 cfg.cn[1].cn.push({
36381 cls: 'masonry-brick-text',
36386 if(this.bgimage.length){
36387 cfg.cn[0].cn.push({
36389 cls: 'masonry-brick-image-view',
36394 if(this.videourl.length){
36395 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36396 // youtube support only?
36397 cfg.cn[0].cn.cn.push({
36399 cls: 'masonry-brick-image-view',
36402 allowfullscreen : true
36409 initEvents: function()
36411 switch (this.size) {
36444 this.el.on('touchstart', this.onTouchStart, this);
36445 this.el.on('touchmove', this.onTouchMove, this);
36446 this.el.on('touchend', this.onTouchEnd, this);
36447 this.el.on('contextmenu', this.onContextMenu, this);
36449 this.el.on('mouseenter' ,this.enter, this);
36450 this.el.on('mouseleave', this.leave, this);
36451 this.el.on('click', this.onClick, this);
36454 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36455 this.parent().bricks.push(this);
36460 onClick: function(e, el)
36462 var time = this.endTimer - this.startTimer;
36463 // Roo.log(e.preventDefault());
36466 e.preventDefault();
36471 if(!this.preventDefault){
36475 e.preventDefault();
36477 if (this.activeClass != '') {
36478 this.selectBrick();
36481 this.fireEvent('click', this, e);
36484 enter: function(e, el)
36486 e.preventDefault();
36488 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36492 if(this.bgimage.length && this.html.length){
36493 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36497 leave: function(e, el)
36499 e.preventDefault();
36501 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36505 if(this.bgimage.length && this.html.length){
36506 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36510 onTouchStart: function(e, el)
36512 // e.preventDefault();
36514 this.touchmoved = false;
36516 if(!this.isFitContainer){
36520 if(!this.bgimage.length || !this.html.length){
36524 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36526 this.timer = new Date().getTime();
36530 onTouchMove: function(e, el)
36532 this.touchmoved = true;
36535 onContextMenu : function(e,el)
36537 e.preventDefault();
36538 e.stopPropagation();
36542 onTouchEnd: function(e, el)
36544 // e.preventDefault();
36546 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36553 if(!this.bgimage.length || !this.html.length){
36555 if(this.href.length){
36556 window.location.href = this.href;
36562 if(!this.isFitContainer){
36566 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36568 window.location.href = this.href;
36571 //selection on single brick only
36572 selectBrick : function() {
36574 if (!this.parentId) {
36578 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36579 var index = m.selectedBrick.indexOf(this.id);
36582 m.selectedBrick.splice(index,1);
36583 this.el.removeClass(this.activeClass);
36587 for(var i = 0; i < m.selectedBrick.length; i++) {
36588 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36589 b.el.removeClass(b.activeClass);
36592 m.selectedBrick = [];
36594 m.selectedBrick.push(this.id);
36595 this.el.addClass(this.activeClass);
36599 isSelected : function(){
36600 return this.el.hasClass(this.activeClass);
36605 Roo.apply(Roo.bootstrap.MasonryBrick, {
36608 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36610 * register a Masonry Brick
36611 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36614 register : function(brick)
36616 //this.groups[brick.id] = brick;
36617 this.groups.add(brick.id, brick);
36620 * fetch a masonry brick based on the masonry brick ID
36621 * @param {string} the masonry brick to add
36622 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36625 get: function(brick_id)
36627 // if (typeof(this.groups[brick_id]) == 'undefined') {
36630 // return this.groups[brick_id] ;
36632 if(this.groups.key(brick_id)) {
36633 return this.groups.key(brick_id);
36651 * @class Roo.bootstrap.Brick
36652 * @extends Roo.bootstrap.Component
36653 * Bootstrap Brick class
36656 * Create a new Brick
36657 * @param {Object} config The config object
36660 Roo.bootstrap.Brick = function(config){
36661 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36667 * When a Brick is click
36668 * @param {Roo.bootstrap.Brick} this
36669 * @param {Roo.EventObject} e
36675 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
36678 * @cfg {String} title
36682 * @cfg {String} html
36686 * @cfg {String} bgimage
36690 * @cfg {String} cls
36694 * @cfg {String} href
36698 * @cfg {String} video
36702 * @cfg {Boolean} square
36706 getAutoCreate : function()
36708 var cls = 'roo-brick';
36710 if(this.href.length){
36711 cls += ' roo-brick-link';
36714 if(this.bgimage.length){
36715 cls += ' roo-brick-image';
36718 if(!this.html.length && !this.bgimage.length){
36719 cls += ' roo-brick-center-title';
36722 if(!this.html.length && this.bgimage.length){
36723 cls += ' roo-brick-bottom-title';
36727 cls += ' ' + this.cls;
36731 tag: (this.href.length) ? 'a' : 'div',
36736 cls: 'roo-brick-paragraph',
36742 if(this.href.length){
36743 cfg.href = this.href;
36746 var cn = cfg.cn[0].cn;
36748 if(this.title.length){
36751 cls: 'roo-brick-title',
36756 if(this.html.length){
36759 cls: 'roo-brick-text',
36766 if(this.bgimage.length){
36769 cls: 'roo-brick-image-view',
36777 initEvents: function()
36779 if(this.title.length || this.html.length){
36780 this.el.on('mouseenter' ,this.enter, this);
36781 this.el.on('mouseleave', this.leave, this);
36784 Roo.EventManager.onWindowResize(this.resize, this);
36786 if(this.bgimage.length){
36787 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36788 this.imageEl.on('load', this.onImageLoad, this);
36795 onImageLoad : function()
36800 resize : function()
36802 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36804 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36806 if(this.bgimage.length){
36807 var image = this.el.select('.roo-brick-image-view', true).first();
36809 image.setWidth(paragraph.getWidth());
36812 image.setHeight(paragraph.getWidth());
36815 this.el.setHeight(image.getHeight());
36816 paragraph.setHeight(image.getHeight());
36822 enter: function(e, el)
36824 e.preventDefault();
36826 if(this.bgimage.length){
36827 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36828 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36832 leave: function(e, el)
36834 e.preventDefault();
36836 if(this.bgimage.length){
36837 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36838 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36853 * @class Roo.bootstrap.NumberField
36854 * @extends Roo.bootstrap.Input
36855 * Bootstrap NumberField class
36861 * Create a new NumberField
36862 * @param {Object} config The config object
36865 Roo.bootstrap.NumberField = function(config){
36866 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36869 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36872 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36874 allowDecimals : true,
36876 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36878 decimalSeparator : ".",
36880 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36882 decimalPrecision : 2,
36884 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36886 allowNegative : true,
36889 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36893 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36895 minValue : Number.NEGATIVE_INFINITY,
36897 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36899 maxValue : Number.MAX_VALUE,
36901 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36903 minText : "The minimum value for this field is {0}",
36905 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36907 maxText : "The maximum value for this field is {0}",
36909 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36910 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36912 nanText : "{0} is not a valid number",
36914 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36916 thousandsDelimiter : false,
36918 * @cfg {String} valueAlign alignment of value
36920 valueAlign : "left",
36922 getAutoCreate : function()
36924 var hiddenInput = {
36928 cls: 'hidden-number-input'
36932 hiddenInput.name = this.name;
36937 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36939 this.name = hiddenInput.name;
36941 if(cfg.cn.length > 0) {
36942 cfg.cn.push(hiddenInput);
36949 initEvents : function()
36951 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36953 var allowed = "0123456789";
36955 if(this.allowDecimals){
36956 allowed += this.decimalSeparator;
36959 if(this.allowNegative){
36963 if(this.thousandsDelimiter) {
36967 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36969 var keyPress = function(e){
36971 var k = e.getKey();
36973 var c = e.getCharCode();
36976 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36977 allowed.indexOf(String.fromCharCode(c)) === -1
36983 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36987 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36992 this.el.on("keypress", keyPress, this);
36995 validateValue : function(value)
36998 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
37002 var num = this.parseValue(value);
37005 this.markInvalid(String.format(this.nanText, value));
37009 if(num < this.minValue){
37010 this.markInvalid(String.format(this.minText, this.minValue));
37014 if(num > this.maxValue){
37015 this.markInvalid(String.format(this.maxText, this.maxValue));
37022 getValue : function()
37024 var v = this.hiddenEl().getValue();
37026 return this.fixPrecision(this.parseValue(v));
37029 parseValue : function(value)
37031 if(this.thousandsDelimiter) {
37033 r = new RegExp(",", "g");
37034 value = value.replace(r, "");
37037 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
37038 return isNaN(value) ? '' : value;
37041 fixPrecision : function(value)
37043 if(this.thousandsDelimiter) {
37045 r = new RegExp(",", "g");
37046 value = value.replace(r, "");
37049 var nan = isNaN(value);
37051 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
37052 return nan ? '' : value;
37054 return parseFloat(value).toFixed(this.decimalPrecision);
37057 setValue : function(v)
37059 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
37065 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
37067 this.inputEl().dom.value = (v == '') ? '' :
37068 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
37070 if(!this.allowZero && v === '0') {
37071 this.hiddenEl().dom.value = '';
37072 this.inputEl().dom.value = '';
37079 decimalPrecisionFcn : function(v)
37081 return Math.floor(v);
37084 beforeBlur : function()
37086 var v = this.parseValue(this.getRawValue());
37088 if(v || v === 0 || v === ''){
37093 hiddenEl : function()
37095 return this.el.select('input.hidden-number-input',true).first();
37107 * @class Roo.bootstrap.DocumentSlider
37108 * @extends Roo.bootstrap.Component
37109 * Bootstrap DocumentSlider class
37112 * Create a new DocumentViewer
37113 * @param {Object} config The config object
37116 Roo.bootstrap.DocumentSlider = function(config){
37117 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
37124 * Fire after initEvent
37125 * @param {Roo.bootstrap.DocumentSlider} this
37130 * Fire after update
37131 * @param {Roo.bootstrap.DocumentSlider} this
37137 * @param {Roo.bootstrap.DocumentSlider} this
37143 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
37149 getAutoCreate : function()
37153 cls : 'roo-document-slider',
37157 cls : 'roo-document-slider-header',
37161 cls : 'roo-document-slider-header-title'
37167 cls : 'roo-document-slider-body',
37171 cls : 'roo-document-slider-prev',
37175 cls : 'fa fa-chevron-left'
37181 cls : 'roo-document-slider-thumb',
37185 cls : 'roo-document-slider-image'
37191 cls : 'roo-document-slider-next',
37195 cls : 'fa fa-chevron-right'
37207 initEvents : function()
37209 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
37210 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
37212 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
37213 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
37215 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
37216 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
37218 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
37219 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
37221 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
37222 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
37224 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
37225 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37227 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
37228 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37230 this.thumbEl.on('click', this.onClick, this);
37232 this.prevIndicator.on('click', this.prev, this);
37234 this.nextIndicator.on('click', this.next, this);
37238 initial : function()
37240 if(this.files.length){
37241 this.indicator = 1;
37245 this.fireEvent('initial', this);
37248 update : function()
37250 this.imageEl.attr('src', this.files[this.indicator - 1]);
37252 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
37254 this.prevIndicator.show();
37256 if(this.indicator == 1){
37257 this.prevIndicator.hide();
37260 this.nextIndicator.show();
37262 if(this.indicator == this.files.length){
37263 this.nextIndicator.hide();
37266 this.thumbEl.scrollTo('top');
37268 this.fireEvent('update', this);
37271 onClick : function(e)
37273 e.preventDefault();
37275 this.fireEvent('click', this);
37280 e.preventDefault();
37282 this.indicator = Math.max(1, this.indicator - 1);
37289 e.preventDefault();
37291 this.indicator = Math.min(this.files.length, this.indicator + 1);
37305 * @class Roo.bootstrap.RadioSet
37306 * @extends Roo.bootstrap.Input
37307 * Bootstrap RadioSet class
37308 * @cfg {String} indicatorpos (left|right) default left
37309 * @cfg {Boolean} inline (true|false) inline the element (default true)
37310 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37312 * Create a new RadioSet
37313 * @param {Object} config The config object
37316 Roo.bootstrap.RadioSet = function(config){
37318 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
37322 Roo.bootstrap.RadioSet.register(this);
37327 * Fires when the element is checked or unchecked.
37328 * @param {Roo.bootstrap.RadioSet} this This radio
37329 * @param {Roo.bootstrap.Radio} item The checked item
37334 * Fires when the element is click.
37335 * @param {Roo.bootstrap.RadioSet} this This radio set
37336 * @param {Roo.bootstrap.Radio} item The checked item
37337 * @param {Roo.EventObject} e The event object
37344 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
37352 indicatorpos : 'left',
37354 getAutoCreate : function()
37358 cls : 'roo-radio-set-label',
37362 html : this.fieldLabel
37366 if (Roo.bootstrap.version == 3) {
37369 if(this.indicatorpos == 'left'){
37372 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37373 tooltip : 'This field is required'
37378 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37379 tooltip : 'This field is required'
37385 cls : 'roo-radio-set-items'
37388 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37390 if (align === 'left' && this.fieldLabel.length) {
37393 cls : "roo-radio-set-right",
37399 if(this.labelWidth > 12){
37400 label.style = "width: " + this.labelWidth + 'px';
37403 if(this.labelWidth < 13 && this.labelmd == 0){
37404 this.labelmd = this.labelWidth;
37407 if(this.labellg > 0){
37408 label.cls += ' col-lg-' + this.labellg;
37409 items.cls += ' col-lg-' + (12 - this.labellg);
37412 if(this.labelmd > 0){
37413 label.cls += ' col-md-' + this.labelmd;
37414 items.cls += ' col-md-' + (12 - this.labelmd);
37417 if(this.labelsm > 0){
37418 label.cls += ' col-sm-' + this.labelsm;
37419 items.cls += ' col-sm-' + (12 - this.labelsm);
37422 if(this.labelxs > 0){
37423 label.cls += ' col-xs-' + this.labelxs;
37424 items.cls += ' col-xs-' + (12 - this.labelxs);
37430 cls : 'roo-radio-set',
37434 cls : 'roo-radio-set-input',
37437 value : this.value ? this.value : ''
37444 if(this.weight.length){
37445 cfg.cls += ' roo-radio-' + this.weight;
37449 cfg.cls += ' roo-radio-set-inline';
37453 ['xs','sm','md','lg'].map(function(size){
37454 if (settings[size]) {
37455 cfg.cls += ' col-' + size + '-' + settings[size];
37463 initEvents : function()
37465 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37466 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37468 if(!this.fieldLabel.length){
37469 this.labelEl.hide();
37472 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37473 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37475 this.indicator = this.indicatorEl();
37477 if(this.indicator){
37478 this.indicator.addClass('invisible');
37481 this.originalValue = this.getValue();
37485 inputEl: function ()
37487 return this.el.select('.roo-radio-set-input', true).first();
37490 getChildContainer : function()
37492 return this.itemsEl;
37495 register : function(item)
37497 this.radioes.push(item);
37501 validate : function()
37503 if(this.getVisibilityEl().hasClass('hidden')){
37509 Roo.each(this.radioes, function(i){
37518 if(this.allowBlank) {
37522 if(this.disabled || valid){
37527 this.markInvalid();
37532 markValid : function()
37534 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37535 this.indicatorEl().removeClass('visible');
37536 this.indicatorEl().addClass('invisible');
37540 if (Roo.bootstrap.version == 3) {
37541 this.el.removeClass([this.invalidClass, this.validClass]);
37542 this.el.addClass(this.validClass);
37544 this.el.removeClass(['is-invalid','is-valid']);
37545 this.el.addClass(['is-valid']);
37547 this.fireEvent('valid', this);
37550 markInvalid : function(msg)
37552 if(this.allowBlank || this.disabled){
37556 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37557 this.indicatorEl().removeClass('invisible');
37558 this.indicatorEl().addClass('visible');
37560 if (Roo.bootstrap.version == 3) {
37561 this.el.removeClass([this.invalidClass, this.validClass]);
37562 this.el.addClass(this.invalidClass);
37564 this.el.removeClass(['is-invalid','is-valid']);
37565 this.el.addClass(['is-invalid']);
37568 this.fireEvent('invalid', this, msg);
37572 setValue : function(v, suppressEvent)
37574 if(this.value === v){
37581 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37584 Roo.each(this.radioes, function(i){
37586 i.el.removeClass('checked');
37589 Roo.each(this.radioes, function(i){
37591 if(i.value === v || i.value.toString() === v.toString()){
37593 i.el.addClass('checked');
37595 if(suppressEvent !== true){
37596 this.fireEvent('check', this, i);
37607 clearInvalid : function(){
37609 if(!this.el || this.preventMark){
37613 this.el.removeClass([this.invalidClass]);
37615 this.fireEvent('valid', this);
37620 Roo.apply(Roo.bootstrap.RadioSet, {
37624 register : function(set)
37626 this.groups[set.name] = set;
37629 get: function(name)
37631 if (typeof(this.groups[name]) == 'undefined') {
37635 return this.groups[name] ;
37641 * Ext JS Library 1.1.1
37642 * Copyright(c) 2006-2007, Ext JS, LLC.
37644 * Originally Released Under LGPL - original licence link has changed is not relivant.
37647 * <script type="text/javascript">
37652 * @class Roo.bootstrap.SplitBar
37653 * @extends Roo.util.Observable
37654 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37658 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37659 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37660 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37661 split.minSize = 100;
37662 split.maxSize = 600;
37663 split.animate = true;
37664 split.on('moved', splitterMoved);
37667 * Create a new SplitBar
37668 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
37669 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
37670 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37671 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
37672 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37673 position of the SplitBar).
37675 Roo.bootstrap.SplitBar = function(cfg){
37680 // dragElement : elm
37681 // resizingElement: el,
37683 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37684 // placement : Roo.bootstrap.SplitBar.LEFT ,
37685 // existingProxy ???
37688 this.el = Roo.get(cfg.dragElement, true);
37689 this.el.dom.unselectable = "on";
37691 this.resizingEl = Roo.get(cfg.resizingElement, true);
37695 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37696 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37699 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37702 * The minimum size of the resizing element. (Defaults to 0)
37708 * The maximum size of the resizing element. (Defaults to 2000)
37711 this.maxSize = 2000;
37714 * Whether to animate the transition to the new size
37717 this.animate = false;
37720 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37723 this.useShim = false;
37728 if(!cfg.existingProxy){
37730 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37732 this.proxy = Roo.get(cfg.existingProxy).dom;
37735 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37738 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37741 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37744 this.dragSpecs = {};
37747 * @private The adapter to use to positon and resize elements
37749 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37750 this.adapter.init(this);
37752 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37754 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37755 this.el.addClass("roo-splitbar-h");
37758 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37759 this.el.addClass("roo-splitbar-v");
37765 * Fires when the splitter is moved (alias for {@link #event-moved})
37766 * @param {Roo.bootstrap.SplitBar} this
37767 * @param {Number} newSize the new width or height
37772 * Fires when the splitter is moved
37773 * @param {Roo.bootstrap.SplitBar} this
37774 * @param {Number} newSize the new width or height
37778 * @event beforeresize
37779 * Fires before the splitter is dragged
37780 * @param {Roo.bootstrap.SplitBar} this
37782 "beforeresize" : true,
37784 "beforeapply" : true
37787 Roo.util.Observable.call(this);
37790 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37791 onStartProxyDrag : function(x, y){
37792 this.fireEvent("beforeresize", this);
37794 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37796 o.enableDisplayMode("block");
37797 // all splitbars share the same overlay
37798 Roo.bootstrap.SplitBar.prototype.overlay = o;
37800 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37801 this.overlay.show();
37802 Roo.get(this.proxy).setDisplayed("block");
37803 var size = this.adapter.getElementSize(this);
37804 this.activeMinSize = this.getMinimumSize();;
37805 this.activeMaxSize = this.getMaximumSize();;
37806 var c1 = size - this.activeMinSize;
37807 var c2 = Math.max(this.activeMaxSize - size, 0);
37808 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37809 this.dd.resetConstraints();
37810 this.dd.setXConstraint(
37811 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37812 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37814 this.dd.setYConstraint(0, 0);
37816 this.dd.resetConstraints();
37817 this.dd.setXConstraint(0, 0);
37818 this.dd.setYConstraint(
37819 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37820 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37823 this.dragSpecs.startSize = size;
37824 this.dragSpecs.startPoint = [x, y];
37825 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37829 * @private Called after the drag operation by the DDProxy
37831 onEndProxyDrag : function(e){
37832 Roo.get(this.proxy).setDisplayed(false);
37833 var endPoint = Roo.lib.Event.getXY(e);
37835 this.overlay.hide();
37838 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37839 newSize = this.dragSpecs.startSize +
37840 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37841 endPoint[0] - this.dragSpecs.startPoint[0] :
37842 this.dragSpecs.startPoint[0] - endPoint[0]
37845 newSize = this.dragSpecs.startSize +
37846 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37847 endPoint[1] - this.dragSpecs.startPoint[1] :
37848 this.dragSpecs.startPoint[1] - endPoint[1]
37851 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37852 if(newSize != this.dragSpecs.startSize){
37853 if(this.fireEvent('beforeapply', this, newSize) !== false){
37854 this.adapter.setElementSize(this, newSize);
37855 this.fireEvent("moved", this, newSize);
37856 this.fireEvent("resize", this, newSize);
37862 * Get the adapter this SplitBar uses
37863 * @return The adapter object
37865 getAdapter : function(){
37866 return this.adapter;
37870 * Set the adapter this SplitBar uses
37871 * @param {Object} adapter A SplitBar adapter object
37873 setAdapter : function(adapter){
37874 this.adapter = adapter;
37875 this.adapter.init(this);
37879 * Gets the minimum size for the resizing element
37880 * @return {Number} The minimum size
37882 getMinimumSize : function(){
37883 return this.minSize;
37887 * Sets the minimum size for the resizing element
37888 * @param {Number} minSize The minimum size
37890 setMinimumSize : function(minSize){
37891 this.minSize = minSize;
37895 * Gets the maximum size for the resizing element
37896 * @return {Number} The maximum size
37898 getMaximumSize : function(){
37899 return this.maxSize;
37903 * Sets the maximum size for the resizing element
37904 * @param {Number} maxSize The maximum size
37906 setMaximumSize : function(maxSize){
37907 this.maxSize = maxSize;
37911 * Sets the initialize size for the resizing element
37912 * @param {Number} size The initial size
37914 setCurrentSize : function(size){
37915 var oldAnimate = this.animate;
37916 this.animate = false;
37917 this.adapter.setElementSize(this, size);
37918 this.animate = oldAnimate;
37922 * Destroy this splitbar.
37923 * @param {Boolean} removeEl True to remove the element
37925 destroy : function(removeEl){
37927 this.shim.remove();
37930 this.proxy.parentNode.removeChild(this.proxy);
37938 * @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.
37940 Roo.bootstrap.SplitBar.createProxy = function(dir){
37941 var proxy = new Roo.Element(document.createElement("div"));
37942 proxy.unselectable();
37943 var cls = 'roo-splitbar-proxy';
37944 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37945 document.body.appendChild(proxy.dom);
37950 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37951 * Default Adapter. It assumes the splitter and resizing element are not positioned
37952 * elements and only gets/sets the width of the element. Generally used for table based layouts.
37954 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37957 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37958 // do nothing for now
37959 init : function(s){
37963 * Called before drag operations to get the current size of the resizing element.
37964 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37966 getElementSize : function(s){
37967 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37968 return s.resizingEl.getWidth();
37970 return s.resizingEl.getHeight();
37975 * Called after drag operations to set the size of the resizing element.
37976 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37977 * @param {Number} newSize The new size to set
37978 * @param {Function} onComplete A function to be invoked when resizing is complete
37980 setElementSize : function(s, newSize, onComplete){
37981 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37983 s.resizingEl.setWidth(newSize);
37985 onComplete(s, newSize);
37988 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37993 s.resizingEl.setHeight(newSize);
37995 onComplete(s, newSize);
37998 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
38005 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
38006 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
38007 * Adapter that moves the splitter element to align with the resized sizing element.
38008 * Used with an absolute positioned SplitBar.
38009 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
38010 * document.body, make sure you assign an id to the body element.
38012 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
38013 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
38014 this.container = Roo.get(container);
38017 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
38018 init : function(s){
38019 this.basic.init(s);
38022 getElementSize : function(s){
38023 return this.basic.getElementSize(s);
38026 setElementSize : function(s, newSize, onComplete){
38027 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
38030 moveSplitter : function(s){
38031 var yes = Roo.bootstrap.SplitBar;
38032 switch(s.placement){
38034 s.el.setX(s.resizingEl.getRight());
38037 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
38040 s.el.setY(s.resizingEl.getBottom());
38043 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
38050 * Orientation constant - Create a vertical SplitBar
38054 Roo.bootstrap.SplitBar.VERTICAL = 1;
38057 * Orientation constant - Create a horizontal SplitBar
38061 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
38064 * Placement constant - The resizing element is to the left of the splitter element
38068 Roo.bootstrap.SplitBar.LEFT = 1;
38071 * Placement constant - The resizing element is to the right of the splitter element
38075 Roo.bootstrap.SplitBar.RIGHT = 2;
38078 * Placement constant - The resizing element is positioned above the splitter element
38082 Roo.bootstrap.SplitBar.TOP = 3;
38085 * Placement constant - The resizing element is positioned under splitter element
38089 Roo.bootstrap.SplitBar.BOTTOM = 4;
38090 Roo.namespace("Roo.bootstrap.layout");/*
38092 * Ext JS Library 1.1.1
38093 * Copyright(c) 2006-2007, Ext JS, LLC.
38095 * Originally Released Under LGPL - original licence link has changed is not relivant.
38098 * <script type="text/javascript">
38102 * @class Roo.bootstrap.layout.Manager
38103 * @extends Roo.bootstrap.Component
38104 * Base class for layout managers.
38106 Roo.bootstrap.layout.Manager = function(config)
38108 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
38114 /** false to disable window resize monitoring @type Boolean */
38115 this.monitorWindowResize = true;
38120 * Fires when a layout is performed.
38121 * @param {Roo.LayoutManager} this
38125 * @event regionresized
38126 * Fires when the user resizes a region.
38127 * @param {Roo.LayoutRegion} region The resized region
38128 * @param {Number} newSize The new size (width for east/west, height for north/south)
38130 "regionresized" : true,
38132 * @event regioncollapsed
38133 * Fires when a region is collapsed.
38134 * @param {Roo.LayoutRegion} region The collapsed region
38136 "regioncollapsed" : true,
38138 * @event regionexpanded
38139 * Fires when a region is expanded.
38140 * @param {Roo.LayoutRegion} region The expanded region
38142 "regionexpanded" : true
38144 this.updating = false;
38147 this.el = Roo.get(config.el);
38153 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
38158 monitorWindowResize : true,
38164 onRender : function(ct, position)
38167 this.el = Roo.get(ct);
38170 //this.fireEvent('render',this);
38174 initEvents: function()
38178 // ie scrollbar fix
38179 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
38180 document.body.scroll = "no";
38181 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
38182 this.el.position('relative');
38184 this.id = this.el.id;
38185 this.el.addClass("roo-layout-container");
38186 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
38187 if(this.el.dom != document.body ) {
38188 this.el.on('resize', this.layout,this);
38189 this.el.on('show', this.layout,this);
38195 * Returns true if this layout is currently being updated
38196 * @return {Boolean}
38198 isUpdating : function(){
38199 return this.updating;
38203 * Suspend the LayoutManager from doing auto-layouts while
38204 * making multiple add or remove calls
38206 beginUpdate : function(){
38207 this.updating = true;
38211 * Restore auto-layouts and optionally disable the manager from performing a layout
38212 * @param {Boolean} noLayout true to disable a layout update
38214 endUpdate : function(noLayout){
38215 this.updating = false;
38221 layout: function(){
38225 onRegionResized : function(region, newSize){
38226 this.fireEvent("regionresized", region, newSize);
38230 onRegionCollapsed : function(region){
38231 this.fireEvent("regioncollapsed", region);
38234 onRegionExpanded : function(region){
38235 this.fireEvent("regionexpanded", region);
38239 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
38240 * performs box-model adjustments.
38241 * @return {Object} The size as an object {width: (the width), height: (the height)}
38243 getViewSize : function()
38246 if(this.el.dom != document.body){
38247 size = this.el.getSize();
38249 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
38251 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
38252 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38257 * Returns the Element this layout is bound to.
38258 * @return {Roo.Element}
38260 getEl : function(){
38265 * Returns the specified region.
38266 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
38267 * @return {Roo.LayoutRegion}
38269 getRegion : function(target){
38270 return this.regions[target.toLowerCase()];
38273 onWindowResize : function(){
38274 if(this.monitorWindowResize){
38281 * Ext JS Library 1.1.1
38282 * Copyright(c) 2006-2007, Ext JS, LLC.
38284 * Originally Released Under LGPL - original licence link has changed is not relivant.
38287 * <script type="text/javascript">
38290 * @class Roo.bootstrap.layout.Border
38291 * @extends Roo.bootstrap.layout.Manager
38292 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38293 * please see: examples/bootstrap/nested.html<br><br>
38295 <b>The container the layout is rendered into can be either the body element or any other element.
38296 If it is not the body element, the container needs to either be an absolute positioned element,
38297 or you will need to add "position:relative" to the css of the container. You will also need to specify
38298 the container size if it is not the body element.</b>
38301 * Create a new Border
38302 * @param {Object} config Configuration options
38304 Roo.bootstrap.layout.Border = function(config){
38305 config = config || {};
38306 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38310 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38311 if(config[region]){
38312 config[region].region = region;
38313 this.addRegion(config[region]);
38319 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
38321 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38323 parent : false, // this might point to a 'nest' or a ???
38326 * Creates and adds a new region if it doesn't already exist.
38327 * @param {String} target The target region key (north, south, east, west or center).
38328 * @param {Object} config The regions config object
38329 * @return {BorderLayoutRegion} The new region
38331 addRegion : function(config)
38333 if(!this.regions[config.region]){
38334 var r = this.factory(config);
38335 this.bindRegion(r);
38337 return this.regions[config.region];
38341 bindRegion : function(r){
38342 this.regions[r.config.region] = r;
38344 r.on("visibilitychange", this.layout, this);
38345 r.on("paneladded", this.layout, this);
38346 r.on("panelremoved", this.layout, this);
38347 r.on("invalidated", this.layout, this);
38348 r.on("resized", this.onRegionResized, this);
38349 r.on("collapsed", this.onRegionCollapsed, this);
38350 r.on("expanded", this.onRegionExpanded, this);
38354 * Performs a layout update.
38356 layout : function()
38358 if(this.updating) {
38362 // render all the rebions if they have not been done alreayd?
38363 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38364 if(this.regions[region] && !this.regions[region].bodyEl){
38365 this.regions[region].onRender(this.el)
38369 var size = this.getViewSize();
38370 var w = size.width;
38371 var h = size.height;
38376 //var x = 0, y = 0;
38378 var rs = this.regions;
38379 var north = rs["north"];
38380 var south = rs["south"];
38381 var west = rs["west"];
38382 var east = rs["east"];
38383 var center = rs["center"];
38384 //if(this.hideOnLayout){ // not supported anymore
38385 //c.el.setStyle("display", "none");
38387 if(north && north.isVisible()){
38388 var b = north.getBox();
38389 var m = north.getMargins();
38390 b.width = w - (m.left+m.right);
38393 centerY = b.height + b.y + m.bottom;
38394 centerH -= centerY;
38395 north.updateBox(this.safeBox(b));
38397 if(south && south.isVisible()){
38398 var b = south.getBox();
38399 var m = south.getMargins();
38400 b.width = w - (m.left+m.right);
38402 var totalHeight = (b.height + m.top + m.bottom);
38403 b.y = h - totalHeight + m.top;
38404 centerH -= totalHeight;
38405 south.updateBox(this.safeBox(b));
38407 if(west && west.isVisible()){
38408 var b = west.getBox();
38409 var m = west.getMargins();
38410 b.height = centerH - (m.top+m.bottom);
38412 b.y = centerY + m.top;
38413 var totalWidth = (b.width + m.left + m.right);
38414 centerX += totalWidth;
38415 centerW -= totalWidth;
38416 west.updateBox(this.safeBox(b));
38418 if(east && east.isVisible()){
38419 var b = east.getBox();
38420 var m = east.getMargins();
38421 b.height = centerH - (m.top+m.bottom);
38422 var totalWidth = (b.width + m.left + m.right);
38423 b.x = w - totalWidth + m.left;
38424 b.y = centerY + m.top;
38425 centerW -= totalWidth;
38426 east.updateBox(this.safeBox(b));
38429 var m = center.getMargins();
38431 x: centerX + m.left,
38432 y: centerY + m.top,
38433 width: centerW - (m.left+m.right),
38434 height: centerH - (m.top+m.bottom)
38436 //if(this.hideOnLayout){
38437 //center.el.setStyle("display", "block");
38439 center.updateBox(this.safeBox(centerBox));
38442 this.fireEvent("layout", this);
38446 safeBox : function(box){
38447 box.width = Math.max(0, box.width);
38448 box.height = Math.max(0, box.height);
38453 * Adds a ContentPanel (or subclass) to this layout.
38454 * @param {String} target The target region key (north, south, east, west or center).
38455 * @param {Roo.ContentPanel} panel The panel to add
38456 * @return {Roo.ContentPanel} The added panel
38458 add : function(target, panel){
38460 target = target.toLowerCase();
38461 return this.regions[target].add(panel);
38465 * Remove a ContentPanel (or subclass) to this layout.
38466 * @param {String} target The target region key (north, south, east, west or center).
38467 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38468 * @return {Roo.ContentPanel} The removed panel
38470 remove : function(target, panel){
38471 target = target.toLowerCase();
38472 return this.regions[target].remove(panel);
38476 * Searches all regions for a panel with the specified id
38477 * @param {String} panelId
38478 * @return {Roo.ContentPanel} The panel or null if it wasn't found
38480 findPanel : function(panelId){
38481 var rs = this.regions;
38482 for(var target in rs){
38483 if(typeof rs[target] != "function"){
38484 var p = rs[target].getPanel(panelId);
38494 * Searches all regions for a panel with the specified id and activates (shows) it.
38495 * @param {String/ContentPanel} panelId The panels id or the panel itself
38496 * @return {Roo.ContentPanel} The shown panel or null
38498 showPanel : function(panelId) {
38499 var rs = this.regions;
38500 for(var target in rs){
38501 var r = rs[target];
38502 if(typeof r != "function"){
38503 if(r.hasPanel(panelId)){
38504 return r.showPanel(panelId);
38512 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38513 * @param {Roo.state.Provider} provider (optional) An alternate state provider
38516 restoreState : function(provider){
38518 provider = Roo.state.Manager;
38520 var sm = new Roo.LayoutStateManager();
38521 sm.init(this, provider);
38527 * Adds a xtype elements to the layout.
38531 xtype : 'ContentPanel',
38538 xtype : 'NestedLayoutPanel',
38544 items : [ ... list of content panels or nested layout panels.. ]
38548 * @param {Object} cfg Xtype definition of item to add.
38550 addxtype : function(cfg)
38552 // basically accepts a pannel...
38553 // can accept a layout region..!?!?
38554 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38557 // theory? children can only be panels??
38559 //if (!cfg.xtype.match(/Panel$/)) {
38564 if (typeof(cfg.region) == 'undefined') {
38565 Roo.log("Failed to add Panel, region was not set");
38569 var region = cfg.region;
38575 xitems = cfg.items;
38580 if ( region == 'center') {
38581 Roo.log("Center: " + cfg.title);
38587 case 'Content': // ContentPanel (el, cfg)
38588 case 'Scroll': // ContentPanel (el, cfg)
38590 cfg.autoCreate = cfg.autoCreate || true;
38591 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38593 // var el = this.el.createChild();
38594 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38597 this.add(region, ret);
38601 case 'TreePanel': // our new panel!
38602 cfg.el = this.el.createChild();
38603 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38604 this.add(region, ret);
38609 // create a new Layout (which is a Border Layout...
38611 var clayout = cfg.layout;
38612 clayout.el = this.el.createChild();
38613 clayout.items = clayout.items || [];
38617 // replace this exitems with the clayout ones..
38618 xitems = clayout.items;
38620 // force background off if it's in center...
38621 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38622 cfg.background = false;
38624 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
38627 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38628 //console.log('adding nested layout panel ' + cfg.toSource());
38629 this.add(region, ret);
38630 nb = {}; /// find first...
38635 // needs grid and region
38637 //var el = this.getRegion(region).el.createChild();
38639 *var el = this.el.createChild();
38640 // create the grid first...
38641 cfg.grid.container = el;
38642 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38645 if (region == 'center' && this.active ) {
38646 cfg.background = false;
38649 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38651 this.add(region, ret);
38653 if (cfg.background) {
38654 // render grid on panel activation (if panel background)
38655 ret.on('activate', function(gp) {
38656 if (!gp.grid.rendered) {
38657 // gp.grid.render(el);
38661 // cfg.grid.render(el);
38667 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38668 // it was the old xcomponent building that caused this before.
38669 // espeically if border is the top element in the tree.
38679 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38681 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38682 this.add(region, ret);
38686 throw "Can not add '" + cfg.xtype + "' to Border";
38692 this.beginUpdate();
38696 Roo.each(xitems, function(i) {
38697 region = nb && i.region ? i.region : false;
38699 var add = ret.addxtype(i);
38702 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38703 if (!i.background) {
38704 abn[region] = nb[region] ;
38711 // make the last non-background panel active..
38712 //if (nb) { Roo.log(abn); }
38715 for(var r in abn) {
38716 region = this.getRegion(r);
38718 // tried using nb[r], but it does not work..
38720 region.showPanel(abn[r]);
38731 factory : function(cfg)
38734 var validRegions = Roo.bootstrap.layout.Border.regions;
38736 var target = cfg.region;
38739 var r = Roo.bootstrap.layout;
38743 return new r.North(cfg);
38745 return new r.South(cfg);
38747 return new r.East(cfg);
38749 return new r.West(cfg);
38751 return new r.Center(cfg);
38753 throw 'Layout region "'+target+'" not supported.';
38760 * Ext JS Library 1.1.1
38761 * Copyright(c) 2006-2007, Ext JS, LLC.
38763 * Originally Released Under LGPL - original licence link has changed is not relivant.
38766 * <script type="text/javascript">
38770 * @class Roo.bootstrap.layout.Basic
38771 * @extends Roo.util.Observable
38772 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38773 * and does not have a titlebar, tabs or any other features. All it does is size and position
38774 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38775 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38776 * @cfg {string} region the region that it inhabits..
38777 * @cfg {bool} skipConfig skip config?
38781 Roo.bootstrap.layout.Basic = function(config){
38783 this.mgr = config.mgr;
38785 this.position = config.region;
38787 var skipConfig = config.skipConfig;
38791 * @scope Roo.BasicLayoutRegion
38795 * @event beforeremove
38796 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38797 * @param {Roo.LayoutRegion} this
38798 * @param {Roo.ContentPanel} panel The panel
38799 * @param {Object} e The cancel event object
38801 "beforeremove" : true,
38803 * @event invalidated
38804 * Fires when the layout for this region is changed.
38805 * @param {Roo.LayoutRegion} this
38807 "invalidated" : true,
38809 * @event visibilitychange
38810 * Fires when this region is shown or hidden
38811 * @param {Roo.LayoutRegion} this
38812 * @param {Boolean} visibility true or false
38814 "visibilitychange" : true,
38816 * @event paneladded
38817 * Fires when a panel is added.
38818 * @param {Roo.LayoutRegion} this
38819 * @param {Roo.ContentPanel} panel The panel
38821 "paneladded" : true,
38823 * @event panelremoved
38824 * Fires when a panel is removed.
38825 * @param {Roo.LayoutRegion} this
38826 * @param {Roo.ContentPanel} panel The panel
38828 "panelremoved" : true,
38830 * @event beforecollapse
38831 * Fires when this region before collapse.
38832 * @param {Roo.LayoutRegion} this
38834 "beforecollapse" : true,
38837 * Fires when this region is collapsed.
38838 * @param {Roo.LayoutRegion} this
38840 "collapsed" : true,
38843 * Fires when this region is expanded.
38844 * @param {Roo.LayoutRegion} this
38849 * Fires when this region is slid into view.
38850 * @param {Roo.LayoutRegion} this
38852 "slideshow" : true,
38855 * Fires when this region slides out of view.
38856 * @param {Roo.LayoutRegion} this
38858 "slidehide" : true,
38860 * @event panelactivated
38861 * Fires when a panel is activated.
38862 * @param {Roo.LayoutRegion} this
38863 * @param {Roo.ContentPanel} panel The activated panel
38865 "panelactivated" : true,
38868 * Fires when the user resizes this region.
38869 * @param {Roo.LayoutRegion} this
38870 * @param {Number} newSize The new size (width for east/west, height for north/south)
38874 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38875 this.panels = new Roo.util.MixedCollection();
38876 this.panels.getKey = this.getPanelId.createDelegate(this);
38878 this.activePanel = null;
38879 // ensure listeners are added...
38881 if (config.listeners || config.events) {
38882 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38883 listeners : config.listeners || {},
38884 events : config.events || {}
38888 if(skipConfig !== true){
38889 this.applyConfig(config);
38893 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38895 getPanelId : function(p){
38899 applyConfig : function(config){
38900 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38901 this.config = config;
38906 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38907 * the width, for horizontal (north, south) the height.
38908 * @param {Number} newSize The new width or height
38910 resizeTo : function(newSize){
38911 var el = this.el ? this.el :
38912 (this.activePanel ? this.activePanel.getEl() : null);
38914 switch(this.position){
38917 el.setWidth(newSize);
38918 this.fireEvent("resized", this, newSize);
38922 el.setHeight(newSize);
38923 this.fireEvent("resized", this, newSize);
38929 getBox : function(){
38930 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38933 getMargins : function(){
38934 return this.margins;
38937 updateBox : function(box){
38939 var el = this.activePanel.getEl();
38940 el.dom.style.left = box.x + "px";
38941 el.dom.style.top = box.y + "px";
38942 this.activePanel.setSize(box.width, box.height);
38946 * Returns the container element for this region.
38947 * @return {Roo.Element}
38949 getEl : function(){
38950 return this.activePanel;
38954 * Returns true if this region is currently visible.
38955 * @return {Boolean}
38957 isVisible : function(){
38958 return this.activePanel ? true : false;
38961 setActivePanel : function(panel){
38962 panel = this.getPanel(panel);
38963 if(this.activePanel && this.activePanel != panel){
38964 this.activePanel.setActiveState(false);
38965 this.activePanel.getEl().setLeftTop(-10000,-10000);
38967 this.activePanel = panel;
38968 panel.setActiveState(true);
38970 panel.setSize(this.box.width, this.box.height);
38972 this.fireEvent("panelactivated", this, panel);
38973 this.fireEvent("invalidated");
38977 * Show the specified panel.
38978 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38979 * @return {Roo.ContentPanel} The shown panel or null
38981 showPanel : function(panel){
38982 panel = this.getPanel(panel);
38984 this.setActivePanel(panel);
38990 * Get the active panel for this region.
38991 * @return {Roo.ContentPanel} The active panel or null
38993 getActivePanel : function(){
38994 return this.activePanel;
38998 * Add the passed ContentPanel(s)
38999 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39000 * @return {Roo.ContentPanel} The panel added (if only one was added)
39002 add : function(panel){
39003 if(arguments.length > 1){
39004 for(var i = 0, len = arguments.length; i < len; i++) {
39005 this.add(arguments[i]);
39009 if(this.hasPanel(panel)){
39010 this.showPanel(panel);
39013 var el = panel.getEl();
39014 if(el.dom.parentNode != this.mgr.el.dom){
39015 this.mgr.el.dom.appendChild(el.dom);
39017 if(panel.setRegion){
39018 panel.setRegion(this);
39020 this.panels.add(panel);
39021 el.setStyle("position", "absolute");
39022 if(!panel.background){
39023 this.setActivePanel(panel);
39024 if(this.config.initialSize && this.panels.getCount()==1){
39025 this.resizeTo(this.config.initialSize);
39028 this.fireEvent("paneladded", this, panel);
39033 * Returns true if the panel is in this region.
39034 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39035 * @return {Boolean}
39037 hasPanel : function(panel){
39038 if(typeof panel == "object"){ // must be panel obj
39039 panel = panel.getId();
39041 return this.getPanel(panel) ? true : false;
39045 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39046 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39047 * @param {Boolean} preservePanel Overrides the config preservePanel option
39048 * @return {Roo.ContentPanel} The panel that was removed
39050 remove : function(panel, preservePanel){
39051 panel = this.getPanel(panel);
39056 this.fireEvent("beforeremove", this, panel, e);
39057 if(e.cancel === true){
39060 var panelId = panel.getId();
39061 this.panels.removeKey(panelId);
39066 * Returns the panel specified or null if it's not in this region.
39067 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39068 * @return {Roo.ContentPanel}
39070 getPanel : function(id){
39071 if(typeof id == "object"){ // must be panel obj
39074 return this.panels.get(id);
39078 * Returns this regions position (north/south/east/west/center).
39081 getPosition: function(){
39082 return this.position;
39086 * Ext JS Library 1.1.1
39087 * Copyright(c) 2006-2007, Ext JS, LLC.
39089 * Originally Released Under LGPL - original licence link has changed is not relivant.
39092 * <script type="text/javascript">
39096 * @class Roo.bootstrap.layout.Region
39097 * @extends Roo.bootstrap.layout.Basic
39098 * This class represents a region in a layout manager.
39100 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
39101 * @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})
39102 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
39103 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
39104 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
39105 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
39106 * @cfg {String} title The title for the region (overrides panel titles)
39107 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
39108 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
39109 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
39110 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
39111 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
39112 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
39113 * the space available, similar to FireFox 1.5 tabs (defaults to false)
39114 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
39115 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
39116 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
39118 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
39119 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
39120 * @cfg {Boolean} disableTabTips True to disable tab tooltips
39121 * @cfg {Number} width For East/West panels
39122 * @cfg {Number} height For North/South panels
39123 * @cfg {Boolean} split To show the splitter
39124 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
39126 * @cfg {string} cls Extra CSS classes to add to region
39128 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
39129 * @cfg {string} region the region that it inhabits..
39132 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
39133 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
39135 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
39136 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
39137 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
39139 Roo.bootstrap.layout.Region = function(config)
39141 this.applyConfig(config);
39143 var mgr = config.mgr;
39144 var pos = config.region;
39145 config.skipConfig = true;
39146 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
39149 this.onRender(mgr.el);
39152 this.visible = true;
39153 this.collapsed = false;
39154 this.unrendered_panels = [];
39157 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
39159 position: '', // set by wrapper (eg. north/south etc..)
39160 unrendered_panels : null, // unrendered panels.
39162 tabPosition : false,
39164 mgr: false, // points to 'Border'
39167 createBody : function(){
39168 /** This region's body element
39169 * @type Roo.Element */
39170 this.bodyEl = this.el.createChild({
39172 cls: "roo-layout-panel-body tab-content" // bootstrap added...
39176 onRender: function(ctr, pos)
39178 var dh = Roo.DomHelper;
39179 /** This region's container element
39180 * @type Roo.Element */
39181 this.el = dh.append(ctr.dom, {
39183 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
39185 /** This region's title element
39186 * @type Roo.Element */
39188 this.titleEl = dh.append(this.el.dom, {
39190 unselectable: "on",
39191 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
39193 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
39194 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
39198 this.titleEl.enableDisplayMode();
39199 /** This region's title text element
39200 * @type HTMLElement */
39201 this.titleTextEl = this.titleEl.dom.firstChild;
39202 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
39204 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
39205 this.closeBtn.enableDisplayMode();
39206 this.closeBtn.on("click", this.closeClicked, this);
39207 this.closeBtn.hide();
39209 this.createBody(this.config);
39210 if(this.config.hideWhenEmpty){
39212 this.on("paneladded", this.validateVisibility, this);
39213 this.on("panelremoved", this.validateVisibility, this);
39215 if(this.autoScroll){
39216 this.bodyEl.setStyle("overflow", "auto");
39218 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
39220 //if(c.titlebar !== false){
39221 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
39222 this.titleEl.hide();
39224 this.titleEl.show();
39225 if(this.config.title){
39226 this.titleTextEl.innerHTML = this.config.title;
39230 if(this.config.collapsed){
39231 this.collapse(true);
39233 if(this.config.hidden){
39237 if (this.unrendered_panels && this.unrendered_panels.length) {
39238 for (var i =0;i< this.unrendered_panels.length; i++) {
39239 this.add(this.unrendered_panels[i]);
39241 this.unrendered_panels = null;
39247 applyConfig : function(c)
39250 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39251 var dh = Roo.DomHelper;
39252 if(c.titlebar !== false){
39253 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39254 this.collapseBtn.on("click", this.collapse, this);
39255 this.collapseBtn.enableDisplayMode();
39257 if(c.showPin === true || this.showPin){
39258 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39259 this.stickBtn.enableDisplayMode();
39260 this.stickBtn.on("click", this.expand, this);
39261 this.stickBtn.hide();
39266 /** This region's collapsed element
39267 * @type Roo.Element */
39270 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39271 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39274 if(c.floatable !== false){
39275 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39276 this.collapsedEl.on("click", this.collapseClick, this);
39279 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39280 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39281 id: "message", unselectable: "on", style:{"float":"left"}});
39282 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39284 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39285 this.expandBtn.on("click", this.expand, this);
39289 if(this.collapseBtn){
39290 this.collapseBtn.setVisible(c.collapsible == true);
39293 this.cmargins = c.cmargins || this.cmargins ||
39294 (this.position == "west" || this.position == "east" ?
39295 {top: 0, left: 2, right:2, bottom: 0} :
39296 {top: 2, left: 0, right:0, bottom: 2});
39298 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39301 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39303 this.autoScroll = c.autoScroll || false;
39308 this.duration = c.duration || .30;
39309 this.slideDuration = c.slideDuration || .45;
39314 * Returns true if this region is currently visible.
39315 * @return {Boolean}
39317 isVisible : function(){
39318 return this.visible;
39322 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39323 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
39325 //setCollapsedTitle : function(title){
39326 // title = title || " ";
39327 // if(this.collapsedTitleTextEl){
39328 // this.collapsedTitleTextEl.innerHTML = title;
39332 getBox : function(){
39334 // if(!this.collapsed){
39335 b = this.el.getBox(false, true);
39337 // b = this.collapsedEl.getBox(false, true);
39342 getMargins : function(){
39343 return this.margins;
39344 //return this.collapsed ? this.cmargins : this.margins;
39347 highlight : function(){
39348 this.el.addClass("x-layout-panel-dragover");
39351 unhighlight : function(){
39352 this.el.removeClass("x-layout-panel-dragover");
39355 updateBox : function(box)
39357 if (!this.bodyEl) {
39358 return; // not rendered yet..
39362 if(!this.collapsed){
39363 this.el.dom.style.left = box.x + "px";
39364 this.el.dom.style.top = box.y + "px";
39365 this.updateBody(box.width, box.height);
39367 this.collapsedEl.dom.style.left = box.x + "px";
39368 this.collapsedEl.dom.style.top = box.y + "px";
39369 this.collapsedEl.setSize(box.width, box.height);
39372 this.tabs.autoSizeTabs();
39376 updateBody : function(w, h)
39379 this.el.setWidth(w);
39380 w -= this.el.getBorderWidth("rl");
39381 if(this.config.adjustments){
39382 w += this.config.adjustments[0];
39385 if(h !== null && h > 0){
39386 this.el.setHeight(h);
39387 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39388 h -= this.el.getBorderWidth("tb");
39389 if(this.config.adjustments){
39390 h += this.config.adjustments[1];
39392 this.bodyEl.setHeight(h);
39394 h = this.tabs.syncHeight(h);
39397 if(this.panelSize){
39398 w = w !== null ? w : this.panelSize.width;
39399 h = h !== null ? h : this.panelSize.height;
39401 if(this.activePanel){
39402 var el = this.activePanel.getEl();
39403 w = w !== null ? w : el.getWidth();
39404 h = h !== null ? h : el.getHeight();
39405 this.panelSize = {width: w, height: h};
39406 this.activePanel.setSize(w, h);
39408 if(Roo.isIE && this.tabs){
39409 this.tabs.el.repaint();
39414 * Returns the container element for this region.
39415 * @return {Roo.Element}
39417 getEl : function(){
39422 * Hides this region.
39425 //if(!this.collapsed){
39426 this.el.dom.style.left = "-2000px";
39429 // this.collapsedEl.dom.style.left = "-2000px";
39430 // this.collapsedEl.hide();
39432 this.visible = false;
39433 this.fireEvent("visibilitychange", this, false);
39437 * Shows this region if it was previously hidden.
39440 //if(!this.collapsed){
39443 // this.collapsedEl.show();
39445 this.visible = true;
39446 this.fireEvent("visibilitychange", this, true);
39449 closeClicked : function(){
39450 if(this.activePanel){
39451 this.remove(this.activePanel);
39455 collapseClick : function(e){
39457 e.stopPropagation();
39460 e.stopPropagation();
39466 * Collapses this region.
39467 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39470 collapse : function(skipAnim, skipCheck = false){
39471 if(this.collapsed) {
39475 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39477 this.collapsed = true;
39479 this.split.el.hide();
39481 if(this.config.animate && skipAnim !== true){
39482 this.fireEvent("invalidated", this);
39483 this.animateCollapse();
39485 this.el.setLocation(-20000,-20000);
39487 this.collapsedEl.show();
39488 this.fireEvent("collapsed", this);
39489 this.fireEvent("invalidated", this);
39495 animateCollapse : function(){
39500 * Expands this region if it was previously collapsed.
39501 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39502 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39505 expand : function(e, skipAnim){
39507 e.stopPropagation();
39509 if(!this.collapsed || this.el.hasActiveFx()) {
39513 this.afterSlideIn();
39516 this.collapsed = false;
39517 if(this.config.animate && skipAnim !== true){
39518 this.animateExpand();
39522 this.split.el.show();
39524 this.collapsedEl.setLocation(-2000,-2000);
39525 this.collapsedEl.hide();
39526 this.fireEvent("invalidated", this);
39527 this.fireEvent("expanded", this);
39531 animateExpand : function(){
39535 initTabs : function()
39537 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39539 var ts = new Roo.bootstrap.panel.Tabs({
39540 el: this.bodyEl.dom,
39542 tabPosition: this.tabPosition ? this.tabPosition : 'top',
39543 disableTooltips: this.config.disableTabTips,
39544 toolbar : this.config.toolbar
39547 if(this.config.hideTabs){
39548 ts.stripWrap.setDisplayed(false);
39551 ts.resizeTabs = this.config.resizeTabs === true;
39552 ts.minTabWidth = this.config.minTabWidth || 40;
39553 ts.maxTabWidth = this.config.maxTabWidth || 250;
39554 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39555 ts.monitorResize = false;
39556 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39557 ts.bodyEl.addClass('roo-layout-tabs-body');
39558 this.panels.each(this.initPanelAsTab, this);
39561 initPanelAsTab : function(panel){
39562 var ti = this.tabs.addTab(
39566 this.config.closeOnTab && panel.isClosable(),
39569 if(panel.tabTip !== undefined){
39570 ti.setTooltip(panel.tabTip);
39572 ti.on("activate", function(){
39573 this.setActivePanel(panel);
39576 if(this.config.closeOnTab){
39577 ti.on("beforeclose", function(t, e){
39579 this.remove(panel);
39583 panel.tabItem = ti;
39588 updatePanelTitle : function(panel, title)
39590 if(this.activePanel == panel){
39591 this.updateTitle(title);
39594 var ti = this.tabs.getTab(panel.getEl().id);
39596 if(panel.tabTip !== undefined){
39597 ti.setTooltip(panel.tabTip);
39602 updateTitle : function(title){
39603 if(this.titleTextEl && !this.config.title){
39604 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
39608 setActivePanel : function(panel)
39610 panel = this.getPanel(panel);
39611 if(this.activePanel && this.activePanel != panel){
39612 if(this.activePanel.setActiveState(false) === false){
39616 this.activePanel = panel;
39617 panel.setActiveState(true);
39618 if(this.panelSize){
39619 panel.setSize(this.panelSize.width, this.panelSize.height);
39622 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39624 this.updateTitle(panel.getTitle());
39626 this.fireEvent("invalidated", this);
39628 this.fireEvent("panelactivated", this, panel);
39632 * Shows the specified panel.
39633 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39634 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39636 showPanel : function(panel)
39638 panel = this.getPanel(panel);
39641 var tab = this.tabs.getTab(panel.getEl().id);
39642 if(tab.isHidden()){
39643 this.tabs.unhideTab(tab.id);
39647 this.setActivePanel(panel);
39654 * Get the active panel for this region.
39655 * @return {Roo.ContentPanel} The active panel or null
39657 getActivePanel : function(){
39658 return this.activePanel;
39661 validateVisibility : function(){
39662 if(this.panels.getCount() < 1){
39663 this.updateTitle(" ");
39664 this.closeBtn.hide();
39667 if(!this.isVisible()){
39674 * Adds the passed ContentPanel(s) to this region.
39675 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39676 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39678 add : function(panel)
39680 if(arguments.length > 1){
39681 for(var i = 0, len = arguments.length; i < len; i++) {
39682 this.add(arguments[i]);
39687 // if we have not been rendered yet, then we can not really do much of this..
39688 if (!this.bodyEl) {
39689 this.unrendered_panels.push(panel);
39696 if(this.hasPanel(panel)){
39697 this.showPanel(panel);
39700 panel.setRegion(this);
39701 this.panels.add(panel);
39702 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39703 // sinle panel - no tab...?? would it not be better to render it with the tabs,
39704 // and hide them... ???
39705 this.bodyEl.dom.appendChild(panel.getEl().dom);
39706 if(panel.background !== true){
39707 this.setActivePanel(panel);
39709 this.fireEvent("paneladded", this, panel);
39716 this.initPanelAsTab(panel);
39720 if(panel.background !== true){
39721 this.tabs.activate(panel.getEl().id);
39723 this.fireEvent("paneladded", this, panel);
39728 * Hides the tab for the specified panel.
39729 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39731 hidePanel : function(panel){
39732 if(this.tabs && (panel = this.getPanel(panel))){
39733 this.tabs.hideTab(panel.getEl().id);
39738 * Unhides the tab for a previously hidden panel.
39739 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39741 unhidePanel : function(panel){
39742 if(this.tabs && (panel = this.getPanel(panel))){
39743 this.tabs.unhideTab(panel.getEl().id);
39747 clearPanels : function(){
39748 while(this.panels.getCount() > 0){
39749 this.remove(this.panels.first());
39754 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39755 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39756 * @param {Boolean} preservePanel Overrides the config preservePanel option
39757 * @return {Roo.ContentPanel} The panel that was removed
39759 remove : function(panel, preservePanel)
39761 panel = this.getPanel(panel);
39766 this.fireEvent("beforeremove", this, panel, e);
39767 if(e.cancel === true){
39770 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39771 var panelId = panel.getId();
39772 this.panels.removeKey(panelId);
39774 document.body.appendChild(panel.getEl().dom);
39777 this.tabs.removeTab(panel.getEl().id);
39778 }else if (!preservePanel){
39779 this.bodyEl.dom.removeChild(panel.getEl().dom);
39781 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39782 var p = this.panels.first();
39783 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39784 tempEl.appendChild(p.getEl().dom);
39785 this.bodyEl.update("");
39786 this.bodyEl.dom.appendChild(p.getEl().dom);
39788 this.updateTitle(p.getTitle());
39790 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39791 this.setActivePanel(p);
39793 panel.setRegion(null);
39794 if(this.activePanel == panel){
39795 this.activePanel = null;
39797 if(this.config.autoDestroy !== false && preservePanel !== true){
39798 try{panel.destroy();}catch(e){}
39800 this.fireEvent("panelremoved", this, panel);
39805 * Returns the TabPanel component used by this region
39806 * @return {Roo.TabPanel}
39808 getTabs : function(){
39812 createTool : function(parentEl, className){
39813 var btn = Roo.DomHelper.append(parentEl, {
39815 cls: "x-layout-tools-button",
39818 cls: "roo-layout-tools-button-inner " + className,
39822 btn.addClassOnOver("roo-layout-tools-button-over");
39827 * Ext JS Library 1.1.1
39828 * Copyright(c) 2006-2007, Ext JS, LLC.
39830 * Originally Released Under LGPL - original licence link has changed is not relivant.
39833 * <script type="text/javascript">
39839 * @class Roo.SplitLayoutRegion
39840 * @extends Roo.LayoutRegion
39841 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39843 Roo.bootstrap.layout.Split = function(config){
39844 this.cursor = config.cursor;
39845 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39848 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39850 splitTip : "Drag to resize.",
39851 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39852 useSplitTips : false,
39854 applyConfig : function(config){
39855 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39858 onRender : function(ctr,pos) {
39860 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39861 if(!this.config.split){
39866 var splitEl = Roo.DomHelper.append(ctr.dom, {
39868 id: this.el.id + "-split",
39869 cls: "roo-layout-split roo-layout-split-"+this.position,
39872 /** The SplitBar for this region
39873 * @type Roo.SplitBar */
39874 // does not exist yet...
39875 Roo.log([this.position, this.orientation]);
39877 this.split = new Roo.bootstrap.SplitBar({
39878 dragElement : splitEl,
39879 resizingElement: this.el,
39880 orientation : this.orientation
39883 this.split.on("moved", this.onSplitMove, this);
39884 this.split.useShim = this.config.useShim === true;
39885 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39886 if(this.useSplitTips){
39887 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39889 //if(config.collapsible){
39890 // this.split.el.on("dblclick", this.collapse, this);
39893 if(typeof this.config.minSize != "undefined"){
39894 this.split.minSize = this.config.minSize;
39896 if(typeof this.config.maxSize != "undefined"){
39897 this.split.maxSize = this.config.maxSize;
39899 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39900 this.hideSplitter();
39905 getHMaxSize : function(){
39906 var cmax = this.config.maxSize || 10000;
39907 var center = this.mgr.getRegion("center");
39908 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39911 getVMaxSize : function(){
39912 var cmax = this.config.maxSize || 10000;
39913 var center = this.mgr.getRegion("center");
39914 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39917 onSplitMove : function(split, newSize){
39918 this.fireEvent("resized", this, newSize);
39922 * Returns the {@link Roo.SplitBar} for this region.
39923 * @return {Roo.SplitBar}
39925 getSplitBar : function(){
39930 this.hideSplitter();
39931 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39934 hideSplitter : function(){
39936 this.split.el.setLocation(-2000,-2000);
39937 this.split.el.hide();
39943 this.split.el.show();
39945 Roo.bootstrap.layout.Split.superclass.show.call(this);
39948 beforeSlide: function(){
39949 if(Roo.isGecko){// firefox overflow auto bug workaround
39950 this.bodyEl.clip();
39952 this.tabs.bodyEl.clip();
39954 if(this.activePanel){
39955 this.activePanel.getEl().clip();
39957 if(this.activePanel.beforeSlide){
39958 this.activePanel.beforeSlide();
39964 afterSlide : function(){
39965 if(Roo.isGecko){// firefox overflow auto bug workaround
39966 this.bodyEl.unclip();
39968 this.tabs.bodyEl.unclip();
39970 if(this.activePanel){
39971 this.activePanel.getEl().unclip();
39972 if(this.activePanel.afterSlide){
39973 this.activePanel.afterSlide();
39979 initAutoHide : function(){
39980 if(this.autoHide !== false){
39981 if(!this.autoHideHd){
39982 var st = new Roo.util.DelayedTask(this.slideIn, this);
39983 this.autoHideHd = {
39984 "mouseout": function(e){
39985 if(!e.within(this.el, true)){
39989 "mouseover" : function(e){
39995 this.el.on(this.autoHideHd);
39999 clearAutoHide : function(){
40000 if(this.autoHide !== false){
40001 this.el.un("mouseout", this.autoHideHd.mouseout);
40002 this.el.un("mouseover", this.autoHideHd.mouseover);
40006 clearMonitor : function(){
40007 Roo.get(document).un("click", this.slideInIf, this);
40010 // these names are backwards but not changed for compat
40011 slideOut : function(){
40012 if(this.isSlid || this.el.hasActiveFx()){
40015 this.isSlid = true;
40016 if(this.collapseBtn){
40017 this.collapseBtn.hide();
40019 this.closeBtnState = this.closeBtn.getStyle('display');
40020 this.closeBtn.hide();
40022 this.stickBtn.show();
40025 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
40026 this.beforeSlide();
40027 this.el.setStyle("z-index", 10001);
40028 this.el.slideIn(this.getSlideAnchor(), {
40029 callback: function(){
40031 this.initAutoHide();
40032 Roo.get(document).on("click", this.slideInIf, this);
40033 this.fireEvent("slideshow", this);
40040 afterSlideIn : function(){
40041 this.clearAutoHide();
40042 this.isSlid = false;
40043 this.clearMonitor();
40044 this.el.setStyle("z-index", "");
40045 if(this.collapseBtn){
40046 this.collapseBtn.show();
40048 this.closeBtn.setStyle('display', this.closeBtnState);
40050 this.stickBtn.hide();
40052 this.fireEvent("slidehide", this);
40055 slideIn : function(cb){
40056 if(!this.isSlid || this.el.hasActiveFx()){
40060 this.isSlid = false;
40061 this.beforeSlide();
40062 this.el.slideOut(this.getSlideAnchor(), {
40063 callback: function(){
40064 this.el.setLeftTop(-10000, -10000);
40066 this.afterSlideIn();
40074 slideInIf : function(e){
40075 if(!e.within(this.el)){
40080 animateCollapse : function(){
40081 this.beforeSlide();
40082 this.el.setStyle("z-index", 20000);
40083 var anchor = this.getSlideAnchor();
40084 this.el.slideOut(anchor, {
40085 callback : function(){
40086 this.el.setStyle("z-index", "");
40087 this.collapsedEl.slideIn(anchor, {duration:.3});
40089 this.el.setLocation(-10000,-10000);
40091 this.fireEvent("collapsed", this);
40098 animateExpand : function(){
40099 this.beforeSlide();
40100 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
40101 this.el.setStyle("z-index", 20000);
40102 this.collapsedEl.hide({
40105 this.el.slideIn(this.getSlideAnchor(), {
40106 callback : function(){
40107 this.el.setStyle("z-index", "");
40110 this.split.el.show();
40112 this.fireEvent("invalidated", this);
40113 this.fireEvent("expanded", this);
40141 getAnchor : function(){
40142 return this.anchors[this.position];
40145 getCollapseAnchor : function(){
40146 return this.canchors[this.position];
40149 getSlideAnchor : function(){
40150 return this.sanchors[this.position];
40153 getAlignAdj : function(){
40154 var cm = this.cmargins;
40155 switch(this.position){
40171 getExpandAdj : function(){
40172 var c = this.collapsedEl, cm = this.cmargins;
40173 switch(this.position){
40175 return [-(cm.right+c.getWidth()+cm.left), 0];
40178 return [cm.right+c.getWidth()+cm.left, 0];
40181 return [0, -(cm.top+cm.bottom+c.getHeight())];
40184 return [0, cm.top+cm.bottom+c.getHeight()];
40190 * Ext JS Library 1.1.1
40191 * Copyright(c) 2006-2007, Ext JS, LLC.
40193 * Originally Released Under LGPL - original licence link has changed is not relivant.
40196 * <script type="text/javascript">
40199 * These classes are private internal classes
40201 Roo.bootstrap.layout.Center = function(config){
40202 config.region = "center";
40203 Roo.bootstrap.layout.Region.call(this, config);
40204 this.visible = true;
40205 this.minWidth = config.minWidth || 20;
40206 this.minHeight = config.minHeight || 20;
40209 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
40211 // center panel can't be hidden
40215 // center panel can't be hidden
40218 getMinWidth: function(){
40219 return this.minWidth;
40222 getMinHeight: function(){
40223 return this.minHeight;
40237 Roo.bootstrap.layout.North = function(config)
40239 config.region = 'north';
40240 config.cursor = 'n-resize';
40242 Roo.bootstrap.layout.Split.call(this, config);
40246 this.split.placement = Roo.bootstrap.SplitBar.TOP;
40247 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40248 this.split.el.addClass("roo-layout-split-v");
40250 //var size = config.initialSize || config.height;
40251 //if(this.el && typeof size != "undefined"){
40252 // this.el.setHeight(size);
40255 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40257 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40260 onRender : function(ctr, pos)
40262 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40263 var size = this.config.initialSize || this.config.height;
40264 if(this.el && typeof size != "undefined"){
40265 this.el.setHeight(size);
40270 getBox : function(){
40271 if(this.collapsed){
40272 return this.collapsedEl.getBox();
40274 var box = this.el.getBox();
40276 box.height += this.split.el.getHeight();
40281 updateBox : function(box){
40282 if(this.split && !this.collapsed){
40283 box.height -= this.split.el.getHeight();
40284 this.split.el.setLeft(box.x);
40285 this.split.el.setTop(box.y+box.height);
40286 this.split.el.setWidth(box.width);
40288 if(this.collapsed){
40289 this.updateBody(box.width, null);
40291 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40299 Roo.bootstrap.layout.South = function(config){
40300 config.region = 'south';
40301 config.cursor = 's-resize';
40302 Roo.bootstrap.layout.Split.call(this, config);
40304 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40305 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40306 this.split.el.addClass("roo-layout-split-v");
40311 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40312 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40314 onRender : function(ctr, pos)
40316 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40317 var size = this.config.initialSize || this.config.height;
40318 if(this.el && typeof size != "undefined"){
40319 this.el.setHeight(size);
40324 getBox : function(){
40325 if(this.collapsed){
40326 return this.collapsedEl.getBox();
40328 var box = this.el.getBox();
40330 var sh = this.split.el.getHeight();
40337 updateBox : function(box){
40338 if(this.split && !this.collapsed){
40339 var sh = this.split.el.getHeight();
40342 this.split.el.setLeft(box.x);
40343 this.split.el.setTop(box.y-sh);
40344 this.split.el.setWidth(box.width);
40346 if(this.collapsed){
40347 this.updateBody(box.width, null);
40349 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40353 Roo.bootstrap.layout.East = function(config){
40354 config.region = "east";
40355 config.cursor = "e-resize";
40356 Roo.bootstrap.layout.Split.call(this, config);
40358 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40359 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40360 this.split.el.addClass("roo-layout-split-h");
40364 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40365 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40367 onRender : function(ctr, pos)
40369 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40370 var size = this.config.initialSize || this.config.width;
40371 if(this.el && typeof size != "undefined"){
40372 this.el.setWidth(size);
40377 getBox : function(){
40378 if(this.collapsed){
40379 return this.collapsedEl.getBox();
40381 var box = this.el.getBox();
40383 var sw = this.split.el.getWidth();
40390 updateBox : function(box){
40391 if(this.split && !this.collapsed){
40392 var sw = this.split.el.getWidth();
40394 this.split.el.setLeft(box.x);
40395 this.split.el.setTop(box.y);
40396 this.split.el.setHeight(box.height);
40399 if(this.collapsed){
40400 this.updateBody(null, box.height);
40402 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40406 Roo.bootstrap.layout.West = function(config){
40407 config.region = "west";
40408 config.cursor = "w-resize";
40410 Roo.bootstrap.layout.Split.call(this, config);
40412 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40413 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40414 this.split.el.addClass("roo-layout-split-h");
40418 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40419 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40421 onRender: function(ctr, pos)
40423 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40424 var size = this.config.initialSize || this.config.width;
40425 if(typeof size != "undefined"){
40426 this.el.setWidth(size);
40430 getBox : function(){
40431 if(this.collapsed){
40432 return this.collapsedEl.getBox();
40434 var box = this.el.getBox();
40435 if (box.width == 0) {
40436 box.width = this.config.width; // kludge?
40439 box.width += this.split.el.getWidth();
40444 updateBox : function(box){
40445 if(this.split && !this.collapsed){
40446 var sw = this.split.el.getWidth();
40448 this.split.el.setLeft(box.x+box.width);
40449 this.split.el.setTop(box.y);
40450 this.split.el.setHeight(box.height);
40452 if(this.collapsed){
40453 this.updateBody(null, box.height);
40455 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40457 });Roo.namespace("Roo.bootstrap.panel");/*
40459 * Ext JS Library 1.1.1
40460 * Copyright(c) 2006-2007, Ext JS, LLC.
40462 * Originally Released Under LGPL - original licence link has changed is not relivant.
40465 * <script type="text/javascript">
40468 * @class Roo.ContentPanel
40469 * @extends Roo.util.Observable
40470 * A basic ContentPanel element.
40471 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
40472 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
40473 * @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
40474 * @cfg {Boolean} closable True if the panel can be closed/removed
40475 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
40476 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40477 * @cfg {Toolbar} toolbar A toolbar for this panel
40478 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
40479 * @cfg {String} title The title for this panel
40480 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40481 * @cfg {String} url Calls {@link #setUrl} with this value
40482 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40483 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
40484 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
40485 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
40486 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
40487 * @cfg {Boolean} badges render the badges
40488 * @cfg {String} cls extra classes to use
40489 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40492 * Create a new ContentPanel.
40493 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40494 * @param {String/Object} config A string to set only the title or a config object
40495 * @param {String} content (optional) Set the HTML content for this panel
40496 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40498 Roo.bootstrap.panel.Content = function( config){
40500 this.tpl = config.tpl || false;
40502 var el = config.el;
40503 var content = config.content;
40505 if(config.autoCreate){ // xtype is available if this is called from factory
40508 this.el = Roo.get(el);
40509 if(!this.el && config && config.autoCreate){
40510 if(typeof config.autoCreate == "object"){
40511 if(!config.autoCreate.id){
40512 config.autoCreate.id = config.id||el;
40514 this.el = Roo.DomHelper.append(document.body,
40515 config.autoCreate, true);
40519 cls: (config.cls || '') +
40520 (config.background ? ' bg-' + config.background : '') +
40521 " roo-layout-inactive-content",
40524 if (config.iframe) {
40528 style : 'border: 0px',
40529 src : 'about:blank'
40535 elcfg.html = config.html;
40539 this.el = Roo.DomHelper.append(document.body, elcfg , true);
40540 if (config.iframe) {
40541 this.iframeEl = this.el.select('iframe',true).first();
40546 this.closable = false;
40547 this.loaded = false;
40548 this.active = false;
40551 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40553 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40555 this.wrapEl = this.el; //this.el.wrap();
40557 if (config.toolbar.items) {
40558 ti = config.toolbar.items ;
40559 delete config.toolbar.items ;
40563 this.toolbar.render(this.wrapEl, 'before');
40564 for(var i =0;i < ti.length;i++) {
40565 // Roo.log(['add child', items[i]]);
40566 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40568 this.toolbar.items = nitems;
40569 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40570 delete config.toolbar;
40574 // xtype created footer. - not sure if will work as we normally have to render first..
40575 if (this.footer && !this.footer.el && this.footer.xtype) {
40576 if (!this.wrapEl) {
40577 this.wrapEl = this.el.wrap();
40580 this.footer.container = this.wrapEl.createChild();
40582 this.footer = Roo.factory(this.footer, Roo);
40587 if(typeof config == "string"){
40588 this.title = config;
40590 Roo.apply(this, config);
40594 this.resizeEl = Roo.get(this.resizeEl, true);
40596 this.resizeEl = this.el;
40598 // handle view.xtype
40606 * Fires when this panel is activated.
40607 * @param {Roo.ContentPanel} this
40611 * @event deactivate
40612 * Fires when this panel is activated.
40613 * @param {Roo.ContentPanel} this
40615 "deactivate" : true,
40619 * Fires when this panel is resized if fitToFrame is true.
40620 * @param {Roo.ContentPanel} this
40621 * @param {Number} width The width after any component adjustments
40622 * @param {Number} height The height after any component adjustments
40628 * Fires when this tab is created
40629 * @param {Roo.ContentPanel} this
40635 * Fires when this content is scrolled
40636 * @param {Roo.ContentPanel} this
40637 * @param {Event} scrollEvent
40648 if(this.autoScroll && !this.iframe){
40649 this.resizeEl.setStyle("overflow", "auto");
40650 this.resizeEl.on('scroll', this.onScroll, this);
40652 // fix randome scrolling
40653 //this.el.on('scroll', function() {
40654 // Roo.log('fix random scolling');
40655 // this.scrollTo('top',0);
40658 content = content || this.content;
40660 this.setContent(content);
40662 if(config && config.url){
40663 this.setUrl(this.url, this.params, this.loadOnce);
40668 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40670 if (this.view && typeof(this.view.xtype) != 'undefined') {
40671 this.view.el = this.el.appendChild(document.createElement("div"));
40672 this.view = Roo.factory(this.view);
40673 this.view.render && this.view.render(false, '');
40677 this.fireEvent('render', this);
40680 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40690 /* Resize Element - use this to work out scroll etc. */
40693 setRegion : function(region){
40694 this.region = region;
40695 this.setActiveClass(region && !this.background);
40699 setActiveClass: function(state)
40702 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40703 this.el.setStyle('position','relative');
40705 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40706 this.el.setStyle('position', 'absolute');
40711 * Returns the toolbar for this Panel if one was configured.
40712 * @return {Roo.Toolbar}
40714 getToolbar : function(){
40715 return this.toolbar;
40718 setActiveState : function(active)
40720 this.active = active;
40721 this.setActiveClass(active);
40723 if(this.fireEvent("deactivate", this) === false){
40728 this.fireEvent("activate", this);
40732 * Updates this panel's element (not for iframe)
40733 * @param {String} content The new content
40734 * @param {Boolean} loadScripts (optional) true to look for and process scripts
40736 setContent : function(content, loadScripts){
40741 this.el.update(content, loadScripts);
40744 ignoreResize : function(w, h){
40745 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40748 this.lastSize = {width: w, height: h};
40753 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40754 * @return {Roo.UpdateManager} The UpdateManager
40756 getUpdateManager : function(){
40760 return this.el.getUpdateManager();
40763 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40764 * Does not work with IFRAME contents
40765 * @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:
40768 url: "your-url.php",
40769 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40770 callback: yourFunction,
40771 scope: yourObject, //(optional scope)
40774 text: "Loading...",
40780 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40781 * 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.
40782 * @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}
40783 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40784 * @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.
40785 * @return {Roo.ContentPanel} this
40793 var um = this.el.getUpdateManager();
40794 um.update.apply(um, arguments);
40800 * 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.
40801 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40802 * @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)
40803 * @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)
40804 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40806 setUrl : function(url, params, loadOnce){
40808 this.iframeEl.dom.src = url;
40812 if(this.refreshDelegate){
40813 this.removeListener("activate", this.refreshDelegate);
40815 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40816 this.on("activate", this.refreshDelegate);
40817 return this.el.getUpdateManager();
40820 _handleRefresh : function(url, params, loadOnce){
40821 if(!loadOnce || !this.loaded){
40822 var updater = this.el.getUpdateManager();
40823 updater.update(url, params, this._setLoaded.createDelegate(this));
40827 _setLoaded : function(){
40828 this.loaded = true;
40832 * Returns this panel's id
40835 getId : function(){
40840 * Returns this panel's element - used by regiosn to add.
40841 * @return {Roo.Element}
40843 getEl : function(){
40844 return this.wrapEl || this.el;
40849 adjustForComponents : function(width, height)
40851 //Roo.log('adjustForComponents ');
40852 if(this.resizeEl != this.el){
40853 width -= this.el.getFrameWidth('lr');
40854 height -= this.el.getFrameWidth('tb');
40857 var te = this.toolbar.getEl();
40858 te.setWidth(width);
40859 height -= te.getHeight();
40862 var te = this.footer.getEl();
40863 te.setWidth(width);
40864 height -= te.getHeight();
40868 if(this.adjustments){
40869 width += this.adjustments[0];
40870 height += this.adjustments[1];
40872 return {"width": width, "height": height};
40875 setSize : function(width, height){
40876 if(this.fitToFrame && !this.ignoreResize(width, height)){
40877 if(this.fitContainer && this.resizeEl != this.el){
40878 this.el.setSize(width, height);
40880 var size = this.adjustForComponents(width, height);
40882 this.iframeEl.setSize(width,height);
40885 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40886 this.fireEvent('resize', this, size.width, size.height);
40893 * Returns this panel's title
40896 getTitle : function(){
40898 if (typeof(this.title) != 'object') {
40903 for (var k in this.title) {
40904 if (!this.title.hasOwnProperty(k)) {
40908 if (k.indexOf('-') >= 0) {
40909 var s = k.split('-');
40910 for (var i = 0; i<s.length; i++) {
40911 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40914 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40921 * Set this panel's title
40922 * @param {String} title
40924 setTitle : function(title){
40925 this.title = title;
40927 this.region.updatePanelTitle(this, title);
40932 * Returns true is this panel was configured to be closable
40933 * @return {Boolean}
40935 isClosable : function(){
40936 return this.closable;
40939 beforeSlide : function(){
40941 this.resizeEl.clip();
40944 afterSlide : function(){
40946 this.resizeEl.unclip();
40950 * Force a content refresh from the URL specified in the {@link #setUrl} method.
40951 * Will fail silently if the {@link #setUrl} method has not been called.
40952 * This does not activate the panel, just updates its content.
40954 refresh : function(){
40955 if(this.refreshDelegate){
40956 this.loaded = false;
40957 this.refreshDelegate();
40962 * Destroys this panel
40964 destroy : function(){
40965 this.el.removeAllListeners();
40966 var tempEl = document.createElement("span");
40967 tempEl.appendChild(this.el.dom);
40968 tempEl.innerHTML = "";
40974 * form - if the content panel contains a form - this is a reference to it.
40975 * @type {Roo.form.Form}
40979 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40980 * This contains a reference to it.
40986 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40996 * @param {Object} cfg Xtype definition of item to add.
41000 getChildContainer: function () {
41001 return this.getEl();
41005 onScroll : function(e)
41007 this.fireEvent('scroll', this, e);
41012 var ret = new Roo.factory(cfg);
41017 if (cfg.xtype.match(/^Form$/)) {
41020 //if (this.footer) {
41021 // el = this.footer.container.insertSibling(false, 'before');
41023 el = this.el.createChild();
41026 this.form = new Roo.form.Form(cfg);
41029 if ( this.form.allItems.length) {
41030 this.form.render(el.dom);
41034 // should only have one of theses..
41035 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
41036 // views.. should not be just added - used named prop 'view''
41038 cfg.el = this.el.appendChild(document.createElement("div"));
41041 var ret = new Roo.factory(cfg);
41043 ret.render && ret.render(false, ''); // render blank..
41053 * @class Roo.bootstrap.panel.Grid
41054 * @extends Roo.bootstrap.panel.Content
41056 * Create a new GridPanel.
41057 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
41058 * @param {Object} config A the config object
41064 Roo.bootstrap.panel.Grid = function(config)
41068 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
41069 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
41071 config.el = this.wrapper;
41072 //this.el = this.wrapper;
41074 if (config.container) {
41075 // ctor'ed from a Border/panel.grid
41078 this.wrapper.setStyle("overflow", "hidden");
41079 this.wrapper.addClass('roo-grid-container');
41084 if(config.toolbar){
41085 var tool_el = this.wrapper.createChild();
41086 this.toolbar = Roo.factory(config.toolbar);
41088 if (config.toolbar.items) {
41089 ti = config.toolbar.items ;
41090 delete config.toolbar.items ;
41094 this.toolbar.render(tool_el);
41095 for(var i =0;i < ti.length;i++) {
41096 // Roo.log(['add child', items[i]]);
41097 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
41099 this.toolbar.items = nitems;
41101 delete config.toolbar;
41104 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
41105 config.grid.scrollBody = true;;
41106 config.grid.monitorWindowResize = false; // turn off autosizing
41107 config.grid.autoHeight = false;
41108 config.grid.autoWidth = false;
41110 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
41112 if (config.background) {
41113 // render grid on panel activation (if panel background)
41114 this.on('activate', function(gp) {
41115 if (!gp.grid.rendered) {
41116 gp.grid.render(this.wrapper);
41117 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
41122 this.grid.render(this.wrapper);
41123 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
41126 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
41127 // ??? needed ??? config.el = this.wrapper;
41132 // xtype created footer. - not sure if will work as we normally have to render first..
41133 if (this.footer && !this.footer.el && this.footer.xtype) {
41135 var ctr = this.grid.getView().getFooterPanel(true);
41136 this.footer.dataSource = this.grid.dataSource;
41137 this.footer = Roo.factory(this.footer, Roo);
41138 this.footer.render(ctr);
41148 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
41149 getId : function(){
41150 return this.grid.id;
41154 * Returns the grid for this panel
41155 * @return {Roo.bootstrap.Table}
41157 getGrid : function(){
41161 setSize : function(width, height){
41162 if(!this.ignoreResize(width, height)){
41163 var grid = this.grid;
41164 var size = this.adjustForComponents(width, height);
41165 // tfoot is not a footer?
41168 var gridel = grid.getGridEl();
41169 gridel.setSize(size.width, size.height);
41171 var tbd = grid.getGridEl().select('tbody', true).first();
41172 var thd = grid.getGridEl().select('thead',true).first();
41173 var tbf= grid.getGridEl().select('tfoot', true).first();
41176 size.height -= tbf.getHeight();
41179 size.height -= thd.getHeight();
41182 tbd.setSize(size.width, size.height );
41183 // this is for the account management tab -seems to work there.
41184 var thd = grid.getGridEl().select('thead',true).first();
41186 // tbd.setSize(size.width, size.height - thd.getHeight());
41195 beforeSlide : function(){
41196 this.grid.getView().scroller.clip();
41199 afterSlide : function(){
41200 this.grid.getView().scroller.unclip();
41203 destroy : function(){
41204 this.grid.destroy();
41206 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
41211 * @class Roo.bootstrap.panel.Nest
41212 * @extends Roo.bootstrap.panel.Content
41214 * Create a new Panel, that can contain a layout.Border.
41217 * @param {Roo.BorderLayout} layout The layout for this panel
41218 * @param {String/Object} config A string to set only the title or a config object
41220 Roo.bootstrap.panel.Nest = function(config)
41222 // construct with only one argument..
41223 /* FIXME - implement nicer consturctors
41224 if (layout.layout) {
41226 layout = config.layout;
41227 delete config.layout;
41229 if (layout.xtype && !layout.getEl) {
41230 // then layout needs constructing..
41231 layout = Roo.factory(layout, Roo);
41235 config.el = config.layout.getEl();
41237 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
41239 config.layout.monitorWindowResize = false; // turn off autosizing
41240 this.layout = config.layout;
41241 this.layout.getEl().addClass("roo-layout-nested-layout");
41242 this.layout.parent = this;
41249 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41251 setSize : function(width, height){
41252 if(!this.ignoreResize(width, height)){
41253 var size = this.adjustForComponents(width, height);
41254 var el = this.layout.getEl();
41255 if (size.height < 1) {
41256 el.setWidth(size.width);
41258 el.setSize(size.width, size.height);
41260 var touch = el.dom.offsetWidth;
41261 this.layout.layout();
41262 // ie requires a double layout on the first pass
41263 if(Roo.isIE && !this.initialized){
41264 this.initialized = true;
41265 this.layout.layout();
41270 // activate all subpanels if not currently active..
41272 setActiveState : function(active){
41273 this.active = active;
41274 this.setActiveClass(active);
41277 this.fireEvent("deactivate", this);
41281 this.fireEvent("activate", this);
41282 // not sure if this should happen before or after..
41283 if (!this.layout) {
41284 return; // should not happen..
41287 for (var r in this.layout.regions) {
41288 reg = this.layout.getRegion(r);
41289 if (reg.getActivePanel()) {
41290 //reg.showPanel(reg.getActivePanel()); // force it to activate..
41291 reg.setActivePanel(reg.getActivePanel());
41294 if (!reg.panels.length) {
41297 reg.showPanel(reg.getPanel(0));
41306 * Returns the nested BorderLayout for this panel
41307 * @return {Roo.BorderLayout}
41309 getLayout : function(){
41310 return this.layout;
41314 * Adds a xtype elements to the layout of the nested panel
41318 xtype : 'ContentPanel',
41325 xtype : 'NestedLayoutPanel',
41331 items : [ ... list of content panels or nested layout panels.. ]
41335 * @param {Object} cfg Xtype definition of item to add.
41337 addxtype : function(cfg) {
41338 return this.layout.addxtype(cfg);
41343 * Ext JS Library 1.1.1
41344 * Copyright(c) 2006-2007, Ext JS, LLC.
41346 * Originally Released Under LGPL - original licence link has changed is not relivant.
41349 * <script type="text/javascript">
41352 * @class Roo.TabPanel
41353 * @extends Roo.util.Observable
41354 * A lightweight tab container.
41358 // basic tabs 1, built from existing content
41359 var tabs = new Roo.TabPanel("tabs1");
41360 tabs.addTab("script", "View Script");
41361 tabs.addTab("markup", "View Markup");
41362 tabs.activate("script");
41364 // more advanced tabs, built from javascript
41365 var jtabs = new Roo.TabPanel("jtabs");
41366 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41368 // set up the UpdateManager
41369 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41370 var updater = tab2.getUpdateManager();
41371 updater.setDefaultUrl("ajax1.htm");
41372 tab2.on('activate', updater.refresh, updater, true);
41374 // Use setUrl for Ajax loading
41375 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41376 tab3.setUrl("ajax2.htm", null, true);
41379 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41382 jtabs.activate("jtabs-1");
41385 * Create a new TabPanel.
41386 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41387 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41389 Roo.bootstrap.panel.Tabs = function(config){
41391 * The container element for this TabPanel.
41392 * @type Roo.Element
41394 this.el = Roo.get(config.el);
41397 if(typeof config == "boolean"){
41398 this.tabPosition = config ? "bottom" : "top";
41400 Roo.apply(this, config);
41404 if(this.tabPosition == "bottom"){
41405 // if tabs are at the bottom = create the body first.
41406 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41407 this.el.addClass("roo-tabs-bottom");
41409 // next create the tabs holders
41411 if (this.tabPosition == "west"){
41413 var reg = this.region; // fake it..
41415 if (!reg.mgr.parent) {
41418 reg = reg.mgr.parent.region;
41420 Roo.log("got nest?");
41422 if (reg.mgr.getRegion('west')) {
41423 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41424 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41425 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41426 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41427 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41435 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41436 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41437 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41438 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41443 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41446 // finally - if tabs are at the top, then create the body last..
41447 if(this.tabPosition != "bottom"){
41448 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41449 * @type Roo.Element
41451 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41452 this.el.addClass("roo-tabs-top");
41456 this.bodyEl.setStyle("position", "relative");
41458 this.active = null;
41459 this.activateDelegate = this.activate.createDelegate(this);
41464 * Fires when the active tab changes
41465 * @param {Roo.TabPanel} this
41466 * @param {Roo.TabPanelItem} activePanel The new active tab
41470 * @event beforetabchange
41471 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41472 * @param {Roo.TabPanel} this
41473 * @param {Object} e Set cancel to true on this object to cancel the tab change
41474 * @param {Roo.TabPanelItem} tab The tab being changed to
41476 "beforetabchange" : true
41479 Roo.EventManager.onWindowResize(this.onResize, this);
41480 this.cpad = this.el.getPadding("lr");
41481 this.hiddenCount = 0;
41484 // toolbar on the tabbar support...
41485 if (this.toolbar) {
41486 alert("no toolbar support yet");
41487 this.toolbar = false;
41489 var tcfg = this.toolbar;
41490 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
41491 this.toolbar = new Roo.Toolbar(tcfg);
41492 if (Roo.isSafari) {
41493 var tbl = tcfg.container.child('table', true);
41494 tbl.setAttribute('width', '100%');
41502 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41505 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41507 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41509 tabPosition : "top",
41511 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41513 currentTabWidth : 0,
41515 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41519 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41523 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41525 preferredTabWidth : 175,
41527 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41529 resizeTabs : false,
41531 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41533 monitorResize : true,
41535 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
41537 toolbar : false, // set by caller..
41539 region : false, /// set by caller
41541 disableTooltips : true, // not used yet...
41544 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41545 * @param {String} id The id of the div to use <b>or create</b>
41546 * @param {String} text The text for the tab
41547 * @param {String} content (optional) Content to put in the TabPanelItem body
41548 * @param {Boolean} closable (optional) True to create a close icon on the tab
41549 * @return {Roo.TabPanelItem} The created TabPanelItem
41551 addTab : function(id, text, content, closable, tpl)
41553 var item = new Roo.bootstrap.panel.TabItem({
41557 closable : closable,
41560 this.addTabItem(item);
41562 item.setContent(content);
41568 * Returns the {@link Roo.TabPanelItem} with the specified id/index
41569 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41570 * @return {Roo.TabPanelItem}
41572 getTab : function(id){
41573 return this.items[id];
41577 * Hides the {@link Roo.TabPanelItem} with the specified id/index
41578 * @param {String/Number} id The id or index of the TabPanelItem to hide.
41580 hideTab : function(id){
41581 var t = this.items[id];
41584 this.hiddenCount++;
41585 this.autoSizeTabs();
41590 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41591 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41593 unhideTab : function(id){
41594 var t = this.items[id];
41596 t.setHidden(false);
41597 this.hiddenCount--;
41598 this.autoSizeTabs();
41603 * Adds an existing {@link Roo.TabPanelItem}.
41604 * @param {Roo.TabPanelItem} item The TabPanelItem to add
41606 addTabItem : function(item)
41608 this.items[item.id] = item;
41609 this.items.push(item);
41610 this.autoSizeTabs();
41611 // if(this.resizeTabs){
41612 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41613 // this.autoSizeTabs();
41615 // item.autoSize();
41620 * Removes a {@link Roo.TabPanelItem}.
41621 * @param {String/Number} id The id or index of the TabPanelItem to remove.
41623 removeTab : function(id){
41624 var items = this.items;
41625 var tab = items[id];
41626 if(!tab) { return; }
41627 var index = items.indexOf(tab);
41628 if(this.active == tab && items.length > 1){
41629 var newTab = this.getNextAvailable(index);
41634 this.stripEl.dom.removeChild(tab.pnode.dom);
41635 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41636 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41638 items.splice(index, 1);
41639 delete this.items[tab.id];
41640 tab.fireEvent("close", tab);
41641 tab.purgeListeners();
41642 this.autoSizeTabs();
41645 getNextAvailable : function(start){
41646 var items = this.items;
41648 // look for a next tab that will slide over to
41649 // replace the one being removed
41650 while(index < items.length){
41651 var item = items[++index];
41652 if(item && !item.isHidden()){
41656 // if one isn't found select the previous tab (on the left)
41659 var item = items[--index];
41660 if(item && !item.isHidden()){
41668 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41669 * @param {String/Number} id The id or index of the TabPanelItem to disable.
41671 disableTab : function(id){
41672 var tab = this.items[id];
41673 if(tab && this.active != tab){
41679 * Enables a {@link Roo.TabPanelItem} that is disabled.
41680 * @param {String/Number} id The id or index of the TabPanelItem to enable.
41682 enableTab : function(id){
41683 var tab = this.items[id];
41688 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41689 * @param {String/Number} id The id or index of the TabPanelItem to activate.
41690 * @return {Roo.TabPanelItem} The TabPanelItem.
41692 activate : function(id)
41694 //Roo.log('activite:' + id);
41696 var tab = this.items[id];
41700 if(tab == this.active || tab.disabled){
41704 this.fireEvent("beforetabchange", this, e, tab);
41705 if(e.cancel !== true && !tab.disabled){
41707 this.active.hide();
41709 this.active = this.items[id];
41710 this.active.show();
41711 this.fireEvent("tabchange", this, this.active);
41717 * Gets the active {@link Roo.TabPanelItem}.
41718 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41720 getActiveTab : function(){
41721 return this.active;
41725 * Updates the tab body element to fit the height of the container element
41726 * for overflow scrolling
41727 * @param {Number} targetHeight (optional) Override the starting height from the elements height
41729 syncHeight : function(targetHeight){
41730 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41731 var bm = this.bodyEl.getMargins();
41732 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41733 this.bodyEl.setHeight(newHeight);
41737 onResize : function(){
41738 if(this.monitorResize){
41739 this.autoSizeTabs();
41744 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41746 beginUpdate : function(){
41747 this.updating = true;
41751 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41753 endUpdate : function(){
41754 this.updating = false;
41755 this.autoSizeTabs();
41759 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41761 autoSizeTabs : function()
41763 var count = this.items.length;
41764 var vcount = count - this.hiddenCount;
41767 this.stripEl.hide();
41769 this.stripEl.show();
41772 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41777 var w = Math.max(this.el.getWidth() - this.cpad, 10);
41778 var availWidth = Math.floor(w / vcount);
41779 var b = this.stripBody;
41780 if(b.getWidth() > w){
41781 var tabs = this.items;
41782 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41783 if(availWidth < this.minTabWidth){
41784 /*if(!this.sleft){ // incomplete scrolling code
41785 this.createScrollButtons();
41788 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41791 if(this.currentTabWidth < this.preferredTabWidth){
41792 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41798 * Returns the number of tabs in this TabPanel.
41801 getCount : function(){
41802 return this.items.length;
41806 * Resizes all the tabs to the passed width
41807 * @param {Number} The new width
41809 setTabWidth : function(width){
41810 this.currentTabWidth = width;
41811 for(var i = 0, len = this.items.length; i < len; i++) {
41812 if(!this.items[i].isHidden()) {
41813 this.items[i].setWidth(width);
41819 * Destroys this TabPanel
41820 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41822 destroy : function(removeEl){
41823 Roo.EventManager.removeResizeListener(this.onResize, this);
41824 for(var i = 0, len = this.items.length; i < len; i++){
41825 this.items[i].purgeListeners();
41827 if(removeEl === true){
41828 this.el.update("");
41833 createStrip : function(container)
41835 var strip = document.createElement("nav");
41836 strip.className = Roo.bootstrap.version == 4 ?
41837 "navbar-light bg-light" :
41838 "navbar navbar-default"; //"x-tabs-wrap";
41839 container.appendChild(strip);
41843 createStripList : function(strip)
41845 // div wrapper for retard IE
41846 // returns the "tr" element.
41847 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41848 //'<div class="x-tabs-strip-wrap">'+
41849 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41850 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41851 return strip.firstChild; //.firstChild.firstChild.firstChild;
41853 createBody : function(container)
41855 var body = document.createElement("div");
41856 Roo.id(body, "tab-body");
41857 //Roo.fly(body).addClass("x-tabs-body");
41858 Roo.fly(body).addClass("tab-content");
41859 container.appendChild(body);
41862 createItemBody :function(bodyEl, id){
41863 var body = Roo.getDom(id);
41865 body = document.createElement("div");
41868 //Roo.fly(body).addClass("x-tabs-item-body");
41869 Roo.fly(body).addClass("tab-pane");
41870 bodyEl.insertBefore(body, bodyEl.firstChild);
41874 createStripElements : function(stripEl, text, closable, tpl)
41876 var td = document.createElement("li"); // was td..
41877 td.className = 'nav-item';
41879 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41882 stripEl.appendChild(td);
41884 td.className = "x-tabs-closable";
41885 if(!this.closeTpl){
41886 this.closeTpl = new Roo.Template(
41887 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41888 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41889 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41892 var el = this.closeTpl.overwrite(td, {"text": text});
41893 var close = el.getElementsByTagName("div")[0];
41894 var inner = el.getElementsByTagName("em")[0];
41895 return {"el": el, "close": close, "inner": inner};
41898 // not sure what this is..
41899 // if(!this.tabTpl){
41900 //this.tabTpl = new Roo.Template(
41901 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41902 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41904 // this.tabTpl = new Roo.Template(
41905 // '<a href="#">' +
41906 // '<span unselectable="on"' +
41907 // (this.disableTooltips ? '' : ' title="{text}"') +
41908 // ' >{text}</span></a>'
41914 var template = tpl || this.tabTpl || false;
41917 template = new Roo.Template(
41918 Roo.bootstrap.version == 4 ?
41920 '<a class="nav-link" href="#" unselectable="on"' +
41921 (this.disableTooltips ? '' : ' title="{text}"') +
41924 '<a class="nav-link" href="#">' +
41925 '<span unselectable="on"' +
41926 (this.disableTooltips ? '' : ' title="{text}"') +
41927 ' >{text}</span></a>'
41932 switch (typeof(template)) {
41936 template = new Roo.Template(template);
41942 var el = template.overwrite(td, {"text": text});
41944 var inner = el.getElementsByTagName("span")[0];
41946 return {"el": el, "inner": inner};
41954 * @class Roo.TabPanelItem
41955 * @extends Roo.util.Observable
41956 * Represents an individual item (tab plus body) in a TabPanel.
41957 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41958 * @param {String} id The id of this TabPanelItem
41959 * @param {String} text The text for the tab of this TabPanelItem
41960 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41962 Roo.bootstrap.panel.TabItem = function(config){
41964 * The {@link Roo.TabPanel} this TabPanelItem belongs to
41965 * @type Roo.TabPanel
41967 this.tabPanel = config.panel;
41969 * The id for this TabPanelItem
41972 this.id = config.id;
41974 this.disabled = false;
41976 this.text = config.text;
41978 this.loaded = false;
41979 this.closable = config.closable;
41982 * The body element for this TabPanelItem.
41983 * @type Roo.Element
41985 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41986 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41987 this.bodyEl.setStyle("display", "block");
41988 this.bodyEl.setStyle("zoom", "1");
41989 //this.hideAction();
41991 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41993 this.el = Roo.get(els.el);
41994 this.inner = Roo.get(els.inner, true);
41995 this.textEl = Roo.bootstrap.version == 4 ?
41996 this.el : Roo.get(this.el.dom.firstChild, true);
41998 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41999 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
42002 // this.el.on("mousedown", this.onTabMouseDown, this);
42003 this.el.on("click", this.onTabClick, this);
42005 if(config.closable){
42006 var c = Roo.get(els.close, true);
42007 c.dom.title = this.closeText;
42008 c.addClassOnOver("close-over");
42009 c.on("click", this.closeClick, this);
42015 * Fires when this tab becomes the active tab.
42016 * @param {Roo.TabPanel} tabPanel The parent TabPanel
42017 * @param {Roo.TabPanelItem} this
42021 * @event beforeclose
42022 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
42023 * @param {Roo.TabPanelItem} this
42024 * @param {Object} e Set cancel to true on this object to cancel the close.
42026 "beforeclose": true,
42029 * Fires when this tab is closed.
42030 * @param {Roo.TabPanelItem} this
42034 * @event deactivate
42035 * Fires when this tab is no longer the active tab.
42036 * @param {Roo.TabPanel} tabPanel The parent TabPanel
42037 * @param {Roo.TabPanelItem} this
42039 "deactivate" : true
42041 this.hidden = false;
42043 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
42046 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
42048 purgeListeners : function(){
42049 Roo.util.Observable.prototype.purgeListeners.call(this);
42050 this.el.removeAllListeners();
42053 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
42056 this.status_node.addClass("active");
42059 this.tabPanel.stripWrap.repaint();
42061 this.fireEvent("activate", this.tabPanel, this);
42065 * Returns true if this tab is the active tab.
42066 * @return {Boolean}
42068 isActive : function(){
42069 return this.tabPanel.getActiveTab() == this;
42073 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
42076 this.status_node.removeClass("active");
42078 this.fireEvent("deactivate", this.tabPanel, this);
42081 hideAction : function(){
42082 this.bodyEl.hide();
42083 this.bodyEl.setStyle("position", "absolute");
42084 this.bodyEl.setLeft("-20000px");
42085 this.bodyEl.setTop("-20000px");
42088 showAction : function(){
42089 this.bodyEl.setStyle("position", "relative");
42090 this.bodyEl.setTop("");
42091 this.bodyEl.setLeft("");
42092 this.bodyEl.show();
42096 * Set the tooltip for the tab.
42097 * @param {String} tooltip The tab's tooltip
42099 setTooltip : function(text){
42100 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
42101 this.textEl.dom.qtip = text;
42102 this.textEl.dom.removeAttribute('title');
42104 this.textEl.dom.title = text;
42108 onTabClick : function(e){
42109 e.preventDefault();
42110 this.tabPanel.activate(this.id);
42113 onTabMouseDown : function(e){
42114 e.preventDefault();
42115 this.tabPanel.activate(this.id);
42118 getWidth : function(){
42119 return this.inner.getWidth();
42122 setWidth : function(width){
42123 var iwidth = width - this.linode.getPadding("lr");
42124 this.inner.setWidth(iwidth);
42125 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
42126 this.linode.setWidth(width);
42130 * Show or hide the tab
42131 * @param {Boolean} hidden True to hide or false to show.
42133 setHidden : function(hidden){
42134 this.hidden = hidden;
42135 this.linode.setStyle("display", hidden ? "none" : "");
42139 * Returns true if this tab is "hidden"
42140 * @return {Boolean}
42142 isHidden : function(){
42143 return this.hidden;
42147 * Returns the text for this tab
42150 getText : function(){
42154 autoSize : function(){
42155 //this.el.beginMeasure();
42156 this.textEl.setWidth(1);
42158 * #2804 [new] Tabs in Roojs
42159 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
42161 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
42162 //this.el.endMeasure();
42166 * Sets the text for the tab (Note: this also sets the tooltip text)
42167 * @param {String} text The tab's text and tooltip
42169 setText : function(text){
42171 this.textEl.update(text);
42172 this.setTooltip(text);
42173 //if(!this.tabPanel.resizeTabs){
42174 // this.autoSize();
42178 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
42180 activate : function(){
42181 this.tabPanel.activate(this.id);
42185 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
42187 disable : function(){
42188 if(this.tabPanel.active != this){
42189 this.disabled = true;
42190 this.status_node.addClass("disabled");
42195 * Enables this TabPanelItem if it was previously disabled.
42197 enable : function(){
42198 this.disabled = false;
42199 this.status_node.removeClass("disabled");
42203 * Sets the content for this TabPanelItem.
42204 * @param {String} content The content
42205 * @param {Boolean} loadScripts true to look for and load scripts
42207 setContent : function(content, loadScripts){
42208 this.bodyEl.update(content, loadScripts);
42212 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
42213 * @return {Roo.UpdateManager} The UpdateManager
42215 getUpdateManager : function(){
42216 return this.bodyEl.getUpdateManager();
42220 * Set a URL to be used to load the content for this TabPanelItem.
42221 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
42222 * @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)
42223 * @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)
42224 * @return {Roo.UpdateManager} The UpdateManager
42226 setUrl : function(url, params, loadOnce){
42227 if(this.refreshDelegate){
42228 this.un('activate', this.refreshDelegate);
42230 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
42231 this.on("activate", this.refreshDelegate);
42232 return this.bodyEl.getUpdateManager();
42236 _handleRefresh : function(url, params, loadOnce){
42237 if(!loadOnce || !this.loaded){
42238 var updater = this.bodyEl.getUpdateManager();
42239 updater.update(url, params, this._setLoaded.createDelegate(this));
42244 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
42245 * Will fail silently if the setUrl method has not been called.
42246 * This does not activate the panel, just updates its content.
42248 refresh : function(){
42249 if(this.refreshDelegate){
42250 this.loaded = false;
42251 this.refreshDelegate();
42256 _setLoaded : function(){
42257 this.loaded = true;
42261 closeClick : function(e){
42264 this.fireEvent("beforeclose", this, o);
42265 if(o.cancel !== true){
42266 this.tabPanel.removeTab(this.id);
42270 * The text displayed in the tooltip for the close icon.
42273 closeText : "Close this tab"
42276 * This script refer to:
42277 * Title: International Telephone Input
42278 * Author: Jack O'Connor
42279 * Code version: v12.1.12
42280 * Availability: https://github.com/jackocnr/intl-tel-input.git
42283 Roo.bootstrap.PhoneInputData = function() {
42286 "Afghanistan (افغانستان)",
42291 "Albania (Shqipëri)",
42296 "Algeria (الجزائر)",
42321 "Antigua and Barbuda",
42331 "Armenia (Հայաստան)",
42347 "Austria (Österreich)",
42352 "Azerbaijan (Azərbaycan)",
42362 "Bahrain (البحرين)",
42367 "Bangladesh (বাংলাদেশ)",
42377 "Belarus (Беларусь)",
42382 "Belgium (België)",
42412 "Bosnia and Herzegovina (Босна и Херцеговина)",
42427 "British Indian Ocean Territory",
42432 "British Virgin Islands",
42442 "Bulgaria (България)",
42452 "Burundi (Uburundi)",
42457 "Cambodia (កម្ពុជា)",
42462 "Cameroon (Cameroun)",
42471 ["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"]
42474 "Cape Verde (Kabu Verdi)",
42479 "Caribbean Netherlands",
42490 "Central African Republic (République centrafricaine)",
42510 "Christmas Island",
42516 "Cocos (Keeling) Islands",
42527 "Comoros (جزر القمر)",
42532 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42537 "Congo (Republic) (Congo-Brazzaville)",
42557 "Croatia (Hrvatska)",
42578 "Czech Republic (Česká republika)",
42583 "Denmark (Danmark)",
42598 "Dominican Republic (República Dominicana)",
42602 ["809", "829", "849"]
42620 "Equatorial Guinea (Guinea Ecuatorial)",
42640 "Falkland Islands (Islas Malvinas)",
42645 "Faroe Islands (Føroyar)",
42666 "French Guiana (Guyane française)",
42671 "French Polynesia (Polynésie française)",
42686 "Georgia (საქართველო)",
42691 "Germany (Deutschland)",
42711 "Greenland (Kalaallit Nunaat)",
42748 "Guinea-Bissau (Guiné Bissau)",
42773 "Hungary (Magyarország)",
42778 "Iceland (Ísland)",
42798 "Iraq (العراق)",
42814 "Israel (ישראל)",
42841 "Jordan (الأردن)",
42846 "Kazakhstan (Казахстан)",
42867 "Kuwait (الكويت)",
42872 "Kyrgyzstan (Кыргызстан)",
42882 "Latvia (Latvija)",
42887 "Lebanon (لبنان)",
42902 "Libya (ليبيا)",
42912 "Lithuania (Lietuva)",
42927 "Macedonia (FYROM) (Македонија)",
42932 "Madagascar (Madagasikara)",
42962 "Marshall Islands",
42972 "Mauritania (موريتانيا)",
42977 "Mauritius (Moris)",
42998 "Moldova (Republica Moldova)",
43008 "Mongolia (Монгол)",
43013 "Montenegro (Crna Gora)",
43023 "Morocco (المغرب)",
43029 "Mozambique (Moçambique)",
43034 "Myanmar (Burma) (မြန်မာ)",
43039 "Namibia (Namibië)",
43054 "Netherlands (Nederland)",
43059 "New Caledonia (Nouvelle-Calédonie)",
43094 "North Korea (조선 민주주의 인민 공화국)",
43099 "Northern Mariana Islands",
43115 "Pakistan (پاکستان)",
43125 "Palestine (فلسطين)",
43135 "Papua New Guinea",
43177 "Réunion (La Réunion)",
43183 "Romania (România)",
43199 "Saint Barthélemy",
43210 "Saint Kitts and Nevis",
43220 "Saint Martin (Saint-Martin (partie française))",
43226 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
43231 "Saint Vincent and the Grenadines",
43246 "São Tomé and Príncipe (São Tomé e Príncipe)",
43251 "Saudi Arabia (المملكة العربية السعودية)",
43256 "Senegal (Sénégal)",
43286 "Slovakia (Slovensko)",
43291 "Slovenia (Slovenija)",
43301 "Somalia (Soomaaliya)",
43311 "South Korea (대한민국)",
43316 "South Sudan (جنوب السودان)",
43326 "Sri Lanka (ශ්රී ලංකාව)",
43331 "Sudan (السودان)",
43341 "Svalbard and Jan Mayen",
43352 "Sweden (Sverige)",
43357 "Switzerland (Schweiz)",
43362 "Syria (سوريا)",
43407 "Trinidad and Tobago",
43412 "Tunisia (تونس)",
43417 "Turkey (Türkiye)",
43427 "Turks and Caicos Islands",
43437 "U.S. Virgin Islands",
43447 "Ukraine (Україна)",
43452 "United Arab Emirates (الإمارات العربية المتحدة)",
43474 "Uzbekistan (Oʻzbekiston)",
43484 "Vatican City (Città del Vaticano)",
43495 "Vietnam (Việt Nam)",
43500 "Wallis and Futuna (Wallis-et-Futuna)",
43505 "Western Sahara (الصحراء الغربية)",
43511 "Yemen (اليمن)",
43535 * This script refer to:
43536 * Title: International Telephone Input
43537 * Author: Jack O'Connor
43538 * Code version: v12.1.12
43539 * Availability: https://github.com/jackocnr/intl-tel-input.git
43543 * @class Roo.bootstrap.PhoneInput
43544 * @extends Roo.bootstrap.TriggerField
43545 * An input with International dial-code selection
43547 * @cfg {String} defaultDialCode default '+852'
43548 * @cfg {Array} preferedCountries default []
43551 * Create a new PhoneInput.
43552 * @param {Object} config Configuration options
43555 Roo.bootstrap.PhoneInput = function(config) {
43556 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43559 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43561 listWidth: undefined,
43563 selectedClass: 'active',
43565 invalidClass : "has-warning",
43567 validClass: 'has-success',
43569 allowed: '0123456789',
43574 * @cfg {String} defaultDialCode The default dial code when initializing the input
43576 defaultDialCode: '+852',
43579 * @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
43581 preferedCountries: false,
43583 getAutoCreate : function()
43585 var data = Roo.bootstrap.PhoneInputData();
43586 var align = this.labelAlign || this.parentLabelAlign();
43589 this.allCountries = [];
43590 this.dialCodeMapping = [];
43592 for (var i = 0; i < data.length; i++) {
43594 this.allCountries[i] = {
43598 priority: c[3] || 0,
43599 areaCodes: c[4] || null
43601 this.dialCodeMapping[c[2]] = {
43604 priority: c[3] || 0,
43605 areaCodes: c[4] || null
43617 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43618 maxlength: this.max_length,
43619 cls : 'form-control tel-input',
43620 autocomplete: 'new-password'
43623 var hiddenInput = {
43626 cls: 'hidden-tel-input'
43630 hiddenInput.name = this.name;
43633 if (this.disabled) {
43634 input.disabled = true;
43637 var flag_container = {
43654 cls: this.hasFeedback ? 'has-feedback' : '',
43660 cls: 'dial-code-holder',
43667 cls: 'roo-select2-container input-group',
43674 if (this.fieldLabel.length) {
43677 tooltip: 'This field is required'
43683 cls: 'control-label',
43689 html: this.fieldLabel
43692 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43698 if(this.indicatorpos == 'right') {
43699 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43706 if(align == 'left') {
43714 if(this.labelWidth > 12){
43715 label.style = "width: " + this.labelWidth + 'px';
43717 if(this.labelWidth < 13 && this.labelmd == 0){
43718 this.labelmd = this.labelWidth;
43720 if(this.labellg > 0){
43721 label.cls += ' col-lg-' + this.labellg;
43722 input.cls += ' col-lg-' + (12 - this.labellg);
43724 if(this.labelmd > 0){
43725 label.cls += ' col-md-' + this.labelmd;
43726 container.cls += ' col-md-' + (12 - this.labelmd);
43728 if(this.labelsm > 0){
43729 label.cls += ' col-sm-' + this.labelsm;
43730 container.cls += ' col-sm-' + (12 - this.labelsm);
43732 if(this.labelxs > 0){
43733 label.cls += ' col-xs-' + this.labelxs;
43734 container.cls += ' col-xs-' + (12 - this.labelxs);
43744 var settings = this;
43746 ['xs','sm','md','lg'].map(function(size){
43747 if (settings[size]) {
43748 cfg.cls += ' col-' + size + '-' + settings[size];
43752 this.store = new Roo.data.Store({
43753 proxy : new Roo.data.MemoryProxy({}),
43754 reader : new Roo.data.JsonReader({
43765 'name' : 'dialCode',
43769 'name' : 'priority',
43773 'name' : 'areaCodes',
43780 if(!this.preferedCountries) {
43781 this.preferedCountries = [
43788 var p = this.preferedCountries.reverse();
43791 for (var i = 0; i < p.length; i++) {
43792 for (var j = 0; j < this.allCountries.length; j++) {
43793 if(this.allCountries[j].iso2 == p[i]) {
43794 var t = this.allCountries[j];
43795 this.allCountries.splice(j,1);
43796 this.allCountries.unshift(t);
43802 this.store.proxy.data = {
43804 data: this.allCountries
43810 initEvents : function()
43813 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43815 this.indicator = this.indicatorEl();
43816 this.flag = this.flagEl();
43817 this.dialCodeHolder = this.dialCodeHolderEl();
43819 this.trigger = this.el.select('div.flag-box',true).first();
43820 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43825 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43826 _this.list.setWidth(lw);
43829 this.list.on('mouseover', this.onViewOver, this);
43830 this.list.on('mousemove', this.onViewMove, this);
43831 this.inputEl().on("keyup", this.onKeyUp, this);
43832 this.inputEl().on("keypress", this.onKeyPress, this);
43834 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43836 this.view = new Roo.View(this.list, this.tpl, {
43837 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43840 this.view.on('click', this.onViewClick, this);
43841 this.setValue(this.defaultDialCode);
43844 onTriggerClick : function(e)
43846 Roo.log('trigger click');
43851 if(this.isExpanded()){
43853 this.hasFocus = false;
43855 this.store.load({});
43856 this.hasFocus = true;
43861 isExpanded : function()
43863 return this.list.isVisible();
43866 collapse : function()
43868 if(!this.isExpanded()){
43872 Roo.get(document).un('mousedown', this.collapseIf, this);
43873 Roo.get(document).un('mousewheel', this.collapseIf, this);
43874 this.fireEvent('collapse', this);
43878 expand : function()
43882 if(this.isExpanded() || !this.hasFocus){
43886 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43887 this.list.setWidth(lw);
43890 this.restrictHeight();
43892 Roo.get(document).on('mousedown', this.collapseIf, this);
43893 Roo.get(document).on('mousewheel', this.collapseIf, this);
43895 this.fireEvent('expand', this);
43898 restrictHeight : function()
43900 this.list.alignTo(this.inputEl(), this.listAlign);
43901 this.list.alignTo(this.inputEl(), this.listAlign);
43904 onViewOver : function(e, t)
43906 if(this.inKeyMode){
43909 var item = this.view.findItemFromChild(t);
43912 var index = this.view.indexOf(item);
43913 this.select(index, false);
43918 onViewClick : function(view, doFocus, el, e)
43920 var index = this.view.getSelectedIndexes()[0];
43922 var r = this.store.getAt(index);
43925 this.onSelect(r, index);
43927 if(doFocus !== false && !this.blockFocus){
43928 this.inputEl().focus();
43932 onViewMove : function(e, t)
43934 this.inKeyMode = false;
43937 select : function(index, scrollIntoView)
43939 this.selectedIndex = index;
43940 this.view.select(index);
43941 if(scrollIntoView !== false){
43942 var el = this.view.getNode(index);
43944 this.list.scrollChildIntoView(el, false);
43949 createList : function()
43951 this.list = Roo.get(document.body).createChild({
43953 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43954 style: 'display:none'
43957 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43960 collapseIf : function(e)
43962 var in_combo = e.within(this.el);
43963 var in_list = e.within(this.list);
43964 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43966 if (in_combo || in_list || is_list) {
43972 onSelect : function(record, index)
43974 if(this.fireEvent('beforeselect', this, record, index) !== false){
43976 this.setFlagClass(record.data.iso2);
43977 this.setDialCode(record.data.dialCode);
43978 this.hasFocus = false;
43980 this.fireEvent('select', this, record, index);
43984 flagEl : function()
43986 var flag = this.el.select('div.flag',true).first();
43993 dialCodeHolderEl : function()
43995 var d = this.el.select('input.dial-code-holder',true).first();
44002 setDialCode : function(v)
44004 this.dialCodeHolder.dom.value = '+'+v;
44007 setFlagClass : function(n)
44009 this.flag.dom.className = 'flag '+n;
44012 getValue : function()
44014 var v = this.inputEl().getValue();
44015 if(this.dialCodeHolder) {
44016 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
44021 setValue : function(v)
44023 var d = this.getDialCode(v);
44025 //invalid dial code
44026 if(v.length == 0 || !d || d.length == 0) {
44028 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
44029 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44035 this.setFlagClass(this.dialCodeMapping[d].iso2);
44036 this.setDialCode(d);
44037 this.inputEl().dom.value = v.replace('+'+d,'');
44038 this.hiddenEl().dom.value = this.getValue();
44043 getDialCode : function(v)
44047 if (v.length == 0) {
44048 return this.dialCodeHolder.dom.value;
44052 if (v.charAt(0) != "+") {
44055 var numericChars = "";
44056 for (var i = 1; i < v.length; i++) {
44057 var c = v.charAt(i);
44060 if (this.dialCodeMapping[numericChars]) {
44061 dialCode = v.substr(1, i);
44063 if (numericChars.length == 4) {
44073 this.setValue(this.defaultDialCode);
44077 hiddenEl : function()
44079 return this.el.select('input.hidden-tel-input',true).first();
44082 // after setting val
44083 onKeyUp : function(e){
44084 this.setValue(this.getValue());
44087 onKeyPress : function(e){
44088 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
44095 * @class Roo.bootstrap.MoneyField
44096 * @extends Roo.bootstrap.ComboBox
44097 * Bootstrap MoneyField class
44100 * Create a new MoneyField.
44101 * @param {Object} config Configuration options
44104 Roo.bootstrap.MoneyField = function(config) {
44106 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
44110 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
44113 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
44115 allowDecimals : true,
44117 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
44119 decimalSeparator : ".",
44121 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
44123 decimalPrecision : 0,
44125 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
44127 allowNegative : true,
44129 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
44133 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
44135 minValue : Number.NEGATIVE_INFINITY,
44137 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
44139 maxValue : Number.MAX_VALUE,
44141 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
44143 minText : "The minimum value for this field is {0}",
44145 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
44147 maxText : "The maximum value for this field is {0}",
44149 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
44150 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
44152 nanText : "{0} is not a valid number",
44154 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
44158 * @cfg {String} defaults currency of the MoneyField
44159 * value should be in lkey
44161 defaultCurrency : false,
44163 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
44165 thousandsDelimiter : false,
44167 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
44178 getAutoCreate : function()
44180 var align = this.labelAlign || this.parentLabelAlign();
44192 cls : 'form-control roo-money-amount-input',
44193 autocomplete: 'new-password'
44196 var hiddenInput = {
44200 cls: 'hidden-number-input'
44203 if(this.max_length) {
44204 input.maxlength = this.max_length;
44208 hiddenInput.name = this.name;
44211 if (this.disabled) {
44212 input.disabled = true;
44215 var clg = 12 - this.inputlg;
44216 var cmd = 12 - this.inputmd;
44217 var csm = 12 - this.inputsm;
44218 var cxs = 12 - this.inputxs;
44222 cls : 'row roo-money-field',
44226 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
44230 cls: 'roo-select2-container input-group',
44234 cls : 'form-control roo-money-currency-input',
44235 autocomplete: 'new-password',
44237 name : this.currencyName
44241 cls : 'input-group-addon',
44255 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44259 cls: this.hasFeedback ? 'has-feedback' : '',
44270 if (this.fieldLabel.length) {
44273 tooltip: 'This field is required'
44279 cls: 'control-label',
44285 html: this.fieldLabel
44288 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44294 if(this.indicatorpos == 'right') {
44295 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44302 if(align == 'left') {
44310 if(this.labelWidth > 12){
44311 label.style = "width: " + this.labelWidth + 'px';
44313 if(this.labelWidth < 13 && this.labelmd == 0){
44314 this.labelmd = this.labelWidth;
44316 if(this.labellg > 0){
44317 label.cls += ' col-lg-' + this.labellg;
44318 input.cls += ' col-lg-' + (12 - this.labellg);
44320 if(this.labelmd > 0){
44321 label.cls += ' col-md-' + this.labelmd;
44322 container.cls += ' col-md-' + (12 - this.labelmd);
44324 if(this.labelsm > 0){
44325 label.cls += ' col-sm-' + this.labelsm;
44326 container.cls += ' col-sm-' + (12 - this.labelsm);
44328 if(this.labelxs > 0){
44329 label.cls += ' col-xs-' + this.labelxs;
44330 container.cls += ' col-xs-' + (12 - this.labelxs);
44341 var settings = this;
44343 ['xs','sm','md','lg'].map(function(size){
44344 if (settings[size]) {
44345 cfg.cls += ' col-' + size + '-' + settings[size];
44352 initEvents : function()
44354 this.indicator = this.indicatorEl();
44356 this.initCurrencyEvent();
44358 this.initNumberEvent();
44361 initCurrencyEvent : function()
44364 throw "can not find store for combo";
44367 this.store = Roo.factory(this.store, Roo.data);
44368 this.store.parent = this;
44372 this.triggerEl = this.el.select('.input-group-addon', true).first();
44374 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44379 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44380 _this.list.setWidth(lw);
44383 this.list.on('mouseover', this.onViewOver, this);
44384 this.list.on('mousemove', this.onViewMove, this);
44385 this.list.on('scroll', this.onViewScroll, this);
44388 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44391 this.view = new Roo.View(this.list, this.tpl, {
44392 singleSelect:true, store: this.store, selectedClass: this.selectedClass
44395 this.view.on('click', this.onViewClick, this);
44397 this.store.on('beforeload', this.onBeforeLoad, this);
44398 this.store.on('load', this.onLoad, this);
44399 this.store.on('loadexception', this.onLoadException, this);
44401 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44402 "up" : function(e){
44403 this.inKeyMode = true;
44407 "down" : function(e){
44408 if(!this.isExpanded()){
44409 this.onTriggerClick();
44411 this.inKeyMode = true;
44416 "enter" : function(e){
44419 if(this.fireEvent("specialkey", this, e)){
44420 this.onViewClick(false);
44426 "esc" : function(e){
44430 "tab" : function(e){
44433 if(this.fireEvent("specialkey", this, e)){
44434 this.onViewClick(false);
44442 doRelay : function(foo, bar, hname){
44443 if(hname == 'down' || this.scope.isExpanded()){
44444 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44452 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44456 initNumberEvent : function(e)
44458 this.inputEl().on("keydown" , this.fireKey, this);
44459 this.inputEl().on("focus", this.onFocus, this);
44460 this.inputEl().on("blur", this.onBlur, this);
44462 this.inputEl().relayEvent('keyup', this);
44464 if(this.indicator){
44465 this.indicator.addClass('invisible');
44468 this.originalValue = this.getValue();
44470 if(this.validationEvent == 'keyup'){
44471 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44472 this.inputEl().on('keyup', this.filterValidation, this);
44474 else if(this.validationEvent !== false){
44475 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44478 if(this.selectOnFocus){
44479 this.on("focus", this.preFocus, this);
44482 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44483 this.inputEl().on("keypress", this.filterKeys, this);
44485 this.inputEl().relayEvent('keypress', this);
44488 var allowed = "0123456789";
44490 if(this.allowDecimals){
44491 allowed += this.decimalSeparator;
44494 if(this.allowNegative){
44498 if(this.thousandsDelimiter) {
44502 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44504 var keyPress = function(e){
44506 var k = e.getKey();
44508 var c = e.getCharCode();
44511 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44512 allowed.indexOf(String.fromCharCode(c)) === -1
44518 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44522 if(allowed.indexOf(String.fromCharCode(c)) === -1){
44527 this.inputEl().on("keypress", keyPress, this);
44531 onTriggerClick : function(e)
44538 this.loadNext = false;
44540 if(this.isExpanded()){
44545 this.hasFocus = true;
44547 if(this.triggerAction == 'all') {
44548 this.doQuery(this.allQuery, true);
44552 this.doQuery(this.getRawValue());
44555 getCurrency : function()
44557 var v = this.currencyEl().getValue();
44562 restrictHeight : function()
44564 this.list.alignTo(this.currencyEl(), this.listAlign);
44565 this.list.alignTo(this.currencyEl(), this.listAlign);
44568 onViewClick : function(view, doFocus, el, e)
44570 var index = this.view.getSelectedIndexes()[0];
44572 var r = this.store.getAt(index);
44575 this.onSelect(r, index);
44579 onSelect : function(record, index){
44581 if(this.fireEvent('beforeselect', this, record, index) !== false){
44583 this.setFromCurrencyData(index > -1 ? record.data : false);
44587 this.fireEvent('select', this, record, index);
44591 setFromCurrencyData : function(o)
44595 this.lastCurrency = o;
44597 if (this.currencyField) {
44598 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44600 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
44603 this.lastSelectionText = currency;
44605 //setting default currency
44606 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44607 this.setCurrency(this.defaultCurrency);
44611 this.setCurrency(currency);
44614 setFromData : function(o)
44618 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44620 this.setFromCurrencyData(c);
44625 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44627 Roo.log('no value set for '+ (this.name ? this.name : this.id));
44630 this.setValue(value);
44634 setCurrency : function(v)
44636 this.currencyValue = v;
44639 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44644 setValue : function(v)
44646 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44652 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44654 this.inputEl().dom.value = (v == '') ? '' :
44655 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44657 if(!this.allowZero && v === '0') {
44658 this.hiddenEl().dom.value = '';
44659 this.inputEl().dom.value = '';
44666 getRawValue : function()
44668 var v = this.inputEl().getValue();
44673 getValue : function()
44675 return this.fixPrecision(this.parseValue(this.getRawValue()));
44678 parseValue : function(value)
44680 if(this.thousandsDelimiter) {
44682 r = new RegExp(",", "g");
44683 value = value.replace(r, "");
44686 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44687 return isNaN(value) ? '' : value;
44691 fixPrecision : function(value)
44693 if(this.thousandsDelimiter) {
44695 r = new RegExp(",", "g");
44696 value = value.replace(r, "");
44699 var nan = isNaN(value);
44701 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44702 return nan ? '' : value;
44704 return parseFloat(value).toFixed(this.decimalPrecision);
44707 decimalPrecisionFcn : function(v)
44709 return Math.floor(v);
44712 validateValue : function(value)
44714 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44718 var num = this.parseValue(value);
44721 this.markInvalid(String.format(this.nanText, value));
44725 if(num < this.minValue){
44726 this.markInvalid(String.format(this.minText, this.minValue));
44730 if(num > this.maxValue){
44731 this.markInvalid(String.format(this.maxText, this.maxValue));
44738 validate : function()
44740 if(this.disabled || this.allowBlank){
44745 var currency = this.getCurrency();
44747 if(this.validateValue(this.getRawValue()) && currency.length){
44752 this.markInvalid();
44756 getName: function()
44761 beforeBlur : function()
44767 var v = this.parseValue(this.getRawValue());
44774 onBlur : function()
44778 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44779 //this.el.removeClass(this.focusClass);
44782 this.hasFocus = false;
44784 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44788 var v = this.getValue();
44790 if(String(v) !== String(this.startValue)){
44791 this.fireEvent('change', this, v, this.startValue);
44794 this.fireEvent("blur", this);
44797 inputEl : function()
44799 return this.el.select('.roo-money-amount-input', true).first();
44802 currencyEl : function()
44804 return this.el.select('.roo-money-currency-input', true).first();
44807 hiddenEl : function()
44809 return this.el.select('input.hidden-number-input',true).first();
44813 * @class Roo.bootstrap.BezierSignature
44814 * @extends Roo.bootstrap.Component
44815 * Bootstrap BezierSignature class
44816 * This script refer to:
44817 * Title: Signature Pad
44819 * Availability: https://github.com/szimek/signature_pad
44822 * Create a new BezierSignature
44823 * @param {Object} config The config object
44826 Roo.bootstrap.BezierSignature = function(config){
44827 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44833 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44840 mouse_btn_down: true,
44843 * @cfg {int} canvas height
44845 canvas_height: '200px',
44848 * @cfg {float|function} Radius of a single dot.
44853 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44858 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44863 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44868 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44873 * @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.
44875 bg_color: 'rgba(0, 0, 0, 0)',
44878 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44880 dot_color: 'black',
44883 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44885 velocity_filter_weight: 0.7,
44888 * @cfg {function} Callback when stroke begin.
44893 * @cfg {function} Callback when stroke end.
44897 getAutoCreate : function()
44899 var cls = 'roo-signature column';
44902 cls += ' ' + this.cls;
44912 for(var i = 0; i < col_sizes.length; i++) {
44913 if(this[col_sizes[i]]) {
44914 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44924 cls: 'roo-signature-body',
44928 cls: 'roo-signature-body-canvas',
44929 height: this.canvas_height,
44930 width: this.canvas_width
44937 style: 'display: none'
44945 initEvents: function()
44947 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44949 var canvas = this.canvasEl();
44951 // mouse && touch event swapping...
44952 canvas.dom.style.touchAction = 'none';
44953 canvas.dom.style.msTouchAction = 'none';
44955 this.mouse_btn_down = false;
44956 canvas.on('mousedown', this._handleMouseDown, this);
44957 canvas.on('mousemove', this._handleMouseMove, this);
44958 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44960 if (window.PointerEvent) {
44961 canvas.on('pointerdown', this._handleMouseDown, this);
44962 canvas.on('pointermove', this._handleMouseMove, this);
44963 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44966 if ('ontouchstart' in window) {
44967 canvas.on('touchstart', this._handleTouchStart, this);
44968 canvas.on('touchmove', this._handleTouchMove, this);
44969 canvas.on('touchend', this._handleTouchEnd, this);
44972 Roo.EventManager.onWindowResize(this.resize, this, true);
44974 // file input event
44975 this.fileEl().on('change', this.uploadImage, this);
44982 resize: function(){
44984 var canvas = this.canvasEl().dom;
44985 var ctx = this.canvasElCtx();
44986 var img_data = false;
44988 if(canvas.width > 0) {
44989 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44991 // setting canvas width will clean img data
44994 var style = window.getComputedStyle ?
44995 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44997 var padding_left = parseInt(style.paddingLeft) || 0;
44998 var padding_right = parseInt(style.paddingRight) || 0;
45000 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
45003 ctx.putImageData(img_data, 0, 0);
45007 _handleMouseDown: function(e)
45009 if (e.browserEvent.which === 1) {
45010 this.mouse_btn_down = true;
45011 this.strokeBegin(e);
45015 _handleMouseMove: function (e)
45017 if (this.mouse_btn_down) {
45018 this.strokeMoveUpdate(e);
45022 _handleMouseUp: function (e)
45024 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
45025 this.mouse_btn_down = false;
45030 _handleTouchStart: function (e) {
45032 e.preventDefault();
45033 if (e.browserEvent.targetTouches.length === 1) {
45034 // var touch = e.browserEvent.changedTouches[0];
45035 // this.strokeBegin(touch);
45037 this.strokeBegin(e); // assume e catching the correct xy...
45041 _handleTouchMove: function (e) {
45042 e.preventDefault();
45043 // var touch = event.targetTouches[0];
45044 // _this._strokeMoveUpdate(touch);
45045 this.strokeMoveUpdate(e);
45048 _handleTouchEnd: function (e) {
45049 var wasCanvasTouched = e.target === this.canvasEl().dom;
45050 if (wasCanvasTouched) {
45051 e.preventDefault();
45052 // var touch = event.changedTouches[0];
45053 // _this._strokeEnd(touch);
45058 reset: function () {
45059 this._lastPoints = [];
45060 this._lastVelocity = 0;
45061 this._lastWidth = (this.min_width + this.max_width) / 2;
45062 this.canvasElCtx().fillStyle = this.dot_color;
45065 strokeMoveUpdate: function(e)
45067 this.strokeUpdate(e);
45069 if (this.throttle) {
45070 this.throttleStroke(this.strokeUpdate, this.throttle);
45073 this.strokeUpdate(e);
45077 strokeBegin: function(e)
45079 var newPointGroup = {
45080 color: this.dot_color,
45084 if (typeof this.onBegin === 'function') {
45088 this.curve_data.push(newPointGroup);
45090 this.strokeUpdate(e);
45093 strokeUpdate: function(e)
45095 var rect = this.canvasEl().dom.getBoundingClientRect();
45096 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
45097 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
45098 var lastPoints = lastPointGroup.points;
45099 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
45100 var isLastPointTooClose = lastPoint
45101 ? point.distanceTo(lastPoint) <= this.min_distance
45103 var color = lastPointGroup.color;
45104 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
45105 var curve = this.addPoint(point);
45107 this.drawDot({color: color, point: point});
45110 this.drawCurve({color: color, curve: curve});
45120 strokeEnd: function(e)
45122 this.strokeUpdate(e);
45123 if (typeof this.onEnd === 'function') {
45128 addPoint: function (point) {
45129 var _lastPoints = this._lastPoints;
45130 _lastPoints.push(point);
45131 if (_lastPoints.length > 2) {
45132 if (_lastPoints.length === 3) {
45133 _lastPoints.unshift(_lastPoints[0]);
45135 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
45136 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
45137 _lastPoints.shift();
45143 calculateCurveWidths: function (startPoint, endPoint) {
45144 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
45145 (1 - this.velocity_filter_weight) * this._lastVelocity;
45147 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
45150 start: this._lastWidth
45153 this._lastVelocity = velocity;
45154 this._lastWidth = newWidth;
45158 drawDot: function (_a) {
45159 var color = _a.color, point = _a.point;
45160 var ctx = this.canvasElCtx();
45161 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
45163 this.drawCurveSegment(point.x, point.y, width);
45165 ctx.fillStyle = color;
45169 drawCurve: function (_a) {
45170 var color = _a.color, curve = _a.curve;
45171 var ctx = this.canvasElCtx();
45172 var widthDelta = curve.endWidth - curve.startWidth;
45173 var drawSteps = Math.floor(curve.length()) * 2;
45175 ctx.fillStyle = color;
45176 for (var i = 0; i < drawSteps; i += 1) {
45177 var t = i / drawSteps;
45183 var x = uuu * curve.startPoint.x;
45184 x += 3 * uu * t * curve.control1.x;
45185 x += 3 * u * tt * curve.control2.x;
45186 x += ttt * curve.endPoint.x;
45187 var y = uuu * curve.startPoint.y;
45188 y += 3 * uu * t * curve.control1.y;
45189 y += 3 * u * tt * curve.control2.y;
45190 y += ttt * curve.endPoint.y;
45191 var width = curve.startWidth + ttt * widthDelta;
45192 this.drawCurveSegment(x, y, width);
45198 drawCurveSegment: function (x, y, width) {
45199 var ctx = this.canvasElCtx();
45201 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
45202 this.is_empty = false;
45207 var ctx = this.canvasElCtx();
45208 var canvas = this.canvasEl().dom;
45209 ctx.fillStyle = this.bg_color;
45210 ctx.clearRect(0, 0, canvas.width, canvas.height);
45211 ctx.fillRect(0, 0, canvas.width, canvas.height);
45212 this.curve_data = [];
45214 this.is_empty = true;
45219 return this.el.select('input',true).first();
45222 canvasEl: function()
45224 return this.el.select('canvas',true).first();
45227 canvasElCtx: function()
45229 return this.el.select('canvas',true).first().dom.getContext('2d');
45232 getImage: function(type)
45234 if(this.is_empty) {
45239 return this.canvasEl().dom.toDataURL('image/'+type, 1);
45242 drawFromImage: function(img_src)
45244 var img = new Image();
45246 img.onload = function(){
45247 this.canvasElCtx().drawImage(img, 0, 0);
45252 this.is_empty = false;
45255 selectImage: function()
45257 this.fileEl().dom.click();
45260 uploadImage: function(e)
45262 var reader = new FileReader();
45264 reader.onload = function(e){
45265 var img = new Image();
45266 img.onload = function(){
45268 this.canvasElCtx().drawImage(img, 0, 0);
45270 img.src = e.target.result;
45273 reader.readAsDataURL(e.target.files[0]);
45276 // Bezier Point Constructor
45277 Point: (function () {
45278 function Point(x, y, time) {
45281 this.time = time || Date.now();
45283 Point.prototype.distanceTo = function (start) {
45284 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45286 Point.prototype.equals = function (other) {
45287 return this.x === other.x && this.y === other.y && this.time === other.time;
45289 Point.prototype.velocityFrom = function (start) {
45290 return this.time !== start.time
45291 ? this.distanceTo(start) / (this.time - start.time)
45298 // Bezier Constructor
45299 Bezier: (function () {
45300 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45301 this.startPoint = startPoint;
45302 this.control2 = control2;
45303 this.control1 = control1;
45304 this.endPoint = endPoint;
45305 this.startWidth = startWidth;
45306 this.endWidth = endWidth;
45308 Bezier.fromPoints = function (points, widths, scope) {
45309 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45310 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45311 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45313 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45314 var dx1 = s1.x - s2.x;
45315 var dy1 = s1.y - s2.y;
45316 var dx2 = s2.x - s3.x;
45317 var dy2 = s2.y - s3.y;
45318 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45319 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45320 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45321 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45322 var dxm = m1.x - m2.x;
45323 var dym = m1.y - m2.y;
45324 var k = l2 / (l1 + l2);
45325 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45326 var tx = s2.x - cm.x;
45327 var ty = s2.y - cm.y;
45329 c1: new scope.Point(m1.x + tx, m1.y + ty),
45330 c2: new scope.Point(m2.x + tx, m2.y + ty)
45333 Bezier.prototype.length = function () {
45338 for (var i = 0; i <= steps; i += 1) {
45340 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45341 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45343 var xdiff = cx - px;
45344 var ydiff = cy - py;
45345 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45352 Bezier.prototype.point = function (t, start, c1, c2, end) {
45353 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45354 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45355 + (3.0 * c2 * (1.0 - t) * t * t)
45356 + (end * t * t * t);
45361 throttleStroke: function(fn, wait) {
45362 if (wait === void 0) { wait = 250; }
45364 var timeout = null;
45368 var later = function () {
45369 previous = Date.now();
45371 result = fn.apply(storedContext, storedArgs);
45373 storedContext = null;
45377 return function wrapper() {
45379 for (var _i = 0; _i < arguments.length; _i++) {
45380 args[_i] = arguments[_i];
45382 var now = Date.now();
45383 var remaining = wait - (now - previous);
45384 storedContext = this;
45386 if (remaining <= 0 || remaining > wait) {
45388 clearTimeout(timeout);
45392 result = fn.apply(storedContext, storedArgs);
45394 storedContext = null;
45398 else if (!timeout) {
45399 timeout = window.setTimeout(later, remaining);