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);
9078 var unitWidth = Math.floor(this.bodyEl.dom.clientWidth / total);
9079 var rem = this.bodyEl.dom.clientWidth - (unitWidth * total);
9082 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9086 hidden = 'display:none;';
9088 // we can honour xs/sm/md/xl ?
9089 var w = cm.getColumnWidth(i, sz) * unitWidth;
9091 hidden = 'display:none;';
9093 // width should return a small number...
9095 w+=rem; // add the remaining with..
9098 var width = "width:" + w+ "px;";
9101 '#' , this.id , ' .x-col-' , i, " {\n", cm.config[i].css, width, hidden, "\n}\n",
9102 '#' , this.id , ' .x-hcol-' , i, " {\n", width, hidden,"}\n"
9105 Roo.log(styles.join(''));
9106 this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9112 onContextMenu : function(e, t)
9114 this.processEvent("contextmenu", e);
9117 processEvent : function(name, e)
9119 if (name != 'touchstart' ) {
9120 this.fireEvent(name, e);
9123 var t = e.getTarget();
9125 var cell = Roo.get(t);
9131 if(cell.findParent('tfoot', false, true)){
9135 if(cell.findParent('thead', false, true)){
9137 if(e.getTarget().nodeName.toLowerCase() != 'th'){
9138 cell = Roo.get(t).findParent('th', false, true);
9140 Roo.log("failed to find th in thead?");
9141 Roo.log(e.getTarget());
9146 var cellIndex = cell.dom.cellIndex;
9148 var ename = name == 'touchstart' ? 'click' : name;
9149 this.fireEvent("header" + ename, this, cellIndex, e);
9154 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9155 cell = Roo.get(t).findParent('td', false, true);
9157 Roo.log("failed to find th in tbody?");
9158 Roo.log(e.getTarget());
9163 var row = cell.findParent('tr', false, true);
9164 var cellIndex = cell.dom.cellIndex;
9165 var rowIndex = row.dom.rowIndex - 1;
9169 this.fireEvent("row" + name, this, rowIndex, e);
9173 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9179 onMouseover : function(e, el)
9181 var cell = Roo.get(el);
9187 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9188 cell = cell.findParent('td', false, true);
9191 var row = cell.findParent('tr', false, true);
9192 var cellIndex = cell.dom.cellIndex;
9193 var rowIndex = row.dom.rowIndex - 1; // start from 0
9195 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9199 onMouseout : function(e, el)
9201 var cell = Roo.get(el);
9207 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9208 cell = cell.findParent('td', false, true);
9211 var row = cell.findParent('tr', false, true);
9212 var cellIndex = cell.dom.cellIndex;
9213 var rowIndex = row.dom.rowIndex - 1; // start from 0
9215 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9219 onClick : function(e, el)
9221 var cell = Roo.get(el);
9223 if(!cell || (!this.cellSelection && !this.rowSelection)){
9227 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9228 cell = cell.findParent('td', false, true);
9231 if(!cell || typeof(cell) == 'undefined'){
9235 var row = cell.findParent('tr', false, true);
9237 if(!row || typeof(row) == 'undefined'){
9241 var cellIndex = cell.dom.cellIndex;
9242 var rowIndex = this.getRowIndex(row);
9244 // why??? - should these not be based on SelectionModel?
9245 //if(this.cellSelection){
9246 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9249 //if(this.rowSelection){
9250 this.fireEvent('rowclick', this, row, rowIndex, e);
9255 onDblClick : function(e,el)
9257 var cell = Roo.get(el);
9259 if(!cell || (!this.cellSelection && !this.rowSelection)){
9263 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9264 cell = cell.findParent('td', false, true);
9267 if(!cell || typeof(cell) == 'undefined'){
9271 var row = cell.findParent('tr', false, true);
9273 if(!row || typeof(row) == 'undefined'){
9277 var cellIndex = cell.dom.cellIndex;
9278 var rowIndex = this.getRowIndex(row);
9280 if(this.cellSelection){
9281 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9284 if(this.rowSelection){
9285 this.fireEvent('rowdblclick', this, row, rowIndex, e);
9288 findRowIndex : function(el)
9290 var cell = Roo.get(el);
9294 var row = cell.findParent('tr', false, true);
9296 if(!row || typeof(row) == 'undefined'){
9299 return this.getRowIndex(row);
9301 sort : function(e,el)
9303 var col = Roo.get(el);
9305 if(!col.hasClass('sortable')){
9309 var sort = col.attr('sort');
9312 if(col.select('i', true).first().hasClass('fa-arrow-up')){
9316 this.store.sortInfo = {field : sort, direction : dir};
9319 Roo.log("calling footer first");
9320 this.footer.onClick('first');
9323 this.store.load({ params : { start : 0 } });
9327 renderHeader : function()
9335 this.totalWidth = 0;
9337 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9339 var config = cm.config[i];
9343 cls : 'x-hcol-' + i,
9346 html: cm.getColumnHeader(i)
9349 var tooltip = cm.getColumnTooltip(i);
9351 c.tooltip = tooltip;
9357 if(typeof(config.sortable) != 'undefined' && config.sortable){
9358 c.cls += ' sortable';
9359 c.html = '<i class="fa"></i>' + c.html;
9362 // could use BS4 hidden-..-down
9364 if(typeof(config.lgHeader) != 'undefined'){
9365 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9368 if(typeof(config.mdHeader) != 'undefined'){
9369 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9372 if(typeof(config.smHeader) != 'undefined'){
9373 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9376 if(typeof(config.xsHeader) != 'undefined'){
9377 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9384 if(typeof(config.tooltip) != 'undefined'){
9385 c.tooltip = config.tooltip;
9388 if(typeof(config.colspan) != 'undefined'){
9389 c.colspan = config.colspan;
9392 // hidden is handled by CSS now
9394 if(typeof(config.dataIndex) != 'undefined'){
9395 c.sort = config.dataIndex;
9400 if(typeof(config.align) != 'undefined' && config.align.length){
9401 c.style += ' text-align:' + config.align + ';';
9404 if(typeof(config.width) != 'undefined'){
9405 c.style += ' width:' + config.width + 'px;';
9406 this.totalWidth += config.width;
9408 this.totalWidth += 100; // assume minimum of 100 per column?
9411 if(typeof(config.cls) != 'undefined'){
9412 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9414 // this is the bit that doesnt reall work at all...
9418 ['xs','sm','md','lg'].map(function(size){
9420 if(typeof(config[size]) == 'undefined'){
9424 if (!config[size]) { // 0 = hidden
9425 // BS 4 '0' is treated as hide that column and below.
9426 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9430 c.cls += ' col-' + size + '-' + config[size] + (
9431 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9439 c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9450 renderBody : function()
9460 colspan : this.cm.getColumnCount()
9470 renderFooter : function()
9480 colspan : this.cm.getColumnCount()
9494 // Roo.log('ds onload');
9499 var ds = this.store;
9501 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9502 e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9503 if (_this.store.sortInfo) {
9505 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9506 e.select('i', true).addClass(['fa-arrow-up']);
9509 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9510 e.select('i', true).addClass(['fa-arrow-down']);
9515 var tbody = this.bodyEl;
9517 if(ds.getCount() > 0){
9518 ds.data.each(function(d,rowIndex){
9519 var row = this.renderRow(cm, ds, rowIndex);
9521 tbody.createChild(row);
9525 if(row.cellObjects.length){
9526 Roo.each(row.cellObjects, function(r){
9527 _this.renderCellObject(r);
9534 var tfoot = this.el.select('tfoot', true).first();
9536 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
9538 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
9540 var total = this.ds.getTotalCount();
9542 if(this.footer.pageSize < total){
9543 this.mainFoot.show();
9547 Roo.each(this.el.select('tbody td', true).elements, function(e){
9548 e.on('mouseover', _this.onMouseover, _this);
9551 Roo.each(this.el.select('tbody td', true).elements, function(e){
9552 e.on('mouseout', _this.onMouseout, _this);
9554 this.fireEvent('rowsrendered', this);
9560 onUpdate : function(ds,record)
9562 this.refreshRow(record);
9566 onRemove : function(ds, record, index, isUpdate){
9567 if(isUpdate !== true){
9568 this.fireEvent("beforerowremoved", this, index, record);
9570 var bt = this.bodyEl.dom;
9572 var rows = this.el.select('tbody > tr', true).elements;
9574 if(typeof(rows[index]) != 'undefined'){
9575 bt.removeChild(rows[index].dom);
9578 // if(bt.rows[index]){
9579 // bt.removeChild(bt.rows[index]);
9582 if(isUpdate !== true){
9583 //this.stripeRows(index);
9584 //this.syncRowHeights(index, index);
9586 this.fireEvent("rowremoved", this, index, record);
9590 onAdd : function(ds, records, rowIndex)
9592 //Roo.log('on Add called');
9593 // - note this does not handle multiple adding very well..
9594 var bt = this.bodyEl.dom;
9595 for (var i =0 ; i < records.length;i++) {
9596 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
9597 //Roo.log(records[i]);
9598 //Roo.log(this.store.getAt(rowIndex+i));
9599 this.insertRow(this.store, rowIndex + i, false);
9606 refreshRow : function(record){
9607 var ds = this.store, index;
9608 if(typeof record == 'number'){
9610 record = ds.getAt(index);
9612 index = ds.indexOf(record);
9614 return; // should not happen - but seems to
9617 this.insertRow(ds, index, true);
9619 this.onRemove(ds, record, index+1, true);
9621 //this.syncRowHeights(index, index);
9623 this.fireEvent("rowupdated", this, index, record);
9626 onRowSelect : function(rowIndex){
9627 var row = this.getRowDom(rowIndex);
9628 row.addClass(['bg-info','info']);
9631 onRowDeselect : function(rowIndex){
9632 var row = this.getRowDom(rowIndex);
9633 row.removeClass(['bg-info','info']);
9636 * Focuses the specified row.
9637 * @param {Number} row The row index
9639 focusRow : function(row)
9641 //Roo.log('GridView.focusRow');
9642 var x = this.bodyEl.dom.scrollLeft;
9643 this.focusCell(row, 0, false);
9644 this.bodyEl.dom.scrollLeft = x;
9648 * Focuses the specified cell.
9649 * @param {Number} row The row index
9650 * @param {Number} col The column index
9651 * @param {Boolean} hscroll false to disable horizontal scrolling
9653 focusCell : function(row, col, hscroll)
9655 //Roo.log('GridView.focusCell');
9656 var el = this.ensureVisible(row, col, hscroll);
9657 // not sure what focusEL achives = it's a <a> pos relative
9658 //this.focusEl.alignTo(el, "tl-tl");
9660 // this.focusEl.focus();
9662 // this.focusEl.focus.defer(1, this.focusEl);
9667 * Scrolls the specified cell into view
9668 * @param {Number} row The row index
9669 * @param {Number} col The column index
9670 * @param {Boolean} hscroll false to disable horizontal scrolling
9672 ensureVisible : function(row, col, hscroll)
9674 //Roo.log('GridView.ensureVisible,' + row + ',' + col);
9675 //return null; //disable for testing.
9676 if(typeof row != "number"){
9679 if(row < 0 && row >= this.ds.getCount()){
9682 col = (col !== undefined ? col : 0);
9684 while(cm.isHidden(col)){
9688 var el = this.getCellDom(row, col);
9692 var c = this.bodyEl.dom;
9694 var ctop = parseInt(el.offsetTop, 10);
9695 var cleft = parseInt(el.offsetLeft, 10);
9696 var cbot = ctop + el.offsetHeight;
9697 var cright = cleft + el.offsetWidth;
9699 //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
9700 var ch = 0; //?? header is not withing the area?
9701 var stop = parseInt(c.scrollTop, 10);
9702 var sleft = parseInt(c.scrollLeft, 10);
9703 var sbot = stop + ch;
9704 var sright = sleft + c.clientWidth;
9706 Roo.log('GridView.ensureVisible:' +
9708 ' c.clientHeight:' + c.clientHeight +
9709 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
9718 //Roo.log("set scrolltop to ctop DISABLE?");
9719 }else if(cbot > sbot){
9720 //Roo.log("set scrolltop to cbot-ch");
9721 c.scrollTop = cbot-ch;
9724 if(hscroll !== false){
9726 c.scrollLeft = cleft;
9727 }else if(cright > sright){
9728 c.scrollLeft = cright-c.clientWidth;
9736 insertRow : function(dm, rowIndex, isUpdate){
9739 this.fireEvent("beforerowsinserted", this, rowIndex);
9741 //var s = this.getScrollState();
9742 var row = this.renderRow(this.cm, this.store, rowIndex);
9743 // insert before rowIndex..
9744 var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
9748 if(row.cellObjects.length){
9749 Roo.each(row.cellObjects, function(r){
9750 _this.renderCellObject(r);
9755 this.fireEvent("rowsinserted", this, rowIndex);
9756 //this.syncRowHeights(firstRow, lastRow);
9757 //this.stripeRows(firstRow);
9764 getRowDom : function(rowIndex)
9766 var rows = this.el.select('tbody > tr', true).elements;
9768 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
9771 getCellDom : function(rowIndex, colIndex)
9773 var row = this.getRowDom(rowIndex);
9774 if (row === false) {
9777 var cols = row.select('td', true).elements;
9778 return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
9782 // returns the object tree for a tr..
9785 renderRow : function(cm, ds, rowIndex)
9787 var d = ds.getAt(rowIndex);
9791 cls : 'x-row-' + rowIndex,
9795 var cellObjects = [];
9797 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9798 var config = cm.config[i];
9800 var renderer = cm.getRenderer(i);
9804 if(typeof(renderer) !== 'undefined'){
9805 value = renderer(d.data[cm.getDataIndex(i)], false, d);
9807 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
9808 // and are rendered into the cells after the row is rendered - using the id for the element.
9810 if(typeof(value) === 'object'){
9820 rowIndex : rowIndex,
9825 this.fireEvent('rowclass', this, rowcfg);
9829 // this might end up displaying HTML?
9830 // this is too messy... - better to only do it on columsn you know are going to be too long
9831 //tooltip : (typeof(value) === 'object') ? '' : value,
9832 cls : rowcfg.rowClass + ' x-col-' + i,
9834 html: (typeof(value) === 'object') ? '' : value
9841 if(typeof(config.colspan) != 'undefined'){
9842 td.colspan = config.colspan;
9847 if(typeof(config.align) != 'undefined' && config.align.length){
9848 td.style += ' text-align:' + config.align + ';';
9850 if(typeof(config.valign) != 'undefined' && config.valign.length){
9851 td.style += ' vertical-align:' + config.valign + ';';
9854 if(typeof(config.width) != 'undefined'){
9855 td.style += ' width:' + config.width + 'px;';
9858 if(typeof(config.cursor) != 'undefined'){
9859 td.style += ' cursor:' + config.cursor + ';';
9862 if(typeof(config.cls) != 'undefined'){
9863 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
9866 ['xs','sm','md','lg'].map(function(size){
9868 if(typeof(config[size]) == 'undefined'){
9874 if (!config[size]) { // 0 = hidden
9875 // BS 4 '0' is treated as hide that column and below.
9876 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
9880 td.cls += ' col-' + size + '-' + config[size] + (
9881 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9891 row.cellObjects = cellObjects;
9899 onBeforeLoad : function()
9908 this.el.select('tbody', true).first().dom.innerHTML = '';
9911 * Show or hide a row.
9912 * @param {Number} rowIndex to show or hide
9913 * @param {Boolean} state hide
9915 setRowVisibility : function(rowIndex, state)
9917 var bt = this.bodyEl.dom;
9919 var rows = this.el.select('tbody > tr', true).elements;
9921 if(typeof(rows[rowIndex]) == 'undefined'){
9924 rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
9929 getSelectionModel : function(){
9931 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
9933 return this.selModel;
9936 * Render the Roo.bootstrap object from renderder
9938 renderCellObject : function(r)
9942 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
9944 var t = r.cfg.render(r.container);
9947 Roo.each(r.cfg.cn, function(c){
9949 container: t.getChildContainer(),
9952 _this.renderCellObject(child);
9957 * get the Row Index from a dom element.
9958 * @param {Roo.Element} row The row to look for
9959 * @returns {Number} the row
9961 getRowIndex : function(row)
9965 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
9976 * get the header TH element for columnIndex
9977 * @param {Number} columnIndex
9978 * @returns {Roo.Element}
9980 getHeaderIndex: function(colIndex)
9982 var cols = this.headEl.select('th', true).elements;
9983 return cols[colIndex];
9986 * get the Column Index from a dom element. (using regex on x-hcol-{colid})
9987 * @param {domElement} cell to look for
9988 * @returns {Number} the column
9990 getCellIndex : function(cell)
9992 var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
9994 return parseInt(id[1], 10);
9999 * Returns the grid's underlying element = used by panel.Grid
10000 * @return {Element} The element
10002 getGridEl : function(){
10006 * Forces a resize - used by panel.Grid
10007 * @return {Element} The element
10009 autoSize : function()
10011 //var ctr = Roo.get(this.container.dom.parentElement);
10012 var ctr = Roo.get(this.el.dom);
10014 var thd = this.getGridEl().select('thead',true).first();
10015 var tbd = this.getGridEl().select('tbody', true).first();
10016 var tfd = this.getGridEl().select('tfoot', true).first();
10018 var cw = ctr.getWidth();
10019 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
10023 tbd.setWidth(ctr.getWidth());
10024 // if the body has a max height - and then scrolls - we should perhaps set up the height here
10025 // this needs fixing for various usage - currently only hydra job advers I think..
10027 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10029 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10032 cw = Math.max(cw, this.totalWidth);
10033 this.getGridEl().select('tbody tr',true).setWidth(cw);
10035 // resize 'expandable coloumn?
10037 return; // we doe not have a view in this design..
10040 onBodyScroll: function()
10042 //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10044 this.headEl.setStyle({
10045 'position' : 'relative',
10046 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10052 var scrollHeight = this.bodyEl.dom.scrollHeight;
10054 var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10056 var height = this.bodyEl.getHeight();
10058 if(scrollHeight - height == scrollTop) {
10060 var total = this.ds.getTotalCount();
10062 if(this.footer.cursor + this.footer.pageSize < total){
10064 this.footer.ds.load({
10066 start : this.footer.cursor + this.footer.pageSize,
10067 limit : this.footer.pageSize
10076 onColumnSplitterMoved : function(i, w)
10078 this.userResized = true;
10080 var cm = this.colModel;
10082 cm.setColumnWidth(i, w, true);
10083 //var cid = cm.getColumnId(i); << not used in this version?
10084 Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10086 this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10087 this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10089 //this.updateSplitters();
10090 //this.layout(); << ??
10091 this.fireEvent("columnresize", i, w);
10093 onHeaderChange : function()
10095 var header = this.renderHeader();
10096 var table = this.el.select('table', true).first();
10098 this.headEl.remove();
10099 this.headEl = table.createChild(header, this.bodyEl, false);
10101 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10102 e.on('click', this.sort, this);
10105 if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10106 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10111 onHiddenChange : function(colModel, colIndex, hidden)
10113 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10114 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10116 this.CSS.updateRule(thSelector, "display", "");
10117 this.CSS.updateRule(tdSelector, "display", "");
10120 this.CSS.updateRule(thSelector, "display", "none");
10121 this.CSS.updateRule(tdSelector, "display", "none");
10124 this.onHeaderChange();
10128 setColumnWidth: function(col_index, width)
10130 // width = "md-2 xs-2..."
10131 if(!this.colModel.config[col_index]) {
10135 var w = width.split(" ");
10137 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10139 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10142 for(var j = 0; j < w.length; j++) {
10148 var size_cls = w[j].split("-");
10150 if(!Number.isInteger(size_cls[1] * 1)) {
10154 if(!this.colModel.config[col_index][size_cls[0]]) {
10158 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10162 h_row[0].classList.replace(
10163 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10164 "col-"+size_cls[0]+"-"+size_cls[1]
10167 for(var i = 0; i < rows.length; i++) {
10169 var size_cls = w[j].split("-");
10171 if(!Number.isInteger(size_cls[1] * 1)) {
10175 if(!this.colModel.config[col_index][size_cls[0]]) {
10179 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10183 rows[i].classList.replace(
10184 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10185 "col-"+size_cls[0]+"-"+size_cls[1]
10189 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10194 // currently only used to find the split on drag..
10195 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10200 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10201 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10210 * @class Roo.bootstrap.TableCell
10211 * @extends Roo.bootstrap.Component
10212 * Bootstrap TableCell class
10213 * @cfg {String} html cell contain text
10214 * @cfg {String} cls cell class
10215 * @cfg {String} tag cell tag (td|th) default td
10216 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10217 * @cfg {String} align Aligns the content in a cell
10218 * @cfg {String} axis Categorizes cells
10219 * @cfg {String} bgcolor Specifies the background color of a cell
10220 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10221 * @cfg {Number} colspan Specifies the number of columns a cell should span
10222 * @cfg {String} headers Specifies one or more header cells a cell is related to
10223 * @cfg {Number} height Sets the height of a cell
10224 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10225 * @cfg {Number} rowspan Sets the number of rows a cell should span
10226 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10227 * @cfg {String} valign Vertical aligns the content in a cell
10228 * @cfg {Number} width Specifies the width of a cell
10231 * Create a new TableCell
10232 * @param {Object} config The config object
10235 Roo.bootstrap.TableCell = function(config){
10236 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10239 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
10259 getAutoCreate : function(){
10260 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10267 cfg.tag = this.tag;
10280 cfg.align=this.align
10285 if (this.bgcolor) {
10286 cfg.bgcolor=this.bgcolor
10288 if (this.charoff) {
10289 cfg.charoff=this.charoff
10291 if (this.colspan) {
10292 cfg.colspan=this.colspan
10294 if (this.headers) {
10295 cfg.headers=this.headers
10298 cfg.height=this.height
10301 cfg.nowrap=this.nowrap
10303 if (this.rowspan) {
10304 cfg.rowspan=this.rowspan
10307 cfg.scope=this.scope
10310 cfg.valign=this.valign
10313 cfg.width=this.width
10332 * @class Roo.bootstrap.TableRow
10333 * @extends Roo.bootstrap.Component
10334 * Bootstrap TableRow class
10335 * @cfg {String} cls row class
10336 * @cfg {String} align Aligns the content in a table row
10337 * @cfg {String} bgcolor Specifies a background color for a table row
10338 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10339 * @cfg {String} valign Vertical aligns the content in a table row
10342 * Create a new TableRow
10343 * @param {Object} config The config object
10346 Roo.bootstrap.TableRow = function(config){
10347 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10350 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
10358 getAutoCreate : function(){
10359 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10366 cfg.cls = this.cls;
10369 cfg.align = this.align;
10372 cfg.bgcolor = this.bgcolor;
10375 cfg.charoff = this.charoff;
10378 cfg.valign = this.valign;
10396 * @class Roo.bootstrap.TableBody
10397 * @extends Roo.bootstrap.Component
10398 * Bootstrap TableBody class
10399 * @cfg {String} cls element class
10400 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10401 * @cfg {String} align Aligns the content inside the element
10402 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10403 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10406 * Create a new TableBody
10407 * @param {Object} config The config object
10410 Roo.bootstrap.TableBody = function(config){
10411 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10414 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
10422 getAutoCreate : function(){
10423 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10433 cfg.tag = this.tag;
10437 cfg.align = this.align;
10440 cfg.charoff = this.charoff;
10443 cfg.valign = this.valign;
10450 // initEvents : function()
10453 // if(!this.store){
10457 // this.store = Roo.factory(this.store, Roo.data);
10458 // this.store.on('load', this.onLoad, this);
10460 // this.store.load();
10464 // onLoad: function ()
10466 // this.fireEvent('load', this);
10476 * Ext JS Library 1.1.1
10477 * Copyright(c) 2006-2007, Ext JS, LLC.
10479 * Originally Released Under LGPL - original licence link has changed is not relivant.
10482 * <script type="text/javascript">
10485 // as we use this in bootstrap.
10486 Roo.namespace('Roo.form');
10488 * @class Roo.form.Action
10489 * Internal Class used to handle form actions
10491 * @param {Roo.form.BasicForm} el The form element or its id
10492 * @param {Object} config Configuration options
10497 // define the action interface
10498 Roo.form.Action = function(form, options){
10500 this.options = options || {};
10503 * Client Validation Failed
10506 Roo.form.Action.CLIENT_INVALID = 'client';
10508 * Server Validation Failed
10511 Roo.form.Action.SERVER_INVALID = 'server';
10513 * Connect to Server Failed
10516 Roo.form.Action.CONNECT_FAILURE = 'connect';
10518 * Reading Data from Server Failed
10521 Roo.form.Action.LOAD_FAILURE = 'load';
10523 Roo.form.Action.prototype = {
10525 failureType : undefined,
10526 response : undefined,
10527 result : undefined,
10529 // interface method
10530 run : function(options){
10534 // interface method
10535 success : function(response){
10539 // interface method
10540 handleResponse : function(response){
10544 // default connection failure
10545 failure : function(response){
10547 this.response = response;
10548 this.failureType = Roo.form.Action.CONNECT_FAILURE;
10549 this.form.afterAction(this, false);
10552 processResponse : function(response){
10553 this.response = response;
10554 if(!response.responseText){
10557 this.result = this.handleResponse(response);
10558 return this.result;
10561 // utility functions used internally
10562 getUrl : function(appendParams){
10563 var url = this.options.url || this.form.url || this.form.el.dom.action;
10565 var p = this.getParams();
10567 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
10573 getMethod : function(){
10574 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
10577 getParams : function(){
10578 var bp = this.form.baseParams;
10579 var p = this.options.params;
10581 if(typeof p == "object"){
10582 p = Roo.urlEncode(Roo.applyIf(p, bp));
10583 }else if(typeof p == 'string' && bp){
10584 p += '&' + Roo.urlEncode(bp);
10587 p = Roo.urlEncode(bp);
10592 createCallback : function(){
10594 success: this.success,
10595 failure: this.failure,
10597 timeout: (this.form.timeout*1000),
10598 upload: this.form.fileUpload ? this.success : undefined
10603 Roo.form.Action.Submit = function(form, options){
10604 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
10607 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
10610 haveProgress : false,
10611 uploadComplete : false,
10613 // uploadProgress indicator.
10614 uploadProgress : function()
10616 if (!this.form.progressUrl) {
10620 if (!this.haveProgress) {
10621 Roo.MessageBox.progress("Uploading", "Uploading");
10623 if (this.uploadComplete) {
10624 Roo.MessageBox.hide();
10628 this.haveProgress = true;
10630 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
10632 var c = new Roo.data.Connection();
10634 url : this.form.progressUrl,
10639 success : function(req){
10640 //console.log(data);
10644 rdata = Roo.decode(req.responseText)
10646 Roo.log("Invalid data from server..");
10650 if (!rdata || !rdata.success) {
10652 Roo.MessageBox.alert(Roo.encode(rdata));
10655 var data = rdata.data;
10657 if (this.uploadComplete) {
10658 Roo.MessageBox.hide();
10663 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
10664 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
10667 this.uploadProgress.defer(2000,this);
10670 failure: function(data) {
10671 Roo.log('progress url failed ');
10682 // run get Values on the form, so it syncs any secondary forms.
10683 this.form.getValues();
10685 var o = this.options;
10686 var method = this.getMethod();
10687 var isPost = method == 'POST';
10688 if(o.clientValidation === false || this.form.isValid()){
10690 if (this.form.progressUrl) {
10691 this.form.findField('UPLOAD_IDENTIFIER').setValue(
10692 (new Date() * 1) + '' + Math.random());
10697 Roo.Ajax.request(Roo.apply(this.createCallback(), {
10698 form:this.form.el.dom,
10699 url:this.getUrl(!isPost),
10701 params:isPost ? this.getParams() : null,
10702 isUpload: this.form.fileUpload,
10703 formData : this.form.formData
10706 this.uploadProgress();
10708 }else if (o.clientValidation !== false){ // client validation failed
10709 this.failureType = Roo.form.Action.CLIENT_INVALID;
10710 this.form.afterAction(this, false);
10714 success : function(response)
10716 this.uploadComplete= true;
10717 if (this.haveProgress) {
10718 Roo.MessageBox.hide();
10722 var result = this.processResponse(response);
10723 if(result === true || result.success){
10724 this.form.afterAction(this, true);
10728 this.form.markInvalid(result.errors);
10729 this.failureType = Roo.form.Action.SERVER_INVALID;
10731 this.form.afterAction(this, false);
10733 failure : function(response)
10735 this.uploadComplete= true;
10736 if (this.haveProgress) {
10737 Roo.MessageBox.hide();
10740 this.response = response;
10741 this.failureType = Roo.form.Action.CONNECT_FAILURE;
10742 this.form.afterAction(this, false);
10745 handleResponse : function(response){
10746 if(this.form.errorReader){
10747 var rs = this.form.errorReader.read(response);
10750 for(var i = 0, len = rs.records.length; i < len; i++) {
10751 var r = rs.records[i];
10752 errors[i] = r.data;
10755 if(errors.length < 1){
10759 success : rs.success,
10765 ret = Roo.decode(response.responseText);
10769 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
10779 Roo.form.Action.Load = function(form, options){
10780 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
10781 this.reader = this.form.reader;
10784 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
10789 Roo.Ajax.request(Roo.apply(
10790 this.createCallback(), {
10791 method:this.getMethod(),
10792 url:this.getUrl(false),
10793 params:this.getParams()
10797 success : function(response){
10799 var result = this.processResponse(response);
10800 if(result === true || !result.success || !result.data){
10801 this.failureType = Roo.form.Action.LOAD_FAILURE;
10802 this.form.afterAction(this, false);
10805 this.form.clearInvalid();
10806 this.form.setValues(result.data);
10807 this.form.afterAction(this, true);
10810 handleResponse : function(response){
10811 if(this.form.reader){
10812 var rs = this.form.reader.read(response);
10813 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
10815 success : rs.success,
10819 return Roo.decode(response.responseText);
10823 Roo.form.Action.ACTION_TYPES = {
10824 'load' : Roo.form.Action.Load,
10825 'submit' : Roo.form.Action.Submit
10834 * @class Roo.bootstrap.Form
10835 * @extends Roo.bootstrap.Component
10836 * Bootstrap Form class
10837 * @cfg {String} method GET | POST (default POST)
10838 * @cfg {String} labelAlign top | left (default top)
10839 * @cfg {String} align left | right - for navbars
10840 * @cfg {Boolean} loadMask load mask when submit (default true)
10844 * Create a new Form
10845 * @param {Object} config The config object
10849 Roo.bootstrap.Form = function(config){
10851 Roo.bootstrap.Form.superclass.constructor.call(this, config);
10853 Roo.bootstrap.Form.popover.apply();
10857 * @event clientvalidation
10858 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
10859 * @param {Form} this
10860 * @param {Boolean} valid true if the form has passed client-side validation
10862 clientvalidation: true,
10864 * @event beforeaction
10865 * Fires before any action is performed. Return false to cancel the action.
10866 * @param {Form} this
10867 * @param {Action} action The action to be performed
10869 beforeaction: true,
10871 * @event actionfailed
10872 * Fires when an action fails.
10873 * @param {Form} this
10874 * @param {Action} action The action that failed
10876 actionfailed : true,
10878 * @event actioncomplete
10879 * Fires when an action is completed.
10880 * @param {Form} this
10881 * @param {Action} action The action that completed
10883 actioncomplete : true
10887 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
10890 * @cfg {String} method
10891 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
10895 * @cfg {String} url
10896 * The URL to use for form actions if one isn't supplied in the action options.
10899 * @cfg {Boolean} fileUpload
10900 * Set to true if this form is a file upload.
10904 * @cfg {Object} baseParams
10905 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
10909 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
10913 * @cfg {Sting} align (left|right) for navbar forms
10918 activeAction : null,
10921 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
10922 * element by passing it or its id or mask the form itself by passing in true.
10925 waitMsgTarget : false,
10930 * @cfg {Boolean} errorMask (true|false) default false
10935 * @cfg {Number} maskOffset Default 100
10940 * @cfg {Boolean} maskBody
10944 getAutoCreate : function(){
10948 method : this.method || 'POST',
10949 id : this.id || Roo.id(),
10952 if (this.parent().xtype.match(/^Nav/)) {
10953 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
10957 if (this.labelAlign == 'left' ) {
10958 cfg.cls += ' form-horizontal';
10964 initEvents : function()
10966 this.el.on('submit', this.onSubmit, this);
10967 // this was added as random key presses on the form where triggering form submit.
10968 this.el.on('keypress', function(e) {
10969 if (e.getCharCode() != 13) {
10972 // we might need to allow it for textareas.. and some other items.
10973 // check e.getTarget().
10975 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
10979 Roo.log("keypress blocked");
10981 e.preventDefault();
10987 onSubmit : function(e){
10992 * Returns true if client-side validation on the form is successful.
10995 isValid : function(){
10996 var items = this.getItems();
10998 var target = false;
11000 items.each(function(f){
11006 Roo.log('invalid field: ' + f.name);
11010 if(!target && f.el.isVisible(true)){
11016 if(this.errorMask && !valid){
11017 Roo.bootstrap.Form.popover.mask(this, target);
11024 * Returns true if any fields in this form have changed since their original load.
11027 isDirty : function(){
11029 var items = this.getItems();
11030 items.each(function(f){
11040 * Performs a predefined action (submit or load) or custom actions you define on this form.
11041 * @param {String} actionName The name of the action type
11042 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
11043 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11044 * accept other config options):
11046 Property Type Description
11047 ---------------- --------------- ----------------------------------------------------------------------------------
11048 url String The url for the action (defaults to the form's url)
11049 method String The form method to use (defaults to the form's method, or POST if not defined)
11050 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
11051 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
11052 validate the form on the client (defaults to false)
11054 * @return {BasicForm} this
11056 doAction : function(action, options){
11057 if(typeof action == 'string'){
11058 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11060 if(this.fireEvent('beforeaction', this, action) !== false){
11061 this.beforeAction(action);
11062 action.run.defer(100, action);
11068 beforeAction : function(action){
11069 var o = action.options;
11074 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11076 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11079 // not really supported yet.. ??
11081 //if(this.waitMsgTarget === true){
11082 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11083 //}else if(this.waitMsgTarget){
11084 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11085 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11087 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11093 afterAction : function(action, success){
11094 this.activeAction = null;
11095 var o = action.options;
11100 Roo.get(document.body).unmask();
11106 //if(this.waitMsgTarget === true){
11107 // this.el.unmask();
11108 //}else if(this.waitMsgTarget){
11109 // this.waitMsgTarget.unmask();
11111 // Roo.MessageBox.updateProgress(1);
11112 // Roo.MessageBox.hide();
11119 Roo.callback(o.success, o.scope, [this, action]);
11120 this.fireEvent('actioncomplete', this, action);
11124 // failure condition..
11125 // we have a scenario where updates need confirming.
11126 // eg. if a locking scenario exists..
11127 // we look for { errors : { needs_confirm : true }} in the response.
11129 (typeof(action.result) != 'undefined') &&
11130 (typeof(action.result.errors) != 'undefined') &&
11131 (typeof(action.result.errors.needs_confirm) != 'undefined')
11134 Roo.log("not supported yet");
11137 Roo.MessageBox.confirm(
11138 "Change requires confirmation",
11139 action.result.errorMsg,
11144 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
11154 Roo.callback(o.failure, o.scope, [this, action]);
11155 // show an error message if no failed handler is set..
11156 if (!this.hasListener('actionfailed')) {
11157 Roo.log("need to add dialog support");
11159 Roo.MessageBox.alert("Error",
11160 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11161 action.result.errorMsg :
11162 "Saving Failed, please check your entries or try again"
11167 this.fireEvent('actionfailed', this, action);
11172 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11173 * @param {String} id The value to search for
11176 findField : function(id){
11177 var items = this.getItems();
11178 var field = items.get(id);
11180 items.each(function(f){
11181 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11188 return field || null;
11191 * Mark fields in this form invalid in bulk.
11192 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11193 * @return {BasicForm} this
11195 markInvalid : function(errors){
11196 if(errors instanceof Array){
11197 for(var i = 0, len = errors.length; i < len; i++){
11198 var fieldError = errors[i];
11199 var f = this.findField(fieldError.id);
11201 f.markInvalid(fieldError.msg);
11207 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11208 field.markInvalid(errors[id]);
11212 //Roo.each(this.childForms || [], function (f) {
11213 // f.markInvalid(errors);
11220 * Set values for fields in this form in bulk.
11221 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11222 * @return {BasicForm} this
11224 setValues : function(values){
11225 if(values instanceof Array){ // array of objects
11226 for(var i = 0, len = values.length; i < len; i++){
11228 var f = this.findField(v.id);
11230 f.setValue(v.value);
11231 if(this.trackResetOnLoad){
11232 f.originalValue = f.getValue();
11236 }else{ // object hash
11239 if(typeof values[id] != 'function' && (field = this.findField(id))){
11241 if (field.setFromData &&
11242 field.valueField &&
11243 field.displayField &&
11244 // combos' with local stores can
11245 // be queried via setValue()
11246 // to set their value..
11247 (field.store && !field.store.isLocal)
11251 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11252 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11253 field.setFromData(sd);
11255 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11257 field.setFromData(values);
11260 field.setValue(values[id]);
11264 if(this.trackResetOnLoad){
11265 field.originalValue = field.getValue();
11271 //Roo.each(this.childForms || [], function (f) {
11272 // f.setValues(values);
11279 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11280 * they are returned as an array.
11281 * @param {Boolean} asString
11284 getValues : function(asString){
11285 //if (this.childForms) {
11286 // copy values from the child forms
11287 // Roo.each(this.childForms, function (f) {
11288 // this.setValues(f.getValues());
11294 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11295 if(asString === true){
11298 return Roo.urlDecode(fs);
11302 * Returns the fields in this form as an object with key/value pairs.
11303 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11306 getFieldValues : function(with_hidden)
11308 var items = this.getItems();
11310 items.each(function(f){
11312 if (!f.getName()) {
11316 var v = f.getValue();
11318 if (f.inputType =='radio') {
11319 if (typeof(ret[f.getName()]) == 'undefined') {
11320 ret[f.getName()] = ''; // empty..
11323 if (!f.el.dom.checked) {
11327 v = f.el.dom.value;
11331 if(f.xtype == 'MoneyField'){
11332 ret[f.currencyName] = f.getCurrency();
11335 // not sure if this supported any more..
11336 if ((typeof(v) == 'object') && f.getRawValue) {
11337 v = f.getRawValue() ; // dates..
11339 // combo boxes where name != hiddenName...
11340 if (f.name !== false && f.name != '' && f.name != f.getName()) {
11341 ret[f.name] = f.getRawValue();
11343 ret[f.getName()] = v;
11350 * Clears all invalid messages in this form.
11351 * @return {BasicForm} this
11353 clearInvalid : function(){
11354 var items = this.getItems();
11356 items.each(function(f){
11364 * Resets this form.
11365 * @return {BasicForm} this
11367 reset : function(){
11368 var items = this.getItems();
11369 items.each(function(f){
11373 Roo.each(this.childForms || [], function (f) {
11381 getItems : function()
11383 var r=new Roo.util.MixedCollection(false, function(o){
11384 return o.id || (o.id = Roo.id());
11386 var iter = function(el) {
11393 Roo.each(el.items,function(e) {
11402 hideFields : function(items)
11404 Roo.each(items, function(i){
11406 var f = this.findField(i);
11417 showFields : function(items)
11419 Roo.each(items, function(i){
11421 var f = this.findField(i);
11434 Roo.apply(Roo.bootstrap.Form, {
11450 intervalID : false,
11456 if(this.isApplied){
11461 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11462 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11463 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11464 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11467 this.maskEl.top.enableDisplayMode("block");
11468 this.maskEl.left.enableDisplayMode("block");
11469 this.maskEl.bottom.enableDisplayMode("block");
11470 this.maskEl.right.enableDisplayMode("block");
11472 this.toolTip = new Roo.bootstrap.Tooltip({
11473 cls : 'roo-form-error-popover',
11475 'left' : ['r-l', [-2,0], 'right'],
11476 'right' : ['l-r', [2,0], 'left'],
11477 'bottom' : ['tl-bl', [0,2], 'top'],
11478 'top' : [ 'bl-tl', [0,-2], 'bottom']
11482 this.toolTip.render(Roo.get(document.body));
11484 this.toolTip.el.enableDisplayMode("block");
11486 Roo.get(document.body).on('click', function(){
11490 Roo.get(document.body).on('touchstart', function(){
11494 this.isApplied = true
11497 mask : function(form, target)
11501 this.target = target;
11503 if(!this.form.errorMask || !target.el){
11507 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
11509 Roo.log(scrollable);
11511 var ot = this.target.el.calcOffsetsTo(scrollable);
11513 var scrollTo = ot[1] - this.form.maskOffset;
11515 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
11517 scrollable.scrollTo('top', scrollTo);
11519 var box = this.target.el.getBox();
11521 var zIndex = Roo.bootstrap.Modal.zIndex++;
11524 this.maskEl.top.setStyle('position', 'absolute');
11525 this.maskEl.top.setStyle('z-index', zIndex);
11526 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
11527 this.maskEl.top.setLeft(0);
11528 this.maskEl.top.setTop(0);
11529 this.maskEl.top.show();
11531 this.maskEl.left.setStyle('position', 'absolute');
11532 this.maskEl.left.setStyle('z-index', zIndex);
11533 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
11534 this.maskEl.left.setLeft(0);
11535 this.maskEl.left.setTop(box.y - this.padding);
11536 this.maskEl.left.show();
11538 this.maskEl.bottom.setStyle('position', 'absolute');
11539 this.maskEl.bottom.setStyle('z-index', zIndex);
11540 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
11541 this.maskEl.bottom.setLeft(0);
11542 this.maskEl.bottom.setTop(box.bottom + this.padding);
11543 this.maskEl.bottom.show();
11545 this.maskEl.right.setStyle('position', 'absolute');
11546 this.maskEl.right.setStyle('z-index', zIndex);
11547 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
11548 this.maskEl.right.setLeft(box.right + this.padding);
11549 this.maskEl.right.setTop(box.y - this.padding);
11550 this.maskEl.right.show();
11552 this.toolTip.bindEl = this.target.el;
11554 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
11556 var tip = this.target.blankText;
11558 if(this.target.getValue() !== '' ) {
11560 if (this.target.invalidText.length) {
11561 tip = this.target.invalidText;
11562 } else if (this.target.regexText.length){
11563 tip = this.target.regexText;
11567 this.toolTip.show(tip);
11569 this.intervalID = window.setInterval(function() {
11570 Roo.bootstrap.Form.popover.unmask();
11573 window.onwheel = function(){ return false;};
11575 (function(){ this.isMasked = true; }).defer(500, this);
11579 unmask : function()
11581 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
11585 this.maskEl.top.setStyle('position', 'absolute');
11586 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
11587 this.maskEl.top.hide();
11589 this.maskEl.left.setStyle('position', 'absolute');
11590 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
11591 this.maskEl.left.hide();
11593 this.maskEl.bottom.setStyle('position', 'absolute');
11594 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
11595 this.maskEl.bottom.hide();
11597 this.maskEl.right.setStyle('position', 'absolute');
11598 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
11599 this.maskEl.right.hide();
11601 this.toolTip.hide();
11603 this.toolTip.el.hide();
11605 window.onwheel = function(){ return true;};
11607 if(this.intervalID){
11608 window.clearInterval(this.intervalID);
11609 this.intervalID = false;
11612 this.isMasked = false;
11622 * Ext JS Library 1.1.1
11623 * Copyright(c) 2006-2007, Ext JS, LLC.
11625 * Originally Released Under LGPL - original licence link has changed is not relivant.
11628 * <script type="text/javascript">
11631 * @class Roo.form.VTypes
11632 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
11635 Roo.form.VTypes = function(){
11636 // closure these in so they are only created once.
11637 var alpha = /^[a-zA-Z_]+$/;
11638 var alphanum = /^[a-zA-Z0-9_]+$/;
11639 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
11640 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
11642 // All these messages and functions are configurable
11645 * The function used to validate email addresses
11646 * @param {String} value The email address
11648 'email' : function(v){
11649 return email.test(v);
11652 * The error text to display when the email validation function returns false
11655 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
11657 * The keystroke filter mask to be applied on email input
11660 'emailMask' : /[a-z0-9_\.\-@]/i,
11663 * The function used to validate URLs
11664 * @param {String} value The URL
11666 'url' : function(v){
11667 return url.test(v);
11670 * The error text to display when the url validation function returns false
11673 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
11676 * The function used to validate alpha values
11677 * @param {String} value The value
11679 'alpha' : function(v){
11680 return alpha.test(v);
11683 * The error text to display when the alpha validation function returns false
11686 'alphaText' : 'This field should only contain letters and _',
11688 * The keystroke filter mask to be applied on alpha input
11691 'alphaMask' : /[a-z_]/i,
11694 * The function used to validate alphanumeric values
11695 * @param {String} value The value
11697 'alphanum' : function(v){
11698 return alphanum.test(v);
11701 * The error text to display when the alphanumeric validation function returns false
11704 'alphanumText' : 'This field should only contain letters, numbers and _',
11706 * The keystroke filter mask to be applied on alphanumeric input
11709 'alphanumMask' : /[a-z0-9_]/i
11719 * @class Roo.bootstrap.Input
11720 * @extends Roo.bootstrap.Component
11721 * Bootstrap Input class
11722 * @cfg {Boolean} disabled is it disabled
11723 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
11724 * @cfg {String} name name of the input
11725 * @cfg {string} fieldLabel - the label associated
11726 * @cfg {string} placeholder - placeholder to put in text.
11727 * @cfg {string} before - input group add on before
11728 * @cfg {string} after - input group add on after
11729 * @cfg {string} size - (lg|sm) or leave empty..
11730 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
11731 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
11732 * @cfg {Number} md colspan out of 12 for computer-sized screens
11733 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
11734 * @cfg {string} value default value of the input
11735 * @cfg {Number} labelWidth set the width of label
11736 * @cfg {Number} labellg set the width of label (1-12)
11737 * @cfg {Number} labelmd set the width of label (1-12)
11738 * @cfg {Number} labelsm set the width of label (1-12)
11739 * @cfg {Number} labelxs set the width of label (1-12)
11740 * @cfg {String} labelAlign (top|left)
11741 * @cfg {Boolean} readOnly Specifies that the field should be read-only
11742 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
11743 * @cfg {String} indicatorpos (left|right) default left
11744 * @cfg {String} capture (user|camera) use for file input only. (default empty)
11745 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
11746 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
11748 * @cfg {String} align (left|center|right) Default left
11749 * @cfg {Boolean} forceFeedback (true|false) Default false
11752 * Create a new Input
11753 * @param {Object} config The config object
11756 Roo.bootstrap.Input = function(config){
11758 Roo.bootstrap.Input.superclass.constructor.call(this, config);
11763 * Fires when this field receives input focus.
11764 * @param {Roo.form.Field} this
11769 * Fires when this field loses input focus.
11770 * @param {Roo.form.Field} this
11774 * @event specialkey
11775 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
11776 * {@link Roo.EventObject#getKey} to determine which key was pressed.
11777 * @param {Roo.form.Field} this
11778 * @param {Roo.EventObject} e The event object
11783 * Fires just before the field blurs if the field value has changed.
11784 * @param {Roo.form.Field} this
11785 * @param {Mixed} newValue The new value
11786 * @param {Mixed} oldValue The original value
11791 * Fires after the field has been marked as invalid.
11792 * @param {Roo.form.Field} this
11793 * @param {String} msg The validation message
11798 * Fires after the field has been validated with no errors.
11799 * @param {Roo.form.Field} this
11804 * Fires after the key up
11805 * @param {Roo.form.Field} this
11806 * @param {Roo.EventObject} e The event Object
11811 * Fires after the user pastes into input
11812 * @param {Roo.form.Field} this
11813 * @param {Roo.EventObject} e The event Object
11819 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
11821 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
11822 automatic validation (defaults to "keyup").
11824 validationEvent : "keyup",
11826 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
11828 validateOnBlur : true,
11830 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
11832 validationDelay : 250,
11834 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
11836 focusClass : "x-form-focus", // not needed???
11840 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11842 invalidClass : "has-warning",
11845 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11847 validClass : "has-success",
11850 * @cfg {Boolean} hasFeedback (true|false) default true
11852 hasFeedback : true,
11855 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11857 invalidFeedbackClass : "glyphicon-warning-sign",
11860 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11862 validFeedbackClass : "glyphicon-ok",
11865 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
11867 selectOnFocus : false,
11870 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
11874 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
11879 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
11881 disableKeyFilter : false,
11884 * @cfg {Boolean} disabled True to disable the field (defaults to false).
11888 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
11892 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
11894 blankText : "Please complete this mandatory field",
11897 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
11901 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
11903 maxLength : Number.MAX_VALUE,
11905 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
11907 minLengthText : "The minimum length for this field is {0}",
11909 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
11911 maxLengthText : "The maximum length for this field is {0}",
11915 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
11916 * If available, this function will be called only after the basic validators all return true, and will be passed the
11917 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
11921 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
11922 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
11923 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
11927 * @cfg {String} regexText -- Depricated - use Invalid Text
11932 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
11938 autocomplete: false,
11942 inputType : 'text',
11945 placeholder: false,
11950 preventMark: false,
11951 isFormField : true,
11954 labelAlign : false,
11957 formatedValue : false,
11958 forceFeedback : false,
11960 indicatorpos : 'left',
11970 parentLabelAlign : function()
11973 while (parent.parent()) {
11974 parent = parent.parent();
11975 if (typeof(parent.labelAlign) !='undefined') {
11976 return parent.labelAlign;
11983 getAutoCreate : function()
11985 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11991 if(this.inputType != 'hidden'){
11992 cfg.cls = 'form-group' //input-group
11998 type : this.inputType,
11999 value : this.value,
12000 cls : 'form-control',
12001 placeholder : this.placeholder || '',
12002 autocomplete : this.autocomplete || 'new-password'
12004 if (this.inputType == 'file') {
12005 input.style = 'overflow:hidden'; // why not in CSS?
12008 if(this.capture.length){
12009 input.capture = this.capture;
12012 if(this.accept.length){
12013 input.accept = this.accept + "/*";
12017 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12020 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12021 input.maxLength = this.maxLength;
12024 if (this.disabled) {
12025 input.disabled=true;
12028 if (this.readOnly) {
12029 input.readonly=true;
12033 input.name = this.name;
12037 input.cls += ' input-' + this.size;
12041 ['xs','sm','md','lg'].map(function(size){
12042 if (settings[size]) {
12043 cfg.cls += ' col-' + size + '-' + settings[size];
12047 var inputblock = input;
12051 cls: 'glyphicon form-control-feedback'
12054 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12057 cls : 'has-feedback',
12065 if (this.before || this.after) {
12068 cls : 'input-group',
12072 if (this.before && typeof(this.before) == 'string') {
12074 inputblock.cn.push({
12076 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12080 if (this.before && typeof(this.before) == 'object') {
12081 this.before = Roo.factory(this.before);
12083 inputblock.cn.push({
12085 cls : 'roo-input-before input-group-prepend input-group-' +
12086 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12090 inputblock.cn.push(input);
12092 if (this.after && typeof(this.after) == 'string') {
12093 inputblock.cn.push({
12095 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12099 if (this.after && typeof(this.after) == 'object') {
12100 this.after = Roo.factory(this.after);
12102 inputblock.cn.push({
12104 cls : 'roo-input-after input-group-append input-group-' +
12105 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12109 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12110 inputblock.cls += ' has-feedback';
12111 inputblock.cn.push(feedback);
12116 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12117 tooltip : 'This field is required'
12119 if (this.allowBlank ) {
12120 indicator.style = this.allowBlank ? ' display:none' : '';
12122 if (align ==='left' && this.fieldLabel.length) {
12124 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12131 cls : 'control-label col-form-label',
12132 html : this.fieldLabel
12143 var labelCfg = cfg.cn[1];
12144 var contentCfg = cfg.cn[2];
12146 if(this.indicatorpos == 'right'){
12151 cls : 'control-label col-form-label',
12155 html : this.fieldLabel
12169 labelCfg = cfg.cn[0];
12170 contentCfg = cfg.cn[1];
12174 if(this.labelWidth > 12){
12175 labelCfg.style = "width: " + this.labelWidth + 'px';
12178 if(this.labelWidth < 13 && this.labelmd == 0){
12179 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12182 if(this.labellg > 0){
12183 labelCfg.cls += ' col-lg-' + this.labellg;
12184 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12187 if(this.labelmd > 0){
12188 labelCfg.cls += ' col-md-' + this.labelmd;
12189 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12192 if(this.labelsm > 0){
12193 labelCfg.cls += ' col-sm-' + this.labelsm;
12194 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12197 if(this.labelxs > 0){
12198 labelCfg.cls += ' col-xs-' + this.labelxs;
12199 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12203 } else if ( this.fieldLabel.length) {
12210 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12211 tooltip : 'This field is required',
12212 style : this.allowBlank ? ' display:none' : ''
12216 //cls : 'input-group-addon',
12217 html : this.fieldLabel
12225 if(this.indicatorpos == 'right'){
12230 //cls : 'input-group-addon',
12231 html : this.fieldLabel
12236 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12237 tooltip : 'This field is required',
12238 style : this.allowBlank ? ' display:none' : ''
12258 if (this.parentType === 'Navbar' && this.parent().bar) {
12259 cfg.cls += ' navbar-form';
12262 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12263 // on BS4 we do this only if not form
12264 cfg.cls += ' navbar-form';
12272 * return the real input element.
12274 inputEl: function ()
12276 return this.el.select('input.form-control',true).first();
12279 tooltipEl : function()
12281 return this.inputEl();
12284 indicatorEl : function()
12286 if (Roo.bootstrap.version == 4) {
12287 return false; // not enabled in v4 yet.
12290 var indicator = this.el.select('i.roo-required-indicator',true).first();
12300 setDisabled : function(v)
12302 var i = this.inputEl().dom;
12304 i.removeAttribute('disabled');
12308 i.setAttribute('disabled','true');
12310 initEvents : function()
12313 this.inputEl().on("keydown" , this.fireKey, this);
12314 this.inputEl().on("focus", this.onFocus, this);
12315 this.inputEl().on("blur", this.onBlur, this);
12317 this.inputEl().relayEvent('keyup', this);
12318 this.inputEl().relayEvent('paste', this);
12320 this.indicator = this.indicatorEl();
12322 if(this.indicator){
12323 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
12326 // reference to original value for reset
12327 this.originalValue = this.getValue();
12328 //Roo.form.TextField.superclass.initEvents.call(this);
12329 if(this.validationEvent == 'keyup'){
12330 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12331 this.inputEl().on('keyup', this.filterValidation, this);
12333 else if(this.validationEvent !== false){
12334 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12337 if(this.selectOnFocus){
12338 this.on("focus", this.preFocus, this);
12341 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12342 this.inputEl().on("keypress", this.filterKeys, this);
12344 this.inputEl().relayEvent('keypress', this);
12347 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
12348 this.el.on("click", this.autoSize, this);
12351 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12352 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12355 if (typeof(this.before) == 'object') {
12356 this.before.render(this.el.select('.roo-input-before',true).first());
12358 if (typeof(this.after) == 'object') {
12359 this.after.render(this.el.select('.roo-input-after',true).first());
12362 this.inputEl().on('change', this.onChange, this);
12365 filterValidation : function(e){
12366 if(!e.isNavKeyPress()){
12367 this.validationTask.delay(this.validationDelay);
12371 * Validates the field value
12372 * @return {Boolean} True if the value is valid, else false
12374 validate : function(){
12375 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12376 if(this.disabled || this.validateValue(this.getRawValue())){
12381 this.markInvalid();
12387 * Validates a value according to the field's validation rules and marks the field as invalid
12388 * if the validation fails
12389 * @param {Mixed} value The value to validate
12390 * @return {Boolean} True if the value is valid, else false
12392 validateValue : function(value)
12394 if(this.getVisibilityEl().hasClass('hidden')){
12398 if(value.length < 1) { // if it's blank
12399 if(this.allowBlank){
12405 if(value.length < this.minLength){
12408 if(value.length > this.maxLength){
12412 var vt = Roo.form.VTypes;
12413 if(!vt[this.vtype](value, this)){
12417 if(typeof this.validator == "function"){
12418 var msg = this.validator(value);
12422 if (typeof(msg) == 'string') {
12423 this.invalidText = msg;
12427 if(this.regex && !this.regex.test(value)){
12435 fireKey : function(e){
12436 //Roo.log('field ' + e.getKey());
12437 if(e.isNavKeyPress()){
12438 this.fireEvent("specialkey", this, e);
12441 focus : function (selectText){
12443 this.inputEl().focus();
12444 if(selectText === true){
12445 this.inputEl().dom.select();
12451 onFocus : function(){
12452 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12453 // this.el.addClass(this.focusClass);
12455 if(!this.hasFocus){
12456 this.hasFocus = true;
12457 this.startValue = this.getValue();
12458 this.fireEvent("focus", this);
12462 beforeBlur : Roo.emptyFn,
12466 onBlur : function(){
12468 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12469 //this.el.removeClass(this.focusClass);
12471 this.hasFocus = false;
12472 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12475 var v = this.getValue();
12476 if(String(v) !== String(this.startValue)){
12477 this.fireEvent('change', this, v, this.startValue);
12479 this.fireEvent("blur", this);
12482 onChange : function(e)
12484 var v = this.getValue();
12485 if(String(v) !== String(this.startValue)){
12486 this.fireEvent('change', this, v, this.startValue);
12492 * Resets the current field value to the originally loaded value and clears any validation messages
12494 reset : function(){
12495 this.setValue(this.originalValue);
12499 * Returns the name of the field
12500 * @return {Mixed} name The name field
12502 getName: function(){
12506 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
12507 * @return {Mixed} value The field value
12509 getValue : function(){
12511 var v = this.inputEl().getValue();
12516 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
12517 * @return {Mixed} value The field value
12519 getRawValue : function(){
12520 var v = this.inputEl().getValue();
12526 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
12527 * @param {Mixed} value The value to set
12529 setRawValue : function(v){
12530 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12533 selectText : function(start, end){
12534 var v = this.getRawValue();
12536 start = start === undefined ? 0 : start;
12537 end = end === undefined ? v.length : end;
12538 var d = this.inputEl().dom;
12539 if(d.setSelectionRange){
12540 d.setSelectionRange(start, end);
12541 }else if(d.createTextRange){
12542 var range = d.createTextRange();
12543 range.moveStart("character", start);
12544 range.moveEnd("character", v.length-end);
12551 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
12552 * @param {Mixed} value The value to set
12554 setValue : function(v){
12557 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12563 processValue : function(value){
12564 if(this.stripCharsRe){
12565 var newValue = value.replace(this.stripCharsRe, '');
12566 if(newValue !== value){
12567 this.setRawValue(newValue);
12574 preFocus : function(){
12576 if(this.selectOnFocus){
12577 this.inputEl().dom.select();
12580 filterKeys : function(e){
12581 var k = e.getKey();
12582 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
12585 var c = e.getCharCode(), cc = String.fromCharCode(c);
12586 if(Roo.isIE && (e.isSpecialKey() || !cc)){
12589 if(!this.maskRe.test(cc)){
12594 * Clear any invalid styles/messages for this field
12596 clearInvalid : function(){
12598 if(!this.el || this.preventMark){ // not rendered
12603 this.el.removeClass([this.invalidClass, 'is-invalid']);
12605 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12607 var feedback = this.el.select('.form-control-feedback', true).first();
12610 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12615 if(this.indicator){
12616 this.indicator.removeClass('visible');
12617 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12620 this.fireEvent('valid', this);
12624 * Mark this field as valid
12626 markValid : function()
12628 if(!this.el || this.preventMark){ // not rendered...
12632 this.el.removeClass([this.invalidClass, this.validClass]);
12633 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12635 var feedback = this.el.select('.form-control-feedback', true).first();
12638 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12641 if(this.indicator){
12642 this.indicator.removeClass('visible');
12643 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12651 if(this.allowBlank && !this.getRawValue().length){
12654 if (Roo.bootstrap.version == 3) {
12655 this.el.addClass(this.validClass);
12657 this.inputEl().addClass('is-valid');
12660 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12662 var feedback = this.el.select('.form-control-feedback', true).first();
12665 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12666 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12671 this.fireEvent('valid', this);
12675 * Mark this field as invalid
12676 * @param {String} msg The validation message
12678 markInvalid : function(msg)
12680 if(!this.el || this.preventMark){ // not rendered
12684 this.el.removeClass([this.invalidClass, this.validClass]);
12685 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12687 var feedback = this.el.select('.form-control-feedback', true).first();
12690 this.el.select('.form-control-feedback', true).first().removeClass(
12691 [this.invalidFeedbackClass, this.validFeedbackClass]);
12698 if(this.allowBlank && !this.getRawValue().length){
12702 if(this.indicator){
12703 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12704 this.indicator.addClass('visible');
12706 if (Roo.bootstrap.version == 3) {
12707 this.el.addClass(this.invalidClass);
12709 this.inputEl().addClass('is-invalid');
12714 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12716 var feedback = this.el.select('.form-control-feedback', true).first();
12719 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12721 if(this.getValue().length || this.forceFeedback){
12722 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12729 this.fireEvent('invalid', this, msg);
12732 SafariOnKeyDown : function(event)
12734 // this is a workaround for a password hang bug on chrome/ webkit.
12735 if (this.inputEl().dom.type != 'password') {
12739 var isSelectAll = false;
12741 if(this.inputEl().dom.selectionEnd > 0){
12742 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
12744 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
12745 event.preventDefault();
12750 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
12752 event.preventDefault();
12753 // this is very hacky as keydown always get's upper case.
12755 var cc = String.fromCharCode(event.getCharCode());
12756 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
12760 adjustWidth : function(tag, w){
12761 tag = tag.toLowerCase();
12762 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
12763 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
12764 if(tag == 'input'){
12767 if(tag == 'textarea'){
12770 }else if(Roo.isOpera){
12771 if(tag == 'input'){
12774 if(tag == 'textarea'){
12782 setFieldLabel : function(v)
12784 if(!this.rendered){
12788 if(this.indicatorEl()){
12789 var ar = this.el.select('label > span',true);
12791 if (ar.elements.length) {
12792 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12793 this.fieldLabel = v;
12797 var br = this.el.select('label',true);
12799 if(br.elements.length) {
12800 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12801 this.fieldLabel = v;
12805 Roo.log('Cannot Found any of label > span || label in input');
12809 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12810 this.fieldLabel = v;
12825 * @class Roo.bootstrap.TextArea
12826 * @extends Roo.bootstrap.Input
12827 * Bootstrap TextArea class
12828 * @cfg {Number} cols Specifies the visible width of a text area
12829 * @cfg {Number} rows Specifies the visible number of lines in a text area
12830 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
12831 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
12832 * @cfg {string} html text
12835 * Create a new TextArea
12836 * @param {Object} config The config object
12839 Roo.bootstrap.TextArea = function(config){
12840 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
12844 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
12854 getAutoCreate : function(){
12856 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12862 if(this.inputType != 'hidden'){
12863 cfg.cls = 'form-group' //input-group
12871 value : this.value || '',
12872 html: this.html || '',
12873 cls : 'form-control',
12874 placeholder : this.placeholder || ''
12878 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12879 input.maxLength = this.maxLength;
12883 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
12887 input.cols = this.cols;
12890 if (this.readOnly) {
12891 input.readonly = true;
12895 input.name = this.name;
12899 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
12903 ['xs','sm','md','lg'].map(function(size){
12904 if (settings[size]) {
12905 cfg.cls += ' col-' + size + '-' + settings[size];
12909 var inputblock = input;
12911 if(this.hasFeedback && !this.allowBlank){
12915 cls: 'glyphicon form-control-feedback'
12919 cls : 'has-feedback',
12928 if (this.before || this.after) {
12931 cls : 'input-group',
12935 inputblock.cn.push({
12937 cls : 'input-group-addon',
12942 inputblock.cn.push(input);
12944 if(this.hasFeedback && !this.allowBlank){
12945 inputblock.cls += ' has-feedback';
12946 inputblock.cn.push(feedback);
12950 inputblock.cn.push({
12952 cls : 'input-group-addon',
12959 if (align ==='left' && this.fieldLabel.length) {
12964 cls : 'control-label',
12965 html : this.fieldLabel
12976 if(this.labelWidth > 12){
12977 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
12980 if(this.labelWidth < 13 && this.labelmd == 0){
12981 this.labelmd = this.labelWidth;
12984 if(this.labellg > 0){
12985 cfg.cn[0].cls += ' col-lg-' + this.labellg;
12986 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
12989 if(this.labelmd > 0){
12990 cfg.cn[0].cls += ' col-md-' + this.labelmd;
12991 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
12994 if(this.labelsm > 0){
12995 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
12996 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
12999 if(this.labelxs > 0){
13000 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13001 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13004 } else if ( this.fieldLabel.length) {
13009 //cls : 'input-group-addon',
13010 html : this.fieldLabel
13028 if (this.disabled) {
13029 input.disabled=true;
13036 * return the real textarea element.
13038 inputEl: function ()
13040 return this.el.select('textarea.form-control',true).first();
13044 * Clear any invalid styles/messages for this field
13046 clearInvalid : function()
13049 if(!this.el || this.preventMark){ // not rendered
13053 var label = this.el.select('label', true).first();
13054 var icon = this.el.select('i.fa-star', true).first();
13059 this.el.removeClass( this.validClass);
13060 this.inputEl().removeClass('is-invalid');
13062 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13064 var feedback = this.el.select('.form-control-feedback', true).first();
13067 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13072 this.fireEvent('valid', this);
13076 * Mark this field as valid
13078 markValid : function()
13080 if(!this.el || this.preventMark){ // not rendered
13084 this.el.removeClass([this.invalidClass, this.validClass]);
13085 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13087 var feedback = this.el.select('.form-control-feedback', true).first();
13090 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13093 if(this.disabled || this.allowBlank){
13097 var label = this.el.select('label', true).first();
13098 var icon = this.el.select('i.fa-star', true).first();
13103 if (Roo.bootstrap.version == 3) {
13104 this.el.addClass(this.validClass);
13106 this.inputEl().addClass('is-valid');
13110 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13112 var feedback = this.el.select('.form-control-feedback', true).first();
13115 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13116 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13121 this.fireEvent('valid', this);
13125 * Mark this field as invalid
13126 * @param {String} msg The validation message
13128 markInvalid : function(msg)
13130 if(!this.el || this.preventMark){ // not rendered
13134 this.el.removeClass([this.invalidClass, this.validClass]);
13135 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13137 var feedback = this.el.select('.form-control-feedback', true).first();
13140 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13143 if(this.disabled || this.allowBlank){
13147 var label = this.el.select('label', true).first();
13148 var icon = this.el.select('i.fa-star', true).first();
13150 if(!this.getValue().length && label && !icon){
13151 this.el.createChild({
13153 cls : 'text-danger fa fa-lg fa-star',
13154 tooltip : 'This field is required',
13155 style : 'margin-right:5px;'
13159 if (Roo.bootstrap.version == 3) {
13160 this.el.addClass(this.invalidClass);
13162 this.inputEl().addClass('is-invalid');
13165 // fixme ... this may be depricated need to test..
13166 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13168 var feedback = this.el.select('.form-control-feedback', true).first();
13171 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13173 if(this.getValue().length || this.forceFeedback){
13174 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13181 this.fireEvent('invalid', this, msg);
13189 * trigger field - base class for combo..
13194 * @class Roo.bootstrap.TriggerField
13195 * @extends Roo.bootstrap.Input
13196 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13197 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13198 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13199 * for which you can provide a custom implementation. For example:
13201 var trigger = new Roo.bootstrap.TriggerField();
13202 trigger.onTriggerClick = myTriggerFn;
13203 trigger.applyTo('my-field');
13206 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13207 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
13208 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
13209 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13210 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13213 * Create a new TriggerField.
13214 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
13215 * to the base TextField)
13217 Roo.bootstrap.TriggerField = function(config){
13218 this.mimicing = false;
13219 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
13222 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
13224 * @cfg {String} triggerClass A CSS class to apply to the trigger
13227 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13232 * @cfg {Boolean} removable (true|false) special filter default false
13236 /** @cfg {Boolean} grow @hide */
13237 /** @cfg {Number} growMin @hide */
13238 /** @cfg {Number} growMax @hide */
13244 autoSize: Roo.emptyFn,
13248 deferHeight : true,
13251 actionMode : 'wrap',
13256 getAutoCreate : function(){
13258 var align = this.labelAlign || this.parentLabelAlign();
13263 cls: 'form-group' //input-group
13270 type : this.inputType,
13271 cls : 'form-control',
13272 autocomplete: 'new-password',
13273 placeholder : this.placeholder || ''
13277 input.name = this.name;
13280 input.cls += ' input-' + this.size;
13283 if (this.disabled) {
13284 input.disabled=true;
13287 var inputblock = input;
13289 if(this.hasFeedback && !this.allowBlank){
13293 cls: 'glyphicon form-control-feedback'
13296 if(this.removable && !this.editable ){
13298 cls : 'has-feedback',
13304 cls : 'roo-combo-removable-btn close'
13311 cls : 'has-feedback',
13320 if(this.removable && !this.editable ){
13322 cls : 'roo-removable',
13328 cls : 'roo-combo-removable-btn close'
13335 if (this.before || this.after) {
13338 cls : 'input-group',
13342 inputblock.cn.push({
13344 cls : 'input-group-addon input-group-prepend input-group-text',
13349 inputblock.cn.push(input);
13351 if(this.hasFeedback && !this.allowBlank){
13352 inputblock.cls += ' has-feedback';
13353 inputblock.cn.push(feedback);
13357 inputblock.cn.push({
13359 cls : 'input-group-addon input-group-append input-group-text',
13368 var ibwrap = inputblock;
13373 cls: 'roo-select2-choices',
13377 cls: 'roo-select2-search-field',
13389 cls: 'roo-select2-container input-group',
13394 cls: 'form-hidden-field'
13400 if(!this.multiple && this.showToggleBtn){
13406 if (this.caret != false) {
13409 cls: 'fa fa-' + this.caret
13416 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13418 Roo.bootstrap.version == 3 ? caret : '',
13421 cls: 'combobox-clear',
13435 combobox.cls += ' roo-select2-container-multi';
13439 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13440 tooltip : 'This field is required'
13442 if (Roo.bootstrap.version == 4) {
13445 style : 'display:none'
13450 if (align ==='left' && this.fieldLabel.length) {
13452 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
13459 cls : 'control-label',
13460 html : this.fieldLabel
13472 var labelCfg = cfg.cn[1];
13473 var contentCfg = cfg.cn[2];
13475 if(this.indicatorpos == 'right'){
13480 cls : 'control-label',
13484 html : this.fieldLabel
13498 labelCfg = cfg.cn[0];
13499 contentCfg = cfg.cn[1];
13502 if(this.labelWidth > 12){
13503 labelCfg.style = "width: " + this.labelWidth + 'px';
13506 if(this.labelWidth < 13 && this.labelmd == 0){
13507 this.labelmd = this.labelWidth;
13510 if(this.labellg > 0){
13511 labelCfg.cls += ' col-lg-' + this.labellg;
13512 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13515 if(this.labelmd > 0){
13516 labelCfg.cls += ' col-md-' + this.labelmd;
13517 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13520 if(this.labelsm > 0){
13521 labelCfg.cls += ' col-sm-' + this.labelsm;
13522 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13525 if(this.labelxs > 0){
13526 labelCfg.cls += ' col-xs-' + this.labelxs;
13527 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13530 } else if ( this.fieldLabel.length) {
13531 // Roo.log(" label");
13536 //cls : 'input-group-addon',
13537 html : this.fieldLabel
13545 if(this.indicatorpos == 'right'){
13553 html : this.fieldLabel
13567 // Roo.log(" no label && no align");
13574 ['xs','sm','md','lg'].map(function(size){
13575 if (settings[size]) {
13576 cfg.cls += ' col-' + size + '-' + settings[size];
13587 onResize : function(w, h){
13588 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
13589 // if(typeof w == 'number'){
13590 // var x = w - this.trigger.getWidth();
13591 // this.inputEl().setWidth(this.adjustWidth('input', x));
13592 // this.trigger.setStyle('left', x+'px');
13597 adjustSize : Roo.BoxComponent.prototype.adjustSize,
13600 getResizeEl : function(){
13601 return this.inputEl();
13605 getPositionEl : function(){
13606 return this.inputEl();
13610 alignErrorIcon : function(){
13611 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
13615 initEvents : function(){
13619 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
13620 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
13621 if(!this.multiple && this.showToggleBtn){
13622 this.trigger = this.el.select('span.dropdown-toggle',true).first();
13623 if(this.hideTrigger){
13624 this.trigger.setDisplayed(false);
13626 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
13630 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
13633 if(this.removable && !this.editable && !this.tickable){
13634 var close = this.closeTriggerEl();
13637 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13638 close.on('click', this.removeBtnClick, this, close);
13642 //this.trigger.addClassOnOver('x-form-trigger-over');
13643 //this.trigger.addClassOnClick('x-form-trigger-click');
13646 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
13650 closeTriggerEl : function()
13652 var close = this.el.select('.roo-combo-removable-btn', true).first();
13653 return close ? close : false;
13656 removeBtnClick : function(e, h, el)
13658 e.preventDefault();
13660 if(this.fireEvent("remove", this) !== false){
13662 this.fireEvent("afterremove", this)
13666 createList : function()
13668 this.list = Roo.get(document.body).createChild({
13669 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
13670 cls: 'typeahead typeahead-long dropdown-menu shadow',
13671 style: 'display:none'
13674 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
13679 initTrigger : function(){
13684 onDestroy : function(){
13686 this.trigger.removeAllListeners();
13687 // this.trigger.remove();
13690 // this.wrap.remove();
13692 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
13696 onFocus : function(){
13697 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
13699 if(!this.mimicing){
13700 this.wrap.addClass('x-trigger-wrap-focus');
13701 this.mimicing = true;
13702 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
13703 if(this.monitorTab){
13704 this.el.on("keydown", this.checkTab, this);
13711 checkTab : function(e){
13712 if(e.getKey() == e.TAB){
13713 this.triggerBlur();
13718 onBlur : function(){
13723 mimicBlur : function(e, t){
13725 if(!this.wrap.contains(t) && this.validateBlur()){
13726 this.triggerBlur();
13732 triggerBlur : function(){
13733 this.mimicing = false;
13734 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
13735 if(this.monitorTab){
13736 this.el.un("keydown", this.checkTab, this);
13738 //this.wrap.removeClass('x-trigger-wrap-focus');
13739 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
13743 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
13744 validateBlur : function(e, t){
13749 onDisable : function(){
13750 this.inputEl().dom.disabled = true;
13751 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
13753 // this.wrap.addClass('x-item-disabled');
13758 onEnable : function(){
13759 this.inputEl().dom.disabled = false;
13760 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
13762 // this.el.removeClass('x-item-disabled');
13767 onShow : function(){
13768 var ae = this.getActionEl();
13771 ae.dom.style.display = '';
13772 ae.dom.style.visibility = 'visible';
13778 onHide : function(){
13779 var ae = this.getActionEl();
13780 ae.dom.style.display = 'none';
13784 * The function that should handle the trigger's click event. This method does nothing by default until overridden
13785 * by an implementing function.
13787 * @param {EventObject} e
13789 onTriggerClick : Roo.emptyFn
13797 * @class Roo.bootstrap.CardUploader
13798 * @extends Roo.bootstrap.Button
13799 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
13800 * @cfg {Number} errorTimeout default 3000
13801 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
13802 * @cfg {Array} html The button text.
13806 * Create a new CardUploader
13807 * @param {Object} config The config object
13810 Roo.bootstrap.CardUploader = function(config){
13814 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
13817 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
13825 * When a image is clicked on - and needs to display a slideshow or similar..
13826 * @param {Roo.bootstrap.Card} this
13827 * @param {Object} The image information data
13833 * When a the download link is clicked
13834 * @param {Roo.bootstrap.Card} this
13835 * @param {Object} The image information data contains
13842 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
13845 errorTimeout : 3000,
13849 fileCollection : false,
13852 getAutoCreate : function()
13856 cls :'form-group' ,
13861 //cls : 'input-group-addon',
13862 html : this.fieldLabel
13870 value : this.value,
13871 cls : 'd-none form-control'
13876 multiple : 'multiple',
13878 cls : 'd-none roo-card-upload-selector'
13882 cls : 'roo-card-uploader-button-container w-100 mb-2'
13885 cls : 'card-columns roo-card-uploader-container'
13895 getChildContainer : function() /// what children are added to.
13897 return this.containerEl;
13900 getButtonContainer : function() /// what children are added to.
13902 return this.el.select(".roo-card-uploader-button-container").first();
13905 initEvents : function()
13908 Roo.bootstrap.Input.prototype.initEvents.call(this);
13912 xns: Roo.bootstrap,
13915 container_method : 'getButtonContainer' ,
13916 html : this.html, // fix changable?
13919 'click' : function(btn, e) {
13928 this.urlAPI = (window.createObjectURL && window) ||
13929 (window.URL && URL.revokeObjectURL && URL) ||
13930 (window.webkitURL && webkitURL);
13935 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
13937 this.selectorEl.on('change', this.onFileSelected, this);
13940 this.images.forEach(function(img) {
13943 this.images = false;
13945 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
13951 onClick : function(e)
13953 e.preventDefault();
13955 this.selectorEl.dom.click();
13959 onFileSelected : function(e)
13961 e.preventDefault();
13963 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
13967 Roo.each(this.selectorEl.dom.files, function(file){
13968 this.addFile(file);
13977 addFile : function(file)
13980 if(typeof(file) === 'string'){
13981 throw "Add file by name?"; // should not happen
13985 if(!file || !this.urlAPI){
13995 var url = _this.urlAPI.createObjectURL( file);
13998 id : Roo.bootstrap.CardUploader.ID--,
13999 is_uploaded : false,
14003 mimetype : file.type,
14011 * addCard - add an Attachment to the uploader
14012 * @param data - the data about the image to upload
14016 title : "Title of file",
14017 is_uploaded : false,
14018 src : "http://.....",
14019 srcfile : { the File upload object },
14020 mimetype : file.type,
14023 .. any other data...
14029 addCard : function (data)
14031 // hidden input element?
14032 // if the file is not an image...
14033 //then we need to use something other that and header_image
14038 xns : Roo.bootstrap,
14039 xtype : 'CardFooter',
14042 xns : Roo.bootstrap,
14048 xns : Roo.bootstrap,
14050 html : String.format("<small>{0}</small>", data.title),
14051 cls : 'col-10 text-left',
14056 click : function() {
14058 t.fireEvent( "download", t, data );
14064 xns : Roo.bootstrap,
14066 style: 'max-height: 28px; ',
14072 click : function() {
14073 t.removeCard(data.id)
14085 var cn = this.addxtype(
14088 xns : Roo.bootstrap,
14091 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14092 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
14093 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
14098 initEvents : function() {
14099 Roo.bootstrap.Card.prototype.initEvents.call(this);
14101 this.imgEl = this.el.select('.card-img-top').first();
14103 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14104 this.imgEl.set({ 'pointer' : 'cursor' });
14107 this.getCardFooter().addClass('p-1');
14114 // dont' really need ot update items.
14115 // this.items.push(cn);
14116 this.fileCollection.add(cn);
14118 if (!data.srcfile) {
14119 this.updateInput();
14124 var reader = new FileReader();
14125 reader.addEventListener("load", function() {
14126 data.srcdata = reader.result;
14129 reader.readAsDataURL(data.srcfile);
14134 removeCard : function(id)
14137 var card = this.fileCollection.get(id);
14138 card.data.is_deleted = 1;
14139 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14140 //this.fileCollection.remove(card);
14141 //this.items = this.items.filter(function(e) { return e != card });
14142 // dont' really need ot update items.
14143 card.el.dom.parentNode.removeChild(card.el.dom);
14144 this.updateInput();
14150 this.fileCollection.each(function(card) {
14151 if (card.el.dom && card.el.dom.parentNode) {
14152 card.el.dom.parentNode.removeChild(card.el.dom);
14155 this.fileCollection.clear();
14156 this.updateInput();
14159 updateInput : function()
14162 this.fileCollection.each(function(e) {
14166 this.inputEl().dom.value = JSON.stringify(data);
14176 Roo.bootstrap.CardUploader.ID = -1;/*
14178 * Ext JS Library 1.1.1
14179 * Copyright(c) 2006-2007, Ext JS, LLC.
14181 * Originally Released Under LGPL - original licence link has changed is not relivant.
14184 * <script type="text/javascript">
14189 * @class Roo.data.SortTypes
14191 * Defines the default sorting (casting?) comparison functions used when sorting data.
14193 Roo.data.SortTypes = {
14195 * Default sort that does nothing
14196 * @param {Mixed} s The value being converted
14197 * @return {Mixed} The comparison value
14199 none : function(s){
14204 * The regular expression used to strip tags
14208 stripTagsRE : /<\/?[^>]+>/gi,
14211 * Strips all HTML tags to sort on text only
14212 * @param {Mixed} s The value being converted
14213 * @return {String} The comparison value
14215 asText : function(s){
14216 return String(s).replace(this.stripTagsRE, "");
14220 * Strips all HTML tags to sort on text only - Case insensitive
14221 * @param {Mixed} s The value being converted
14222 * @return {String} The comparison value
14224 asUCText : function(s){
14225 return String(s).toUpperCase().replace(this.stripTagsRE, "");
14229 * Case insensitive string
14230 * @param {Mixed} s The value being converted
14231 * @return {String} The comparison value
14233 asUCString : function(s) {
14234 return String(s).toUpperCase();
14239 * @param {Mixed} s The value being converted
14240 * @return {Number} The comparison value
14242 asDate : function(s) {
14246 if(s instanceof Date){
14247 return s.getTime();
14249 return Date.parse(String(s));
14254 * @param {Mixed} s The value being converted
14255 * @return {Float} The comparison value
14257 asFloat : function(s) {
14258 var val = parseFloat(String(s).replace(/,/g, ""));
14267 * @param {Mixed} s The value being converted
14268 * @return {Number} The comparison value
14270 asInt : function(s) {
14271 var val = parseInt(String(s).replace(/,/g, ""));
14279 * Ext JS Library 1.1.1
14280 * Copyright(c) 2006-2007, Ext JS, LLC.
14282 * Originally Released Under LGPL - original licence link has changed is not relivant.
14285 * <script type="text/javascript">
14289 * @class Roo.data.Record
14290 * Instances of this class encapsulate both record <em>definition</em> information, and record
14291 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14292 * to access Records cached in an {@link Roo.data.Store} object.<br>
14294 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14295 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14298 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14300 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14301 * {@link #create}. The parameters are the same.
14302 * @param {Array} data An associative Array of data values keyed by the field name.
14303 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14304 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14305 * not specified an integer id is generated.
14307 Roo.data.Record = function(data, id){
14308 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14313 * Generate a constructor for a specific record layout.
14314 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14315 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14316 * Each field definition object may contain the following properties: <ul>
14317 * <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,
14318 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14319 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14320 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14321 * is being used, then this is a string containing the javascript expression to reference the data relative to
14322 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14323 * to the data item relative to the record element. If the mapping expression is the same as the field name,
14324 * this may be omitted.</p></li>
14325 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14326 * <ul><li>auto (Default, implies no conversion)</li>
14331 * <li>date</li></ul></p></li>
14332 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14333 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14334 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14335 * by the Reader into an object that will be stored in the Record. It is passed the
14336 * following parameters:<ul>
14337 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14339 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14341 * <br>usage:<br><pre><code>
14342 var TopicRecord = Roo.data.Record.create(
14343 {name: 'title', mapping: 'topic_title'},
14344 {name: 'author', mapping: 'username'},
14345 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14346 {name: 'lastPost', mapping: 'post_time', type: 'date'},
14347 {name: 'lastPoster', mapping: 'user2'},
14348 {name: 'excerpt', mapping: 'post_text'}
14351 var myNewRecord = new TopicRecord({
14352 title: 'Do my job please',
14355 lastPost: new Date(),
14356 lastPoster: 'Animal',
14357 excerpt: 'No way dude!'
14359 myStore.add(myNewRecord);
14364 Roo.data.Record.create = function(o){
14365 var f = function(){
14366 f.superclass.constructor.apply(this, arguments);
14368 Roo.extend(f, Roo.data.Record);
14369 var p = f.prototype;
14370 p.fields = new Roo.util.MixedCollection(false, function(field){
14373 for(var i = 0, len = o.length; i < len; i++){
14374 p.fields.add(new Roo.data.Field(o[i]));
14376 f.getField = function(name){
14377 return p.fields.get(name);
14382 Roo.data.Record.AUTO_ID = 1000;
14383 Roo.data.Record.EDIT = 'edit';
14384 Roo.data.Record.REJECT = 'reject';
14385 Roo.data.Record.COMMIT = 'commit';
14387 Roo.data.Record.prototype = {
14389 * Readonly flag - true if this record has been modified.
14398 join : function(store){
14399 this.store = store;
14403 * Set the named field to the specified value.
14404 * @param {String} name The name of the field to set.
14405 * @param {Object} value The value to set the field to.
14407 set : function(name, value){
14408 if(this.data[name] == value){
14412 if(!this.modified){
14413 this.modified = {};
14415 if(typeof this.modified[name] == 'undefined'){
14416 this.modified[name] = this.data[name];
14418 this.data[name] = value;
14419 if(!this.editing && this.store){
14420 this.store.afterEdit(this);
14425 * Get the value of the named field.
14426 * @param {String} name The name of the field to get the value of.
14427 * @return {Object} The value of the field.
14429 get : function(name){
14430 return this.data[name];
14434 beginEdit : function(){
14435 this.editing = true;
14436 this.modified = {};
14440 cancelEdit : function(){
14441 this.editing = false;
14442 delete this.modified;
14446 endEdit : function(){
14447 this.editing = false;
14448 if(this.dirty && this.store){
14449 this.store.afterEdit(this);
14454 * Usually called by the {@link Roo.data.Store} which owns the Record.
14455 * Rejects all changes made to the Record since either creation, or the last commit operation.
14456 * Modified fields are reverted to their original values.
14458 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14459 * of reject operations.
14461 reject : function(){
14462 var m = this.modified;
14464 if(typeof m[n] != "function"){
14465 this.data[n] = m[n];
14468 this.dirty = false;
14469 delete this.modified;
14470 this.editing = false;
14472 this.store.afterReject(this);
14477 * Usually called by the {@link Roo.data.Store} which owns the Record.
14478 * Commits all changes made to the Record since either creation, or the last commit operation.
14480 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14481 * of commit operations.
14483 commit : function(){
14484 this.dirty = false;
14485 delete this.modified;
14486 this.editing = false;
14488 this.store.afterCommit(this);
14493 hasError : function(){
14494 return this.error != null;
14498 clearError : function(){
14503 * Creates a copy of this record.
14504 * @param {String} id (optional) A new record id if you don't want to use this record's id
14507 copy : function(newId) {
14508 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
14512 * Ext JS Library 1.1.1
14513 * Copyright(c) 2006-2007, Ext JS, LLC.
14515 * Originally Released Under LGPL - original licence link has changed is not relivant.
14518 * <script type="text/javascript">
14524 * @class Roo.data.Store
14525 * @extends Roo.util.Observable
14526 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
14527 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
14529 * 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
14530 * has no knowledge of the format of the data returned by the Proxy.<br>
14532 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
14533 * instances from the data object. These records are cached and made available through accessor functions.
14535 * Creates a new Store.
14536 * @param {Object} config A config object containing the objects needed for the Store to access data,
14537 * and read the data into Records.
14539 Roo.data.Store = function(config){
14540 this.data = new Roo.util.MixedCollection(false);
14541 this.data.getKey = function(o){
14544 this.baseParams = {};
14546 this.paramNames = {
14551 "multisort" : "_multisort"
14554 if(config && config.data){
14555 this.inlineData = config.data;
14556 delete config.data;
14559 Roo.apply(this, config);
14561 if(this.reader){ // reader passed
14562 this.reader = Roo.factory(this.reader, Roo.data);
14563 this.reader.xmodule = this.xmodule || false;
14564 if(!this.recordType){
14565 this.recordType = this.reader.recordType;
14567 if(this.reader.onMetaChange){
14568 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
14572 if(this.recordType){
14573 this.fields = this.recordType.prototype.fields;
14575 this.modified = [];
14579 * @event datachanged
14580 * Fires when the data cache has changed, and a widget which is using this Store
14581 * as a Record cache should refresh its view.
14582 * @param {Store} this
14584 datachanged : true,
14586 * @event metachange
14587 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
14588 * @param {Store} this
14589 * @param {Object} meta The JSON metadata
14594 * Fires when Records have been added to the Store
14595 * @param {Store} this
14596 * @param {Roo.data.Record[]} records The array of Records added
14597 * @param {Number} index The index at which the record(s) were added
14602 * Fires when a Record has been removed from the Store
14603 * @param {Store} this
14604 * @param {Roo.data.Record} record The Record that was removed
14605 * @param {Number} index The index at which the record was removed
14610 * Fires when a Record has been updated
14611 * @param {Store} this
14612 * @param {Roo.data.Record} record The Record that was updated
14613 * @param {String} operation The update operation being performed. Value may be one of:
14615 Roo.data.Record.EDIT
14616 Roo.data.Record.REJECT
14617 Roo.data.Record.COMMIT
14623 * Fires when the data cache has been cleared.
14624 * @param {Store} this
14628 * @event beforeload
14629 * Fires before a request is made for a new data object. If the beforeload handler returns false
14630 * the load action will be canceled.
14631 * @param {Store} this
14632 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14636 * @event beforeloadadd
14637 * Fires after a new set of Records has been loaded.
14638 * @param {Store} this
14639 * @param {Roo.data.Record[]} records The Records that were loaded
14640 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14642 beforeloadadd : true,
14645 * Fires after a new set of Records has been loaded, before they are added to the store.
14646 * @param {Store} this
14647 * @param {Roo.data.Record[]} records The Records that were loaded
14648 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14649 * @params {Object} return from reader
14653 * @event loadexception
14654 * Fires if an exception occurs in the Proxy during loading.
14655 * Called with the signature of the Proxy's "loadexception" event.
14656 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
14659 * @param {Object} return from JsonData.reader() - success, totalRecords, records
14660 * @param {Object} load options
14661 * @param {Object} jsonData from your request (normally this contains the Exception)
14663 loadexception : true
14667 this.proxy = Roo.factory(this.proxy, Roo.data);
14668 this.proxy.xmodule = this.xmodule || false;
14669 this.relayEvents(this.proxy, ["loadexception"]);
14671 this.sortToggle = {};
14672 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
14674 Roo.data.Store.superclass.constructor.call(this);
14676 if(this.inlineData){
14677 this.loadData(this.inlineData);
14678 delete this.inlineData;
14682 Roo.extend(Roo.data.Store, Roo.util.Observable, {
14684 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
14685 * without a remote query - used by combo/forms at present.
14689 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
14692 * @cfg {Array} data Inline data to be loaded when the store is initialized.
14695 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
14696 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
14699 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
14700 * on any HTTP request
14703 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
14706 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
14710 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
14711 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
14713 remoteSort : false,
14716 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
14717 * loaded or when a record is removed. (defaults to false).
14719 pruneModifiedRecords : false,
14722 lastOptions : null,
14725 * Add Records to the Store and fires the add event.
14726 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14728 add : function(records){
14729 records = [].concat(records);
14730 for(var i = 0, len = records.length; i < len; i++){
14731 records[i].join(this);
14733 var index = this.data.length;
14734 this.data.addAll(records);
14735 this.fireEvent("add", this, records, index);
14739 * Remove a Record from the Store and fires the remove event.
14740 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
14742 remove : function(record){
14743 var index = this.data.indexOf(record);
14744 this.data.removeAt(index);
14746 if(this.pruneModifiedRecords){
14747 this.modified.remove(record);
14749 this.fireEvent("remove", this, record, index);
14753 * Remove all Records from the Store and fires the clear event.
14755 removeAll : function(){
14757 if(this.pruneModifiedRecords){
14758 this.modified = [];
14760 this.fireEvent("clear", this);
14764 * Inserts Records to the Store at the given index and fires the add event.
14765 * @param {Number} index The start index at which to insert the passed Records.
14766 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14768 insert : function(index, records){
14769 records = [].concat(records);
14770 for(var i = 0, len = records.length; i < len; i++){
14771 this.data.insert(index, records[i]);
14772 records[i].join(this);
14774 this.fireEvent("add", this, records, index);
14778 * Get the index within the cache of the passed Record.
14779 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
14780 * @return {Number} The index of the passed Record. Returns -1 if not found.
14782 indexOf : function(record){
14783 return this.data.indexOf(record);
14787 * Get the index within the cache of the Record with the passed id.
14788 * @param {String} id The id of the Record to find.
14789 * @return {Number} The index of the Record. Returns -1 if not found.
14791 indexOfId : function(id){
14792 return this.data.indexOfKey(id);
14796 * Get the Record with the specified id.
14797 * @param {String} id The id of the Record to find.
14798 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
14800 getById : function(id){
14801 return this.data.key(id);
14805 * Get the Record at the specified index.
14806 * @param {Number} index The index of the Record to find.
14807 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
14809 getAt : function(index){
14810 return this.data.itemAt(index);
14814 * Returns a range of Records between specified indices.
14815 * @param {Number} startIndex (optional) The starting index (defaults to 0)
14816 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
14817 * @return {Roo.data.Record[]} An array of Records
14819 getRange : function(start, end){
14820 return this.data.getRange(start, end);
14824 storeOptions : function(o){
14825 o = Roo.apply({}, o);
14828 this.lastOptions = o;
14832 * Loads the Record cache from the configured Proxy using the configured Reader.
14834 * If using remote paging, then the first load call must specify the <em>start</em>
14835 * and <em>limit</em> properties in the options.params property to establish the initial
14836 * position within the dataset, and the number of Records to cache on each read from the Proxy.
14838 * <strong>It is important to note that for remote data sources, loading is asynchronous,
14839 * and this call will return before the new data has been loaded. Perform any post-processing
14840 * in a callback function, or in a "load" event handler.</strong>
14842 * @param {Object} options An object containing properties which control loading options:<ul>
14843 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
14844 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
14845 * passed the following arguments:<ul>
14846 * <li>r : Roo.data.Record[]</li>
14847 * <li>options: Options object from the load call</li>
14848 * <li>success: Boolean success indicator</li></ul></li>
14849 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
14850 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
14853 load : function(options){
14854 options = options || {};
14855 if(this.fireEvent("beforeload", this, options) !== false){
14856 this.storeOptions(options);
14857 var p = Roo.apply(options.params || {}, this.baseParams);
14858 // if meta was not loaded from remote source.. try requesting it.
14859 if (!this.reader.metaFromRemote) {
14860 p._requestMeta = 1;
14862 if(this.sortInfo && this.remoteSort){
14863 var pn = this.paramNames;
14864 p[pn["sort"]] = this.sortInfo.field;
14865 p[pn["dir"]] = this.sortInfo.direction;
14867 if (this.multiSort) {
14868 var pn = this.paramNames;
14869 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
14872 this.proxy.load(p, this.reader, this.loadRecords, this, options);
14877 * Reloads the Record cache from the configured Proxy using the configured Reader and
14878 * the options from the last load operation performed.
14879 * @param {Object} options (optional) An object containing properties which may override the options
14880 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
14881 * the most recently used options are reused).
14883 reload : function(options){
14884 this.load(Roo.applyIf(options||{}, this.lastOptions));
14888 // Called as a callback by the Reader during a load operation.
14889 loadRecords : function(o, options, success){
14890 if(!o || success === false){
14891 if(success !== false){
14892 this.fireEvent("load", this, [], options, o);
14894 if(options.callback){
14895 options.callback.call(options.scope || this, [], options, false);
14899 // if data returned failure - throw an exception.
14900 if (o.success === false) {
14901 // show a message if no listener is registered.
14902 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
14903 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
14905 // loadmask wil be hooked into this..
14906 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
14909 var r = o.records, t = o.totalRecords || r.length;
14911 this.fireEvent("beforeloadadd", this, r, options, o);
14913 if(!options || options.add !== true){
14914 if(this.pruneModifiedRecords){
14915 this.modified = [];
14917 for(var i = 0, len = r.length; i < len; i++){
14921 this.data = this.snapshot;
14922 delete this.snapshot;
14925 this.data.addAll(r);
14926 this.totalLength = t;
14928 this.fireEvent("datachanged", this);
14930 this.totalLength = Math.max(t, this.data.length+r.length);
14934 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
14936 var e = new Roo.data.Record({});
14938 e.set(this.parent.displayField, this.parent.emptyTitle);
14939 e.set(this.parent.valueField, '');
14944 this.fireEvent("load", this, r, options, o);
14945 if(options.callback){
14946 options.callback.call(options.scope || this, r, options, true);
14952 * Loads data from a passed data block. A Reader which understands the format of the data
14953 * must have been configured in the constructor.
14954 * @param {Object} data The data block from which to read the Records. The format of the data expected
14955 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
14956 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
14958 loadData : function(o, append){
14959 var r = this.reader.readRecords(o);
14960 this.loadRecords(r, {add: append}, true);
14964 * using 'cn' the nested child reader read the child array into it's child stores.
14965 * @param {Object} rec The record with a 'children array
14967 loadDataFromChildren : function(rec)
14969 this.loadData(this.reader.toLoadData(rec));
14974 * Gets the number of cached records.
14976 * <em>If using paging, this may not be the total size of the dataset. If the data object
14977 * used by the Reader contains the dataset size, then the getTotalCount() function returns
14978 * the data set size</em>
14980 getCount : function(){
14981 return this.data.length || 0;
14985 * Gets the total number of records in the dataset as returned by the server.
14987 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
14988 * the dataset size</em>
14990 getTotalCount : function(){
14991 return this.totalLength || 0;
14995 * Returns the sort state of the Store as an object with two properties:
14997 field {String} The name of the field by which the Records are sorted
14998 direction {String} The sort order, "ASC" or "DESC"
15001 getSortState : function(){
15002 return this.sortInfo;
15006 applySort : function(){
15007 if(this.sortInfo && !this.remoteSort){
15008 var s = this.sortInfo, f = s.field;
15009 var st = this.fields.get(f).sortType;
15010 var fn = function(r1, r2){
15011 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15012 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15014 this.data.sort(s.direction, fn);
15015 if(this.snapshot && this.snapshot != this.data){
15016 this.snapshot.sort(s.direction, fn);
15022 * Sets the default sort column and order to be used by the next load operation.
15023 * @param {String} fieldName The name of the field to sort by.
15024 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15026 setDefaultSort : function(field, dir){
15027 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15031 * Sort the Records.
15032 * If remote sorting is used, the sort is performed on the server, and the cache is
15033 * reloaded. If local sorting is used, the cache is sorted internally.
15034 * @param {String} fieldName The name of the field to sort by.
15035 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15037 sort : function(fieldName, dir){
15038 var f = this.fields.get(fieldName);
15040 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15042 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15043 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15048 this.sortToggle[f.name] = dir;
15049 this.sortInfo = {field: f.name, direction: dir};
15050 if(!this.remoteSort){
15052 this.fireEvent("datachanged", this);
15054 this.load(this.lastOptions);
15059 * Calls the specified function for each of the Records in the cache.
15060 * @param {Function} fn The function to call. The Record is passed as the first parameter.
15061 * Returning <em>false</em> aborts and exits the iteration.
15062 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15064 each : function(fn, scope){
15065 this.data.each(fn, scope);
15069 * Gets all records modified since the last commit. Modified records are persisted across load operations
15070 * (e.g., during paging).
15071 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15073 getModifiedRecords : function(){
15074 return this.modified;
15078 createFilterFn : function(property, value, anyMatch){
15079 if(!value.exec){ // not a regex
15080 value = String(value);
15081 if(value.length == 0){
15084 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15086 return function(r){
15087 return value.test(r.data[property]);
15092 * Sums the value of <i>property</i> for each record between start and end and returns the result.
15093 * @param {String} property A field on your records
15094 * @param {Number} start The record index to start at (defaults to 0)
15095 * @param {Number} end The last record index to include (defaults to length - 1)
15096 * @return {Number} The sum
15098 sum : function(property, start, end){
15099 var rs = this.data.items, v = 0;
15100 start = start || 0;
15101 end = (end || end === 0) ? end : rs.length-1;
15103 for(var i = start; i <= end; i++){
15104 v += (rs[i].data[property] || 0);
15110 * Filter the records by a specified property.
15111 * @param {String} field A field on your records
15112 * @param {String/RegExp} value Either a string that the field
15113 * should start with or a RegExp to test against the field
15114 * @param {Boolean} anyMatch True to match any part not just the beginning
15116 filter : function(property, value, anyMatch){
15117 var fn = this.createFilterFn(property, value, anyMatch);
15118 return fn ? this.filterBy(fn) : this.clearFilter();
15122 * Filter by a function. The specified function will be called with each
15123 * record in this data source. If the function returns true the record is included,
15124 * otherwise it is filtered.
15125 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15126 * @param {Object} scope (optional) The scope of the function (defaults to this)
15128 filterBy : function(fn, scope){
15129 this.snapshot = this.snapshot || this.data;
15130 this.data = this.queryBy(fn, scope||this);
15131 this.fireEvent("datachanged", this);
15135 * Query the records by a specified property.
15136 * @param {String} field A field on your records
15137 * @param {String/RegExp} value Either a string that the field
15138 * should start with or a RegExp to test against the field
15139 * @param {Boolean} anyMatch True to match any part not just the beginning
15140 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15142 query : function(property, value, anyMatch){
15143 var fn = this.createFilterFn(property, value, anyMatch);
15144 return fn ? this.queryBy(fn) : this.data.clone();
15148 * Query by a function. The specified function will be called with each
15149 * record in this data source. If the function returns true the record is included
15151 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15152 * @param {Object} scope (optional) The scope of the function (defaults to this)
15153 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15155 queryBy : function(fn, scope){
15156 var data = this.snapshot || this.data;
15157 return data.filterBy(fn, scope||this);
15161 * Collects unique values for a particular dataIndex from this store.
15162 * @param {String} dataIndex The property to collect
15163 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15164 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15165 * @return {Array} An array of the unique values
15167 collect : function(dataIndex, allowNull, bypassFilter){
15168 var d = (bypassFilter === true && this.snapshot) ?
15169 this.snapshot.items : this.data.items;
15170 var v, sv, r = [], l = {};
15171 for(var i = 0, len = d.length; i < len; i++){
15172 v = d[i].data[dataIndex];
15174 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15183 * Revert to a view of the Record cache with no filtering applied.
15184 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15186 clearFilter : function(suppressEvent){
15187 if(this.snapshot && this.snapshot != this.data){
15188 this.data = this.snapshot;
15189 delete this.snapshot;
15190 if(suppressEvent !== true){
15191 this.fireEvent("datachanged", this);
15197 afterEdit : function(record){
15198 if(this.modified.indexOf(record) == -1){
15199 this.modified.push(record);
15201 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15205 afterReject : function(record){
15206 this.modified.remove(record);
15207 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15211 afterCommit : function(record){
15212 this.modified.remove(record);
15213 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15217 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15218 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15220 commitChanges : function(){
15221 var m = this.modified.slice(0);
15222 this.modified = [];
15223 for(var i = 0, len = m.length; i < len; i++){
15229 * Cancel outstanding changes on all changed records.
15231 rejectChanges : function(){
15232 var m = this.modified.slice(0);
15233 this.modified = [];
15234 for(var i = 0, len = m.length; i < len; i++){
15239 onMetaChange : function(meta, rtype, o){
15240 this.recordType = rtype;
15241 this.fields = rtype.prototype.fields;
15242 delete this.snapshot;
15243 this.sortInfo = meta.sortInfo || this.sortInfo;
15244 this.modified = [];
15245 this.fireEvent('metachange', this, this.reader.meta);
15248 moveIndex : function(data, type)
15250 var index = this.indexOf(data);
15252 var newIndex = index + type;
15256 this.insert(newIndex, data);
15261 * Ext JS Library 1.1.1
15262 * Copyright(c) 2006-2007, Ext JS, LLC.
15264 * Originally Released Under LGPL - original licence link has changed is not relivant.
15267 * <script type="text/javascript">
15271 * @class Roo.data.SimpleStore
15272 * @extends Roo.data.Store
15273 * Small helper class to make creating Stores from Array data easier.
15274 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15275 * @cfg {Array} fields An array of field definition objects, or field name strings.
15276 * @cfg {Object} an existing reader (eg. copied from another store)
15277 * @cfg {Array} data The multi-dimensional array of data
15279 * @param {Object} config
15281 Roo.data.SimpleStore = function(config)
15283 Roo.data.SimpleStore.superclass.constructor.call(this, {
15285 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15288 Roo.data.Record.create(config.fields)
15290 proxy : new Roo.data.MemoryProxy(config.data)
15294 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15296 * Ext JS Library 1.1.1
15297 * Copyright(c) 2006-2007, Ext JS, LLC.
15299 * Originally Released Under LGPL - original licence link has changed is not relivant.
15302 * <script type="text/javascript">
15307 * @extends Roo.data.Store
15308 * @class Roo.data.JsonStore
15309 * Small helper class to make creating Stores for JSON data easier. <br/>
15311 var store = new Roo.data.JsonStore({
15312 url: 'get-images.php',
15314 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15317 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15318 * JsonReader and HttpProxy (unless inline data is provided).</b>
15319 * @cfg {Array} fields An array of field definition objects, or field name strings.
15321 * @param {Object} config
15323 Roo.data.JsonStore = function(c){
15324 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15325 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15326 reader: new Roo.data.JsonReader(c, c.fields)
15329 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15331 * Ext JS Library 1.1.1
15332 * Copyright(c) 2006-2007, Ext JS, LLC.
15334 * Originally Released Under LGPL - original licence link has changed is not relivant.
15337 * <script type="text/javascript">
15341 Roo.data.Field = function(config){
15342 if(typeof config == "string"){
15343 config = {name: config};
15345 Roo.apply(this, config);
15348 this.type = "auto";
15351 var st = Roo.data.SortTypes;
15352 // named sortTypes are supported, here we look them up
15353 if(typeof this.sortType == "string"){
15354 this.sortType = st[this.sortType];
15357 // set default sortType for strings and dates
15358 if(!this.sortType){
15361 this.sortType = st.asUCString;
15364 this.sortType = st.asDate;
15367 this.sortType = st.none;
15372 var stripRe = /[\$,%]/g;
15374 // prebuilt conversion function for this field, instead of
15375 // switching every time we're reading a value
15377 var cv, dateFormat = this.dateFormat;
15382 cv = function(v){ return v; };
15385 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15389 return v !== undefined && v !== null && v !== '' ?
15390 parseInt(String(v).replace(stripRe, ""), 10) : '';
15395 return v !== undefined && v !== null && v !== '' ?
15396 parseFloat(String(v).replace(stripRe, ""), 10) : '';
15401 cv = function(v){ return v === true || v === "true" || v == 1; };
15408 if(v instanceof Date){
15412 if(dateFormat == "timestamp"){
15413 return new Date(v*1000);
15415 return Date.parseDate(v, dateFormat);
15417 var parsed = Date.parse(v);
15418 return parsed ? new Date(parsed) : null;
15427 Roo.data.Field.prototype = {
15435 * Ext JS Library 1.1.1
15436 * Copyright(c) 2006-2007, Ext JS, LLC.
15438 * Originally Released Under LGPL - original licence link has changed is not relivant.
15441 * <script type="text/javascript">
15444 // Base class for reading structured data from a data source. This class is intended to be
15445 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15448 * @class Roo.data.DataReader
15449 * Base class for reading structured data from a data source. This class is intended to be
15450 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15453 Roo.data.DataReader = function(meta, recordType){
15457 this.recordType = recordType instanceof Array ?
15458 Roo.data.Record.create(recordType) : recordType;
15461 Roo.data.DataReader.prototype = {
15464 readerType : 'Data',
15466 * Create an empty record
15467 * @param {Object} data (optional) - overlay some values
15468 * @return {Roo.data.Record} record created.
15470 newRow : function(d) {
15472 this.recordType.prototype.fields.each(function(c) {
15474 case 'int' : da[c.name] = 0; break;
15475 case 'date' : da[c.name] = new Date(); break;
15476 case 'float' : da[c.name] = 0.0; break;
15477 case 'boolean' : da[c.name] = false; break;
15478 default : da[c.name] = ""; break;
15482 return new this.recordType(Roo.apply(da, d));
15488 * Ext JS Library 1.1.1
15489 * Copyright(c) 2006-2007, Ext JS, LLC.
15491 * Originally Released Under LGPL - original licence link has changed is not relivant.
15494 * <script type="text/javascript">
15498 * @class Roo.data.DataProxy
15499 * @extends Roo.data.Observable
15500 * This class is an abstract base class for implementations which provide retrieval of
15501 * unformatted data objects.<br>
15503 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
15504 * (of the appropriate type which knows how to parse the data object) to provide a block of
15505 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
15507 * Custom implementations must implement the load method as described in
15508 * {@link Roo.data.HttpProxy#load}.
15510 Roo.data.DataProxy = function(){
15513 * @event beforeload
15514 * Fires before a network request is made to retrieve a data object.
15515 * @param {Object} This DataProxy object.
15516 * @param {Object} params The params parameter to the load function.
15521 * Fires before the load method's callback is called.
15522 * @param {Object} This DataProxy object.
15523 * @param {Object} o The data object.
15524 * @param {Object} arg The callback argument object passed to the load function.
15528 * @event loadexception
15529 * Fires if an Exception occurs during data retrieval.
15530 * @param {Object} This DataProxy object.
15531 * @param {Object} o The data object.
15532 * @param {Object} arg The callback argument object passed to the load function.
15533 * @param {Object} e The Exception.
15535 loadexception : true
15537 Roo.data.DataProxy.superclass.constructor.call(this);
15540 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
15543 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
15547 * Ext JS Library 1.1.1
15548 * Copyright(c) 2006-2007, Ext JS, LLC.
15550 * Originally Released Under LGPL - original licence link has changed is not relivant.
15553 * <script type="text/javascript">
15556 * @class Roo.data.MemoryProxy
15557 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
15558 * to the Reader when its load method is called.
15560 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
15562 Roo.data.MemoryProxy = function(data){
15566 Roo.data.MemoryProxy.superclass.constructor.call(this);
15570 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
15573 * Load data from the requested source (in this case an in-memory
15574 * data object passed to the constructor), read the data object into
15575 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15576 * process that block using the passed callback.
15577 * @param {Object} params This parameter is not used by the MemoryProxy class.
15578 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15579 * object into a block of Roo.data.Records.
15580 * @param {Function} callback The function into which to pass the block of Roo.data.records.
15581 * The function must be passed <ul>
15582 * <li>The Record block object</li>
15583 * <li>The "arg" argument from the load function</li>
15584 * <li>A boolean success indicator</li>
15586 * @param {Object} scope The scope in which to call the callback
15587 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15589 load : function(params, reader, callback, scope, arg){
15590 params = params || {};
15593 result = reader.readRecords(params.data ? params.data :this.data);
15595 this.fireEvent("loadexception", this, arg, null, e);
15596 callback.call(scope, null, arg, false);
15599 callback.call(scope, result, arg, true);
15603 update : function(params, records){
15608 * Ext JS Library 1.1.1
15609 * Copyright(c) 2006-2007, Ext JS, LLC.
15611 * Originally Released Under LGPL - original licence link has changed is not relivant.
15614 * <script type="text/javascript">
15617 * @class Roo.data.HttpProxy
15618 * @extends Roo.data.DataProxy
15619 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
15620 * configured to reference a certain URL.<br><br>
15622 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
15623 * from which the running page was served.<br><br>
15625 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
15627 * Be aware that to enable the browser to parse an XML document, the server must set
15628 * the Content-Type header in the HTTP response to "text/xml".
15630 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
15631 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
15632 * will be used to make the request.
15634 Roo.data.HttpProxy = function(conn){
15635 Roo.data.HttpProxy.superclass.constructor.call(this);
15636 // is conn a conn config or a real conn?
15638 this.useAjax = !conn || !conn.events;
15642 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
15643 // thse are take from connection...
15646 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
15649 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
15650 * extra parameters to each request made by this object. (defaults to undefined)
15653 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
15654 * to each request made by this object. (defaults to undefined)
15657 * @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)
15660 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
15663 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
15669 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
15673 * Return the {@link Roo.data.Connection} object being used by this Proxy.
15674 * @return {Connection} The Connection object. This object may be used to subscribe to events on
15675 * a finer-grained basis than the DataProxy events.
15677 getConnection : function(){
15678 return this.useAjax ? Roo.Ajax : this.conn;
15682 * Load data from the configured {@link Roo.data.Connection}, read the data object into
15683 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
15684 * process that block using the passed callback.
15685 * @param {Object} params An object containing properties which are to be used as HTTP parameters
15686 * for the request to the remote server.
15687 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15688 * object into a block of Roo.data.Records.
15689 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15690 * The function must be passed <ul>
15691 * <li>The Record block object</li>
15692 * <li>The "arg" argument from the load function</li>
15693 * <li>A boolean success indicator</li>
15695 * @param {Object} scope The scope in which to call the callback
15696 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15698 load : function(params, reader, callback, scope, arg){
15699 if(this.fireEvent("beforeload", this, params) !== false){
15701 params : params || {},
15703 callback : callback,
15708 callback : this.loadResponse,
15712 Roo.applyIf(o, this.conn);
15713 if(this.activeRequest){
15714 Roo.Ajax.abort(this.activeRequest);
15716 this.activeRequest = Roo.Ajax.request(o);
15718 this.conn.request(o);
15721 callback.call(scope||this, null, arg, false);
15726 loadResponse : function(o, success, response){
15727 delete this.activeRequest;
15729 this.fireEvent("loadexception", this, o, response);
15730 o.request.callback.call(o.request.scope, null, o.request.arg, false);
15735 result = o.reader.read(response);
15737 this.fireEvent("loadexception", this, o, response, e);
15738 o.request.callback.call(o.request.scope, null, o.request.arg, false);
15742 this.fireEvent("load", this, o, o.request.arg);
15743 o.request.callback.call(o.request.scope, result, o.request.arg, true);
15747 update : function(dataSet){
15752 updateResponse : function(dataSet){
15757 * Ext JS Library 1.1.1
15758 * Copyright(c) 2006-2007, Ext JS, LLC.
15760 * Originally Released Under LGPL - original licence link has changed is not relivant.
15763 * <script type="text/javascript">
15767 * @class Roo.data.ScriptTagProxy
15768 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
15769 * other than the originating domain of the running page.<br><br>
15771 * <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
15772 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
15774 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
15775 * source code that is used as the source inside a <script> tag.<br><br>
15777 * In order for the browser to process the returned data, the server must wrap the data object
15778 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
15779 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
15780 * depending on whether the callback name was passed:
15783 boolean scriptTag = false;
15784 String cb = request.getParameter("callback");
15787 response.setContentType("text/javascript");
15789 response.setContentType("application/x-json");
15791 Writer out = response.getWriter();
15793 out.write(cb + "(");
15795 out.print(dataBlock.toJsonString());
15802 * @param {Object} config A configuration object.
15804 Roo.data.ScriptTagProxy = function(config){
15805 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
15806 Roo.apply(this, config);
15807 this.head = document.getElementsByTagName("head")[0];
15810 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
15812 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
15814 * @cfg {String} url The URL from which to request the data object.
15817 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
15821 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
15822 * the server the name of the callback function set up by the load call to process the returned data object.
15823 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
15824 * javascript output which calls this named function passing the data object as its only parameter.
15826 callbackParam : "callback",
15828 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
15829 * name to the request.
15834 * Load data from the configured URL, read the data object into
15835 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15836 * process that block using the passed callback.
15837 * @param {Object} params An object containing properties which are to be used as HTTP parameters
15838 * for the request to the remote server.
15839 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15840 * object into a block of Roo.data.Records.
15841 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15842 * The function must be passed <ul>
15843 * <li>The Record block object</li>
15844 * <li>The "arg" argument from the load function</li>
15845 * <li>A boolean success indicator</li>
15847 * @param {Object} scope The scope in which to call the callback
15848 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15850 load : function(params, reader, callback, scope, arg){
15851 if(this.fireEvent("beforeload", this, params) !== false){
15853 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
15855 var url = this.url;
15856 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
15858 url += "&_dc=" + (new Date().getTime());
15860 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
15863 cb : "stcCallback"+transId,
15864 scriptId : "stcScript"+transId,
15868 callback : callback,
15874 window[trans.cb] = function(o){
15875 conn.handleResponse(o, trans);
15878 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
15880 if(this.autoAbort !== false){
15884 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
15886 var script = document.createElement("script");
15887 script.setAttribute("src", url);
15888 script.setAttribute("type", "text/javascript");
15889 script.setAttribute("id", trans.scriptId);
15890 this.head.appendChild(script);
15892 this.trans = trans;
15894 callback.call(scope||this, null, arg, false);
15899 isLoading : function(){
15900 return this.trans ? true : false;
15904 * Abort the current server request.
15906 abort : function(){
15907 if(this.isLoading()){
15908 this.destroyTrans(this.trans);
15913 destroyTrans : function(trans, isLoaded){
15914 this.head.removeChild(document.getElementById(trans.scriptId));
15915 clearTimeout(trans.timeoutId);
15917 window[trans.cb] = undefined;
15919 delete window[trans.cb];
15922 // if hasn't been loaded, wait for load to remove it to prevent script error
15923 window[trans.cb] = function(){
15924 window[trans.cb] = undefined;
15926 delete window[trans.cb];
15933 handleResponse : function(o, trans){
15934 this.trans = false;
15935 this.destroyTrans(trans, true);
15938 result = trans.reader.readRecords(o);
15940 this.fireEvent("loadexception", this, o, trans.arg, e);
15941 trans.callback.call(trans.scope||window, null, trans.arg, false);
15944 this.fireEvent("load", this, o, trans.arg);
15945 trans.callback.call(trans.scope||window, result, trans.arg, true);
15949 handleFailure : function(trans){
15950 this.trans = false;
15951 this.destroyTrans(trans, false);
15952 this.fireEvent("loadexception", this, null, trans.arg);
15953 trans.callback.call(trans.scope||window, null, trans.arg, false);
15957 * Ext JS Library 1.1.1
15958 * Copyright(c) 2006-2007, Ext JS, LLC.
15960 * Originally Released Under LGPL - original licence link has changed is not relivant.
15963 * <script type="text/javascript">
15967 * @class Roo.data.JsonReader
15968 * @extends Roo.data.DataReader
15969 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
15970 * based on mappings in a provided Roo.data.Record constructor.
15972 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
15973 * in the reply previously.
15978 var RecordDef = Roo.data.Record.create([
15979 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
15980 {name: 'occupation'} // This field will use "occupation" as the mapping.
15982 var myReader = new Roo.data.JsonReader({
15983 totalProperty: "results", // The property which contains the total dataset size (optional)
15984 root: "rows", // The property which contains an Array of row objects
15985 id: "id" // The property within each row object that provides an ID for the record (optional)
15989 * This would consume a JSON file like this:
15991 { 'results': 2, 'rows': [
15992 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
15993 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
15996 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
15997 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
15998 * paged from the remote server.
15999 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16000 * @cfg {String} root name of the property which contains the Array of row objects.
16001 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16002 * @cfg {Array} fields Array of field definition objects
16004 * Create a new JsonReader
16005 * @param {Object} meta Metadata configuration options
16006 * @param {Object} recordType Either an Array of field definition objects,
16007 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16009 Roo.data.JsonReader = function(meta, recordType){
16012 // set some defaults:
16013 Roo.applyIf(meta, {
16014 totalProperty: 'total',
16015 successProperty : 'success',
16020 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16022 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16024 readerType : 'Json',
16027 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
16028 * Used by Store query builder to append _requestMeta to params.
16031 metaFromRemote : false,
16033 * This method is only used by a DataProxy which has retrieved data from a remote server.
16034 * @param {Object} response The XHR object which contains the JSON data in its responseText.
16035 * @return {Object} data A data block which is used by an Roo.data.Store object as
16036 * a cache of Roo.data.Records.
16038 read : function(response){
16039 var json = response.responseText;
16041 var o = /* eval:var:o */ eval("("+json+")");
16043 throw {message: "JsonReader.read: Json object not found"};
16049 this.metaFromRemote = true;
16050 this.meta = o.metaData;
16051 this.recordType = Roo.data.Record.create(o.metaData.fields);
16052 this.onMetaChange(this.meta, this.recordType, o);
16054 return this.readRecords(o);
16057 // private function a store will implement
16058 onMetaChange : function(meta, recordType, o){
16065 simpleAccess: function(obj, subsc) {
16072 getJsonAccessor: function(){
16074 return function(expr) {
16076 return(re.test(expr))
16077 ? new Function("obj", "return obj." + expr)
16082 return Roo.emptyFn;
16087 * Create a data block containing Roo.data.Records from an XML document.
16088 * @param {Object} o An object which contains an Array of row objects in the property specified
16089 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16090 * which contains the total size of the dataset.
16091 * @return {Object} data A data block which is used by an Roo.data.Store object as
16092 * a cache of Roo.data.Records.
16094 readRecords : function(o){
16096 * After any data loads, the raw JSON data is available for further custom processing.
16100 var s = this.meta, Record = this.recordType,
16101 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16103 // Generate extraction functions for the totalProperty, the root, the id, and for each field
16105 if(s.totalProperty) {
16106 this.getTotal = this.getJsonAccessor(s.totalProperty);
16108 if(s.successProperty) {
16109 this.getSuccess = this.getJsonAccessor(s.successProperty);
16111 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16113 var g = this.getJsonAccessor(s.id);
16114 this.getId = function(rec) {
16116 return (r === undefined || r === "") ? null : r;
16119 this.getId = function(){return null;};
16122 for(var jj = 0; jj < fl; jj++){
16124 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16125 this.ef[jj] = this.getJsonAccessor(map);
16129 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16130 if(s.totalProperty){
16131 var vt = parseInt(this.getTotal(o), 10);
16136 if(s.successProperty){
16137 var vs = this.getSuccess(o);
16138 if(vs === false || vs === 'false'){
16143 for(var i = 0; i < c; i++){
16146 var id = this.getId(n);
16147 for(var j = 0; j < fl; j++){
16149 var v = this.ef[j](n);
16151 Roo.log('missing convert for ' + f.name);
16155 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16157 var record = new Record(values, id);
16159 records[i] = record;
16165 totalRecords : totalRecords
16168 // used when loading children.. @see loadDataFromChildren
16169 toLoadData: function(rec)
16171 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16172 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16173 return { data : data, total : data.length };
16178 * Ext JS Library 1.1.1
16179 * Copyright(c) 2006-2007, Ext JS, LLC.
16181 * Originally Released Under LGPL - original licence link has changed is not relivant.
16184 * <script type="text/javascript">
16188 * @class Roo.data.ArrayReader
16189 * @extends Roo.data.DataReader
16190 * Data reader class to create an Array of Roo.data.Record objects from an Array.
16191 * Each element of that Array represents a row of data fields. The
16192 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16193 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16197 var RecordDef = Roo.data.Record.create([
16198 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
16199 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
16201 var myReader = new Roo.data.ArrayReader({
16202 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
16206 * This would consume an Array like this:
16208 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16212 * Create a new JsonReader
16213 * @param {Object} meta Metadata configuration options.
16214 * @param {Object|Array} recordType Either an Array of field definition objects
16216 * @cfg {Array} fields Array of field definition objects
16217 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16218 * as specified to {@link Roo.data.Record#create},
16219 * or an {@link Roo.data.Record} object
16222 * created using {@link Roo.data.Record#create}.
16224 Roo.data.ArrayReader = function(meta, recordType)
16226 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16229 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16232 * Create a data block containing Roo.data.Records from an XML document.
16233 * @param {Object} o An Array of row objects which represents the dataset.
16234 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16235 * a cache of Roo.data.Records.
16237 readRecords : function(o)
16239 var sid = this.meta ? this.meta.id : null;
16240 var recordType = this.recordType, fields = recordType.prototype.fields;
16243 for(var i = 0; i < root.length; i++){
16246 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16247 for(var j = 0, jlen = fields.length; j < jlen; j++){
16248 var f = fields.items[j];
16249 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16250 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16252 values[f.name] = v;
16254 var record = new recordType(values, id);
16256 records[records.length] = record;
16260 totalRecords : records.length
16263 // used when loading children.. @see loadDataFromChildren
16264 toLoadData: function(rec)
16266 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16267 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16278 * @class Roo.bootstrap.ComboBox
16279 * @extends Roo.bootstrap.TriggerField
16280 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16281 * @cfg {Boolean} append (true|false) default false
16282 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16283 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16284 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16285 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16286 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16287 * @cfg {Boolean} animate default true
16288 * @cfg {Boolean} emptyResultText only for touch device
16289 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16290 * @cfg {String} emptyTitle default ''
16291 * @cfg {Number} width fixed with? experimental
16293 * Create a new ComboBox.
16294 * @param {Object} config Configuration options
16296 Roo.bootstrap.ComboBox = function(config){
16297 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
16301 * Fires when the dropdown list is expanded
16302 * @param {Roo.bootstrap.ComboBox} combo This combo box
16307 * Fires when the dropdown list is collapsed
16308 * @param {Roo.bootstrap.ComboBox} combo This combo box
16312 * @event beforeselect
16313 * Fires before a list item is selected. Return false to cancel the selection.
16314 * @param {Roo.bootstrap.ComboBox} combo This combo box
16315 * @param {Roo.data.Record} record The data record returned from the underlying store
16316 * @param {Number} index The index of the selected item in the dropdown list
16318 'beforeselect' : true,
16321 * Fires when a list item is selected
16322 * @param {Roo.bootstrap.ComboBox} combo This combo box
16323 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16324 * @param {Number} index The index of the selected item in the dropdown list
16328 * @event beforequery
16329 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16330 * The event object passed has these properties:
16331 * @param {Roo.bootstrap.ComboBox} combo This combo box
16332 * @param {String} query The query
16333 * @param {Boolean} forceAll true to force "all" query
16334 * @param {Boolean} cancel true to cancel the query
16335 * @param {Object} e The query event object
16337 'beforequery': true,
16340 * Fires when the 'add' icon is pressed (add a listener to enable add button)
16341 * @param {Roo.bootstrap.ComboBox} combo This combo box
16346 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16347 * @param {Roo.bootstrap.ComboBox} combo This combo box
16348 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16353 * Fires when the remove value from the combobox array
16354 * @param {Roo.bootstrap.ComboBox} combo This combo box
16358 * @event afterremove
16359 * Fires when the remove value from the combobox array
16360 * @param {Roo.bootstrap.ComboBox} combo This combo box
16362 'afterremove' : true,
16364 * @event specialfilter
16365 * Fires when specialfilter
16366 * @param {Roo.bootstrap.ComboBox} combo This combo box
16368 'specialfilter' : true,
16371 * Fires when tick the element
16372 * @param {Roo.bootstrap.ComboBox} combo This combo box
16376 * @event touchviewdisplay
16377 * Fires when touch view require special display (default is using displayField)
16378 * @param {Roo.bootstrap.ComboBox} combo This combo box
16379 * @param {Object} cfg set html .
16381 'touchviewdisplay' : true
16386 this.tickItems = [];
16388 this.selectedIndex = -1;
16389 if(this.mode == 'local'){
16390 if(config.queryDelay === undefined){
16391 this.queryDelay = 10;
16393 if(config.minChars === undefined){
16399 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
16402 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16403 * rendering into an Roo.Editor, defaults to false)
16406 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16407 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16410 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16413 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16414 * the dropdown list (defaults to undefined, with no header element)
16418 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
16422 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16424 listWidth: undefined,
16426 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16427 * mode = 'remote' or 'text' if mode = 'local')
16429 displayField: undefined,
16432 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16433 * mode = 'remote' or 'value' if mode = 'local').
16434 * Note: use of a valueField requires the user make a selection
16435 * in order for a value to be mapped.
16437 valueField: undefined,
16439 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16444 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16445 * field's data value (defaults to the underlying DOM element's name)
16447 hiddenName: undefined,
16449 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16453 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16455 selectedClass: 'active',
16458 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16462 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16463 * anchor positions (defaults to 'tl-bl')
16465 listAlign: 'tl-bl?',
16467 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16471 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
16472 * query specified by the allQuery config option (defaults to 'query')
16474 triggerAction: 'query',
16476 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16477 * (defaults to 4, does not apply if editable = false)
16481 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16482 * delay (typeAheadDelay) if it matches a known value (defaults to false)
16486 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
16487 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
16491 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
16492 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
16496 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
16497 * when editable = true (defaults to false)
16499 selectOnFocus:false,
16501 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
16503 queryParam: 'query',
16505 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
16506 * when mode = 'remote' (defaults to 'Loading...')
16508 loadingText: 'Loading...',
16510 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
16514 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
16518 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
16519 * traditional select (defaults to true)
16523 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
16527 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
16531 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
16532 * listWidth has a higher value)
16536 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
16537 * allow the user to set arbitrary text into the field (defaults to false)
16539 forceSelection:false,
16541 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
16542 * if typeAhead = true (defaults to 250)
16544 typeAheadDelay : 250,
16546 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
16547 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
16549 valueNotFoundText : undefined,
16551 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
16553 blockFocus : false,
16556 * @cfg {Boolean} disableClear Disable showing of clear button.
16558 disableClear : false,
16560 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
16562 alwaysQuery : false,
16565 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
16570 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
16572 invalidClass : "has-warning",
16575 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
16577 validClass : "has-success",
16580 * @cfg {Boolean} specialFilter (true|false) special filter default false
16582 specialFilter : false,
16585 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
16587 mobileTouchView : true,
16590 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
16592 useNativeIOS : false,
16595 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
16597 mobile_restrict_height : false,
16599 ios_options : false,
16611 btnPosition : 'right',
16612 triggerList : true,
16613 showToggleBtn : true,
16615 emptyResultText: 'Empty',
16616 triggerText : 'Select',
16620 // element that contains real text value.. (when hidden is used..)
16622 getAutoCreate : function()
16627 * Render classic select for iso
16630 if(Roo.isIOS && this.useNativeIOS){
16631 cfg = this.getAutoCreateNativeIOS();
16639 if(Roo.isTouch && this.mobileTouchView){
16640 cfg = this.getAutoCreateTouchView();
16647 if(!this.tickable){
16648 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
16653 * ComboBox with tickable selections
16656 var align = this.labelAlign || this.parentLabelAlign();
16659 cls : 'form-group roo-combobox-tickable' //input-group
16662 var btn_text_select = '';
16663 var btn_text_done = '';
16664 var btn_text_cancel = '';
16666 if (this.btn_text_show) {
16667 btn_text_select = 'Select';
16668 btn_text_done = 'Done';
16669 btn_text_cancel = 'Cancel';
16674 cls : 'tickable-buttons',
16679 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
16680 //html : this.triggerText
16681 html: btn_text_select
16687 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
16689 html: btn_text_done
16695 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
16697 html: btn_text_cancel
16703 buttons.cn.unshift({
16705 cls: 'roo-select2-search-field-input'
16711 Roo.each(buttons.cn, function(c){
16713 c.cls += ' btn-' + _this.size;
16716 if (_this.disabled) {
16723 style : 'display: contents',
16728 cls: 'form-hidden-field'
16732 cls: 'roo-select2-choices',
16736 cls: 'roo-select2-search-field',
16747 cls: 'roo-select2-container input-group roo-select2-container-multi',
16753 // cls: 'typeahead typeahead-long dropdown-menu',
16754 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
16759 if(this.hasFeedback && !this.allowBlank){
16763 cls: 'glyphicon form-control-feedback'
16766 combobox.cn.push(feedback);
16773 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
16774 tooltip : 'This field is required'
16776 if (Roo.bootstrap.version == 4) {
16779 style : 'display:none'
16782 if (align ==='left' && this.fieldLabel.length) {
16784 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
16791 cls : 'control-label col-form-label',
16792 html : this.fieldLabel
16804 var labelCfg = cfg.cn[1];
16805 var contentCfg = cfg.cn[2];
16808 if(this.indicatorpos == 'right'){
16814 cls : 'control-label col-form-label',
16818 html : this.fieldLabel
16834 labelCfg = cfg.cn[0];
16835 contentCfg = cfg.cn[1];
16839 if(this.labelWidth > 12){
16840 labelCfg.style = "width: " + this.labelWidth + 'px';
16842 if(this.width * 1 > 0){
16843 contentCfg.style = "width: " + this.width + 'px';
16845 if(this.labelWidth < 13 && this.labelmd == 0){
16846 this.labelmd = this.labelWidth;
16849 if(this.labellg > 0){
16850 labelCfg.cls += ' col-lg-' + this.labellg;
16851 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
16854 if(this.labelmd > 0){
16855 labelCfg.cls += ' col-md-' + this.labelmd;
16856 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
16859 if(this.labelsm > 0){
16860 labelCfg.cls += ' col-sm-' + this.labelsm;
16861 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
16864 if(this.labelxs > 0){
16865 labelCfg.cls += ' col-xs-' + this.labelxs;
16866 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
16870 } else if ( this.fieldLabel.length) {
16871 // Roo.log(" label");
16876 //cls : 'input-group-addon',
16877 html : this.fieldLabel
16882 if(this.indicatorpos == 'right'){
16886 //cls : 'input-group-addon',
16887 html : this.fieldLabel
16897 // Roo.log(" no label && no align");
16904 ['xs','sm','md','lg'].map(function(size){
16905 if (settings[size]) {
16906 cfg.cls += ' col-' + size + '-' + settings[size];
16914 _initEventsCalled : false,
16917 initEvents: function()
16919 if (this._initEventsCalled) { // as we call render... prevent looping...
16922 this._initEventsCalled = true;
16925 throw "can not find store for combo";
16928 this.indicator = this.indicatorEl();
16930 this.store = Roo.factory(this.store, Roo.data);
16931 this.store.parent = this;
16933 // if we are building from html. then this element is so complex, that we can not really
16934 // use the rendered HTML.
16935 // so we have to trash and replace the previous code.
16936 if (Roo.XComponent.build_from_html) {
16937 // remove this element....
16938 var e = this.el.dom, k=0;
16939 while (e ) { e = e.previousSibling; ++k;}
16944 this.rendered = false;
16946 this.render(this.parent().getChildContainer(true), k);
16949 if(Roo.isIOS && this.useNativeIOS){
16950 this.initIOSView();
16958 if(Roo.isTouch && this.mobileTouchView){
16959 this.initTouchView();
16964 this.initTickableEvents();
16968 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
16970 if(this.hiddenName){
16972 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16974 this.hiddenField.dom.value =
16975 this.hiddenValue !== undefined ? this.hiddenValue :
16976 this.value !== undefined ? this.value : '';
16978 // prevent input submission
16979 this.el.dom.removeAttribute('name');
16980 this.hiddenField.dom.setAttribute('name', this.hiddenName);
16985 // this.el.dom.setAttribute('autocomplete', 'off');
16988 var cls = 'x-combo-list';
16990 //this.list = new Roo.Layer({
16991 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
16997 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16998 _this.list.setWidth(lw);
17001 this.list.on('mouseover', this.onViewOver, this);
17002 this.list.on('mousemove', this.onViewMove, this);
17003 this.list.on('scroll', this.onViewScroll, this);
17006 this.list.swallowEvent('mousewheel');
17007 this.assetHeight = 0;
17010 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17011 this.assetHeight += this.header.getHeight();
17014 this.innerList = this.list.createChild({cls:cls+'-inner'});
17015 this.innerList.on('mouseover', this.onViewOver, this);
17016 this.innerList.on('mousemove', this.onViewMove, this);
17017 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17019 if(this.allowBlank && !this.pageSize && !this.disableClear){
17020 this.footer = this.list.createChild({cls:cls+'-ft'});
17021 this.pageTb = new Roo.Toolbar(this.footer);
17025 this.footer = this.list.createChild({cls:cls+'-ft'});
17026 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17027 {pageSize: this.pageSize});
17031 if (this.pageTb && this.allowBlank && !this.disableClear) {
17033 this.pageTb.add(new Roo.Toolbar.Fill(), {
17034 cls: 'x-btn-icon x-btn-clear',
17036 handler: function()
17039 _this.clearValue();
17040 _this.onSelect(false, -1);
17045 this.assetHeight += this.footer.getHeight();
17050 this.tpl = Roo.bootstrap.version == 4 ?
17051 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
17052 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17055 this.view = new Roo.View(this.list, this.tpl, {
17056 singleSelect:true, store: this.store, selectedClass: this.selectedClass
17058 //this.view.wrapEl.setDisplayed(false);
17059 this.view.on('click', this.onViewClick, this);
17062 this.store.on('beforeload', this.onBeforeLoad, this);
17063 this.store.on('load', this.onLoad, this);
17064 this.store.on('loadexception', this.onLoadException, this);
17066 if(this.resizable){
17067 this.resizer = new Roo.Resizable(this.list, {
17068 pinned:true, handles:'se'
17070 this.resizer.on('resize', function(r, w, h){
17071 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17072 this.listWidth = w;
17073 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17074 this.restrictHeight();
17076 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17079 if(!this.editable){
17080 this.editable = true;
17081 this.setEditable(false);
17086 if (typeof(this.events.add.listeners) != 'undefined') {
17088 this.addicon = this.wrap.createChild(
17089 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
17091 this.addicon.on('click', function(e) {
17092 this.fireEvent('add', this);
17095 if (typeof(this.events.edit.listeners) != 'undefined') {
17097 this.editicon = this.wrap.createChild(
17098 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
17099 if (this.addicon) {
17100 this.editicon.setStyle('margin-left', '40px');
17102 this.editicon.on('click', function(e) {
17104 // we fire even if inothing is selected..
17105 this.fireEvent('edit', this, this.lastData );
17111 this.keyNav = new Roo.KeyNav(this.inputEl(), {
17112 "up" : function(e){
17113 this.inKeyMode = true;
17117 "down" : function(e){
17118 if(!this.isExpanded()){
17119 this.onTriggerClick();
17121 this.inKeyMode = true;
17126 "enter" : function(e){
17127 // this.onViewClick();
17131 if(this.fireEvent("specialkey", this, e)){
17132 this.onViewClick(false);
17138 "esc" : function(e){
17142 "tab" : function(e){
17145 if(this.fireEvent("specialkey", this, e)){
17146 this.onViewClick(false);
17154 doRelay : function(foo, bar, hname){
17155 if(hname == 'down' || this.scope.isExpanded()){
17156 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17165 this.queryDelay = Math.max(this.queryDelay || 10,
17166 this.mode == 'local' ? 10 : 250);
17169 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17171 if(this.typeAhead){
17172 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17174 if(this.editable !== false){
17175 this.inputEl().on("keyup", this.onKeyUp, this);
17177 if(this.forceSelection){
17178 this.inputEl().on('blur', this.doForce, this);
17182 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17183 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17187 initTickableEvents: function()
17191 if(this.hiddenName){
17193 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17195 this.hiddenField.dom.value =
17196 this.hiddenValue !== undefined ? this.hiddenValue :
17197 this.value !== undefined ? this.value : '';
17199 // prevent input submission
17200 this.el.dom.removeAttribute('name');
17201 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17206 // this.list = this.el.select('ul.dropdown-menu',true).first();
17208 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17209 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17210 if(this.triggerList){
17211 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17214 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17215 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17217 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17218 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17220 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17221 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17223 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17224 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17225 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17228 this.cancelBtn.hide();
17233 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17234 _this.list.setWidth(lw);
17237 this.list.on('mouseover', this.onViewOver, this);
17238 this.list.on('mousemove', this.onViewMove, this);
17240 this.list.on('scroll', this.onViewScroll, this);
17243 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
17244 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17247 this.view = new Roo.View(this.list, this.tpl, {
17252 selectedClass: this.selectedClass
17255 //this.view.wrapEl.setDisplayed(false);
17256 this.view.on('click', this.onViewClick, this);
17260 this.store.on('beforeload', this.onBeforeLoad, this);
17261 this.store.on('load', this.onLoad, this);
17262 this.store.on('loadexception', this.onLoadException, this);
17265 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17266 "up" : function(e){
17267 this.inKeyMode = true;
17271 "down" : function(e){
17272 this.inKeyMode = true;
17276 "enter" : function(e){
17277 if(this.fireEvent("specialkey", this, e)){
17278 this.onViewClick(false);
17284 "esc" : function(e){
17285 this.onTickableFooterButtonClick(e, false, false);
17288 "tab" : function(e){
17289 this.fireEvent("specialkey", this, e);
17291 this.onTickableFooterButtonClick(e, false, false);
17298 doRelay : function(e, fn, key){
17299 if(this.scope.isExpanded()){
17300 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17309 this.queryDelay = Math.max(this.queryDelay || 10,
17310 this.mode == 'local' ? 10 : 250);
17313 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17315 if(this.typeAhead){
17316 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17319 if(this.editable !== false){
17320 this.tickableInputEl().on("keyup", this.onKeyUp, this);
17323 this.indicator = this.indicatorEl();
17325 if(this.indicator){
17326 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17327 this.indicator.hide();
17332 onDestroy : function(){
17334 this.view.setStore(null);
17335 this.view.el.removeAllListeners();
17336 this.view.el.remove();
17337 this.view.purgeListeners();
17340 this.list.dom.innerHTML = '';
17344 this.store.un('beforeload', this.onBeforeLoad, this);
17345 this.store.un('load', this.onLoad, this);
17346 this.store.un('loadexception', this.onLoadException, this);
17348 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
17352 fireKey : function(e){
17353 if(e.isNavKeyPress() && !this.list.isVisible()){
17354 this.fireEvent("specialkey", this, e);
17359 onResize: function(w, h)
17363 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
17365 // if(typeof w != 'number'){
17366 // // we do not handle it!?!?
17369 // var tw = this.trigger.getWidth();
17370 // // tw += this.addicon ? this.addicon.getWidth() : 0;
17371 // // tw += this.editicon ? this.editicon.getWidth() : 0;
17373 // this.inputEl().setWidth( this.adjustWidth('input', x));
17375 // //this.trigger.setStyle('left', x+'px');
17377 // if(this.list && this.listWidth === undefined){
17378 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17379 // this.list.setWidth(lw);
17380 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17388 * Allow or prevent the user from directly editing the field text. If false is passed,
17389 * the user will only be able to select from the items defined in the dropdown list. This method
17390 * is the runtime equivalent of setting the 'editable' config option at config time.
17391 * @param {Boolean} value True to allow the user to directly edit the field text
17393 setEditable : function(value){
17394 if(value == this.editable){
17397 this.editable = value;
17399 this.inputEl().dom.setAttribute('readOnly', true);
17400 this.inputEl().on('mousedown', this.onTriggerClick, this);
17401 this.inputEl().addClass('x-combo-noedit');
17403 this.inputEl().dom.removeAttribute('readOnly');
17404 this.inputEl().un('mousedown', this.onTriggerClick, this);
17405 this.inputEl().removeClass('x-combo-noedit');
17411 onBeforeLoad : function(combo,opts){
17412 if(!this.hasFocus){
17416 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17418 this.restrictHeight();
17419 this.selectedIndex = -1;
17423 onLoad : function(){
17425 this.hasQuery = false;
17427 if(!this.hasFocus){
17431 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17432 this.loading.hide();
17435 if(this.store.getCount() > 0){
17438 this.restrictHeight();
17439 if(this.lastQuery == this.allQuery){
17440 if(this.editable && !this.tickable){
17441 this.inputEl().dom.select();
17445 !this.selectByValue(this.value, true) &&
17448 !this.store.lastOptions ||
17449 typeof(this.store.lastOptions.add) == 'undefined' ||
17450 this.store.lastOptions.add != true
17453 this.select(0, true);
17456 if(this.autoFocus){
17459 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17460 this.taTask.delay(this.typeAheadDelay);
17464 this.onEmptyResults();
17470 onLoadException : function()
17472 this.hasQuery = false;
17474 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17475 this.loading.hide();
17478 if(this.tickable && this.editable){
17483 // only causes errors at present
17484 //Roo.log(this.store.reader.jsonData);
17485 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
17487 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
17493 onTypeAhead : function(){
17494 if(this.store.getCount() > 0){
17495 var r = this.store.getAt(0);
17496 var newValue = r.data[this.displayField];
17497 var len = newValue.length;
17498 var selStart = this.getRawValue().length;
17500 if(selStart != len){
17501 this.setRawValue(newValue);
17502 this.selectText(selStart, newValue.length);
17508 onSelect : function(record, index){
17510 if(this.fireEvent('beforeselect', this, record, index) !== false){
17512 this.setFromData(index > -1 ? record.data : false);
17515 this.fireEvent('select', this, record, index);
17520 * Returns the currently selected field value or empty string if no value is set.
17521 * @return {String} value The selected value
17523 getValue : function()
17525 if(Roo.isIOS && this.useNativeIOS){
17526 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
17530 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
17533 if(this.valueField){
17534 return typeof this.value != 'undefined' ? this.value : '';
17536 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
17540 getRawValue : function()
17542 if(Roo.isIOS && this.useNativeIOS){
17543 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
17546 var v = this.inputEl().getValue();
17552 * Clears any text/value currently set in the field
17554 clearValue : function(){
17556 if(this.hiddenField){
17557 this.hiddenField.dom.value = '';
17560 this.setRawValue('');
17561 this.lastSelectionText = '';
17562 this.lastData = false;
17564 var close = this.closeTriggerEl();
17575 * Sets the specified value into the field. If the value finds a match, the corresponding record text
17576 * will be displayed in the field. If the value does not match the data value of an existing item,
17577 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
17578 * Otherwise the field will be blank (although the value will still be set).
17579 * @param {String} value The value to match
17581 setValue : function(v)
17583 if(Roo.isIOS && this.useNativeIOS){
17584 this.setIOSValue(v);
17594 if(this.valueField){
17595 var r = this.findRecord(this.valueField, v);
17597 text = r.data[this.displayField];
17598 }else if(this.valueNotFoundText !== undefined){
17599 text = this.valueNotFoundText;
17602 this.lastSelectionText = text;
17603 if(this.hiddenField){
17604 this.hiddenField.dom.value = v;
17606 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
17609 var close = this.closeTriggerEl();
17612 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
17618 * @property {Object} the last set data for the element
17623 * Sets the value of the field based on a object which is related to the record format for the store.
17624 * @param {Object} value the value to set as. or false on reset?
17626 setFromData : function(o){
17633 var dv = ''; // display value
17634 var vv = ''; // value value..
17636 if (this.displayField) {
17637 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17639 // this is an error condition!!!
17640 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
17643 if(this.valueField){
17644 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
17647 var close = this.closeTriggerEl();
17650 if(dv.length || vv * 1 > 0){
17652 this.blockFocus=true;
17658 if(this.hiddenField){
17659 this.hiddenField.dom.value = vv;
17661 this.lastSelectionText = dv;
17662 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17666 // no hidden field.. - we store the value in 'value', but still display
17667 // display field!!!!
17668 this.lastSelectionText = dv;
17669 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17676 reset : function(){
17677 // overridden so that last data is reset..
17684 this.setValue(this.originalValue);
17685 //this.clearInvalid();
17686 this.lastData = false;
17688 this.view.clearSelections();
17694 findRecord : function(prop, value){
17696 if(this.store.getCount() > 0){
17697 this.store.each(function(r){
17698 if(r.data[prop] == value){
17708 getName: function()
17710 // returns hidden if it's set..
17711 if (!this.rendered) {return ''};
17712 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
17716 onViewMove : function(e, t){
17717 this.inKeyMode = false;
17721 onViewOver : function(e, t){
17722 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
17725 var item = this.view.findItemFromChild(t);
17728 var index = this.view.indexOf(item);
17729 this.select(index, false);
17734 onViewClick : function(view, doFocus, el, e)
17736 var index = this.view.getSelectedIndexes()[0];
17738 var r = this.store.getAt(index);
17742 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
17749 Roo.each(this.tickItems, function(v,k){
17751 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
17753 _this.tickItems.splice(k, 1);
17755 if(typeof(e) == 'undefined' && view == false){
17756 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
17768 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
17769 this.tickItems.push(r.data);
17772 if(typeof(e) == 'undefined' && view == false){
17773 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
17780 this.onSelect(r, index);
17782 if(doFocus !== false && !this.blockFocus){
17783 this.inputEl().focus();
17788 restrictHeight : function(){
17789 //this.innerList.dom.style.height = '';
17790 //var inner = this.innerList.dom;
17791 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
17792 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
17793 //this.list.beginUpdate();
17794 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
17795 this.list.alignTo(this.inputEl(), this.listAlign);
17796 this.list.alignTo(this.inputEl(), this.listAlign);
17797 //this.list.endUpdate();
17801 onEmptyResults : function(){
17803 if(this.tickable && this.editable){
17804 this.hasFocus = false;
17805 this.restrictHeight();
17813 * Returns true if the dropdown list is expanded, else false.
17815 isExpanded : function(){
17816 return this.list.isVisible();
17820 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
17821 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17822 * @param {String} value The data value of the item to select
17823 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17824 * selected item if it is not currently in view (defaults to true)
17825 * @return {Boolean} True if the value matched an item in the list, else false
17827 selectByValue : function(v, scrollIntoView){
17828 if(v !== undefined && v !== null){
17829 var r = this.findRecord(this.valueField || this.displayField, v);
17831 this.select(this.store.indexOf(r), scrollIntoView);
17839 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
17840 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17841 * @param {Number} index The zero-based index of the list item to select
17842 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17843 * selected item if it is not currently in view (defaults to true)
17845 select : function(index, scrollIntoView){
17846 this.selectedIndex = index;
17847 this.view.select(index);
17848 if(scrollIntoView !== false){
17849 var el = this.view.getNode(index);
17851 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
17854 this.list.scrollChildIntoView(el, false);
17860 selectNext : function(){
17861 var ct = this.store.getCount();
17863 if(this.selectedIndex == -1){
17865 }else if(this.selectedIndex < ct-1){
17866 this.select(this.selectedIndex+1);
17872 selectPrev : function(){
17873 var ct = this.store.getCount();
17875 if(this.selectedIndex == -1){
17877 }else if(this.selectedIndex != 0){
17878 this.select(this.selectedIndex-1);
17884 onKeyUp : function(e){
17885 if(this.editable !== false && !e.isSpecialKey()){
17886 this.lastKey = e.getKey();
17887 this.dqTask.delay(this.queryDelay);
17892 validateBlur : function(){
17893 return !this.list || !this.list.isVisible();
17897 initQuery : function(){
17899 var v = this.getRawValue();
17901 if(this.tickable && this.editable){
17902 v = this.tickableInputEl().getValue();
17909 doForce : function(){
17910 if(this.inputEl().dom.value.length > 0){
17911 this.inputEl().dom.value =
17912 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
17918 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
17919 * query allowing the query action to be canceled if needed.
17920 * @param {String} query The SQL query to execute
17921 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
17922 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
17923 * saved in the current store (defaults to false)
17925 doQuery : function(q, forceAll){
17927 if(q === undefined || q === null){
17932 forceAll: forceAll,
17936 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
17941 forceAll = qe.forceAll;
17942 if(forceAll === true || (q.length >= this.minChars)){
17944 this.hasQuery = true;
17946 if(this.lastQuery != q || this.alwaysQuery){
17947 this.lastQuery = q;
17948 if(this.mode == 'local'){
17949 this.selectedIndex = -1;
17951 this.store.clearFilter();
17954 if(this.specialFilter){
17955 this.fireEvent('specialfilter', this);
17960 this.store.filter(this.displayField, q);
17963 this.store.fireEvent("datachanged", this.store);
17970 this.store.baseParams[this.queryParam] = q;
17972 var options = {params : this.getParams(q)};
17975 options.add = true;
17976 options.params.start = this.page * this.pageSize;
17979 this.store.load(options);
17982 * this code will make the page width larger, at the beginning, the list not align correctly,
17983 * we should expand the list on onLoad
17984 * so command out it
17989 this.selectedIndex = -1;
17994 this.loadNext = false;
17998 getParams : function(q){
18000 //p[this.queryParam] = q;
18004 p.limit = this.pageSize;
18010 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18012 collapse : function(){
18013 if(!this.isExpanded()){
18019 this.hasFocus = false;
18023 this.cancelBtn.hide();
18024 this.trigger.show();
18027 this.tickableInputEl().dom.value = '';
18028 this.tickableInputEl().blur();
18033 Roo.get(document).un('mousedown', this.collapseIf, this);
18034 Roo.get(document).un('mousewheel', this.collapseIf, this);
18035 if (!this.editable) {
18036 Roo.get(document).un('keydown', this.listKeyPress, this);
18038 this.fireEvent('collapse', this);
18044 collapseIf : function(e){
18045 var in_combo = e.within(this.el);
18046 var in_list = e.within(this.list);
18047 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18049 if (in_combo || in_list || is_list) {
18050 //e.stopPropagation();
18055 this.onTickableFooterButtonClick(e, false, false);
18063 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18065 expand : function(){
18067 if(this.isExpanded() || !this.hasFocus){
18071 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18072 this.list.setWidth(lw);
18078 this.restrictHeight();
18082 this.tickItems = Roo.apply([], this.item);
18085 this.cancelBtn.show();
18086 this.trigger.hide();
18089 this.tickableInputEl().focus();
18094 Roo.get(document).on('mousedown', this.collapseIf, this);
18095 Roo.get(document).on('mousewheel', this.collapseIf, this);
18096 if (!this.editable) {
18097 Roo.get(document).on('keydown', this.listKeyPress, this);
18100 this.fireEvent('expand', this);
18104 // Implements the default empty TriggerField.onTriggerClick function
18105 onTriggerClick : function(e)
18107 Roo.log('trigger click');
18109 if(this.disabled || !this.triggerList){
18114 this.loadNext = false;
18116 if(this.isExpanded()){
18118 if (!this.blockFocus) {
18119 this.inputEl().focus();
18123 this.hasFocus = true;
18124 if(this.triggerAction == 'all') {
18125 this.doQuery(this.allQuery, true);
18127 this.doQuery(this.getRawValue());
18129 if (!this.blockFocus) {
18130 this.inputEl().focus();
18135 onTickableTriggerClick : function(e)
18142 this.loadNext = false;
18143 this.hasFocus = true;
18145 if(this.triggerAction == 'all') {
18146 this.doQuery(this.allQuery, true);
18148 this.doQuery(this.getRawValue());
18152 onSearchFieldClick : function(e)
18154 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18155 this.onTickableFooterButtonClick(e, false, false);
18159 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18164 this.loadNext = false;
18165 this.hasFocus = true;
18167 if(this.triggerAction == 'all') {
18168 this.doQuery(this.allQuery, true);
18170 this.doQuery(this.getRawValue());
18174 listKeyPress : function(e)
18176 //Roo.log('listkeypress');
18177 // scroll to first matching element based on key pres..
18178 if (e.isSpecialKey()) {
18181 var k = String.fromCharCode(e.getKey()).toUpperCase();
18184 var csel = this.view.getSelectedNodes();
18185 var cselitem = false;
18187 var ix = this.view.indexOf(csel[0]);
18188 cselitem = this.store.getAt(ix);
18189 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18195 this.store.each(function(v) {
18197 // start at existing selection.
18198 if (cselitem.id == v.id) {
18204 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18205 match = this.store.indexOf(v);
18211 if (match === false) {
18212 return true; // no more action?
18215 this.view.select(match);
18216 var sn = Roo.get(this.view.getSelectedNodes()[0]);
18217 sn.scrollIntoView(sn.dom.parentNode, false);
18220 onViewScroll : function(e, t){
18222 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){
18226 this.hasQuery = true;
18228 this.loading = this.list.select('.loading', true).first();
18230 if(this.loading === null){
18231 this.list.createChild({
18233 cls: 'loading roo-select2-more-results roo-select2-active',
18234 html: 'Loading more results...'
18237 this.loading = this.list.select('.loading', true).first();
18239 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18241 this.loading.hide();
18244 this.loading.show();
18249 this.loadNext = true;
18251 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18256 addItem : function(o)
18258 var dv = ''; // display value
18260 if (this.displayField) {
18261 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18263 // this is an error condition!!!
18264 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
18271 var choice = this.choices.createChild({
18273 cls: 'roo-select2-search-choice',
18282 cls: 'roo-select2-search-choice-close fa fa-times',
18287 }, this.searchField);
18289 var close = choice.select('a.roo-select2-search-choice-close', true).first();
18291 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18299 this.inputEl().dom.value = '';
18304 onRemoveItem : function(e, _self, o)
18306 e.preventDefault();
18308 this.lastItem = Roo.apply([], this.item);
18310 var index = this.item.indexOf(o.data) * 1;
18313 Roo.log('not this item?!');
18317 this.item.splice(index, 1);
18322 this.fireEvent('remove', this, e);
18328 syncValue : function()
18330 if(!this.item.length){
18337 Roo.each(this.item, function(i){
18338 if(_this.valueField){
18339 value.push(i[_this.valueField]);
18346 this.value = value.join(',');
18348 if(this.hiddenField){
18349 this.hiddenField.dom.value = this.value;
18352 this.store.fireEvent("datachanged", this.store);
18357 clearItem : function()
18359 if(!this.multiple){
18365 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18373 if(this.tickable && !Roo.isTouch){
18374 this.view.refresh();
18378 inputEl: function ()
18380 if(Roo.isIOS && this.useNativeIOS){
18381 return this.el.select('select.roo-ios-select', true).first();
18384 if(Roo.isTouch && this.mobileTouchView){
18385 return this.el.select('input.form-control',true).first();
18389 return this.searchField;
18392 return this.el.select('input.form-control',true).first();
18395 onTickableFooterButtonClick : function(e, btn, el)
18397 e.preventDefault();
18399 this.lastItem = Roo.apply([], this.item);
18401 if(btn && btn.name == 'cancel'){
18402 this.tickItems = Roo.apply([], this.item);
18411 Roo.each(this.tickItems, function(o){
18419 validate : function()
18421 if(this.getVisibilityEl().hasClass('hidden')){
18425 var v = this.getRawValue();
18428 v = this.getValue();
18431 if(this.disabled || this.allowBlank || v.length){
18436 this.markInvalid();
18440 tickableInputEl : function()
18442 if(!this.tickable || !this.editable){
18443 return this.inputEl();
18446 return this.inputEl().select('.roo-select2-search-field-input', true).first();
18450 getAutoCreateTouchView : function()
18455 cls: 'form-group' //input-group
18461 type : this.inputType,
18462 cls : 'form-control x-combo-noedit',
18463 autocomplete: 'new-password',
18464 placeholder : this.placeholder || '',
18469 input.name = this.name;
18473 input.cls += ' input-' + this.size;
18476 if (this.disabled) {
18477 input.disabled = true;
18481 cls : 'roo-combobox-wrap',
18488 inputblock.cls += ' input-group';
18490 inputblock.cn.unshift({
18492 cls : 'input-group-addon input-group-prepend input-group-text',
18497 if(this.removable && !this.multiple){
18498 inputblock.cls += ' roo-removable';
18500 inputblock.cn.push({
18503 cls : 'roo-combo-removable-btn close'
18507 if(this.hasFeedback && !this.allowBlank){
18509 inputblock.cls += ' has-feedback';
18511 inputblock.cn.push({
18513 cls: 'glyphicon form-control-feedback'
18520 inputblock.cls += (this.before) ? '' : ' input-group';
18522 inputblock.cn.push({
18524 cls : 'input-group-addon input-group-append input-group-text',
18530 var ibwrap = inputblock;
18535 cls: 'roo-select2-choices',
18539 cls: 'roo-select2-search-field',
18552 cls: 'roo-select2-container input-group roo-touchview-combobox ',
18557 cls: 'form-hidden-field'
18563 if(!this.multiple && this.showToggleBtn){
18569 if (this.caret != false) {
18572 cls: 'fa fa-' + this.caret
18579 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
18581 Roo.bootstrap.version == 3 ? caret : '',
18584 cls: 'combobox-clear',
18598 combobox.cls += ' roo-select2-container-multi';
18601 var required = this.allowBlank ? {
18603 style: 'display: none'
18606 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
18607 tooltip : 'This field is required'
18610 var align = this.labelAlign || this.parentLabelAlign();
18612 if (align ==='left' && this.fieldLabel.length) {
18618 cls : 'control-label col-form-label',
18619 html : this.fieldLabel
18623 cls : 'roo-combobox-wrap ',
18630 var labelCfg = cfg.cn[1];
18631 var contentCfg = cfg.cn[2];
18634 if(this.indicatorpos == 'right'){
18639 cls : 'control-label col-form-label',
18643 html : this.fieldLabel
18649 cls : "roo-combobox-wrap ",
18657 labelCfg = cfg.cn[0];
18658 contentCfg = cfg.cn[1];
18663 if(this.labelWidth > 12){
18664 labelCfg.style = "width: " + this.labelWidth + 'px';
18667 if(this.labelWidth < 13 && this.labelmd == 0){
18668 this.labelmd = this.labelWidth;
18671 if(this.labellg > 0){
18672 labelCfg.cls += ' col-lg-' + this.labellg;
18673 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
18676 if(this.labelmd > 0){
18677 labelCfg.cls += ' col-md-' + this.labelmd;
18678 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
18681 if(this.labelsm > 0){
18682 labelCfg.cls += ' col-sm-' + this.labelsm;
18683 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
18686 if(this.labelxs > 0){
18687 labelCfg.cls += ' col-xs-' + this.labelxs;
18688 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
18692 } else if ( this.fieldLabel.length) {
18697 cls : 'control-label',
18698 html : this.fieldLabel
18709 if(this.indicatorpos == 'right'){
18713 cls : 'control-label',
18714 html : this.fieldLabel,
18732 var settings = this;
18734 ['xs','sm','md','lg'].map(function(size){
18735 if (settings[size]) {
18736 cfg.cls += ' col-' + size + '-' + settings[size];
18743 initTouchView : function()
18745 this.renderTouchView();
18747 this.touchViewEl.on('scroll', function(){
18748 this.el.dom.scrollTop = 0;
18751 this.originalValue = this.getValue();
18753 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
18755 this.inputEl().on("click", this.showTouchView, this);
18756 if (this.triggerEl) {
18757 this.triggerEl.on("click", this.showTouchView, this);
18761 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
18762 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
18764 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
18766 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
18767 this.store.on('load', this.onTouchViewLoad, this);
18768 this.store.on('loadexception', this.onTouchViewLoadException, this);
18770 if(this.hiddenName){
18772 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
18774 this.hiddenField.dom.value =
18775 this.hiddenValue !== undefined ? this.hiddenValue :
18776 this.value !== undefined ? this.value : '';
18778 this.el.dom.removeAttribute('name');
18779 this.hiddenField.dom.setAttribute('name', this.hiddenName);
18783 this.choices = this.el.select('ul.roo-select2-choices', true).first();
18784 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
18787 if(this.removable && !this.multiple){
18788 var close = this.closeTriggerEl();
18790 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
18791 close.on('click', this.removeBtnClick, this, close);
18795 * fix the bug in Safari iOS8
18797 this.inputEl().on("focus", function(e){
18798 document.activeElement.blur();
18801 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
18808 renderTouchView : function()
18810 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
18811 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18813 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
18814 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18816 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
18817 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18818 this.touchViewBodyEl.setStyle('overflow', 'auto');
18820 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
18821 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18823 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
18824 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18828 showTouchView : function()
18834 this.touchViewHeaderEl.hide();
18836 if(this.modalTitle.length){
18837 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
18838 this.touchViewHeaderEl.show();
18841 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
18842 this.touchViewEl.show();
18844 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
18846 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
18847 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18849 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18851 if(this.modalTitle.length){
18852 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18855 this.touchViewBodyEl.setHeight(bodyHeight);
18859 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
18861 this.touchViewEl.addClass(['in','show']);
18864 if(this._touchViewMask){
18865 Roo.get(document.body).addClass("x-body-masked");
18866 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18867 this._touchViewMask.setStyle('z-index', 10000);
18868 this._touchViewMask.addClass('show');
18871 this.doTouchViewQuery();
18875 hideTouchView : function()
18877 this.touchViewEl.removeClass(['in','show']);
18881 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
18883 this.touchViewEl.setStyle('display', 'none');
18886 if(this._touchViewMask){
18887 this._touchViewMask.removeClass('show');
18888 Roo.get(document.body).removeClass("x-body-masked");
18892 setTouchViewValue : function()
18899 Roo.each(this.tickItems, function(o){
18904 this.hideTouchView();
18907 doTouchViewQuery : function()
18916 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
18920 if(!this.alwaysQuery || this.mode == 'local'){
18921 this.onTouchViewLoad();
18928 onTouchViewBeforeLoad : function(combo,opts)
18934 onTouchViewLoad : function()
18936 if(this.store.getCount() < 1){
18937 this.onTouchViewEmptyResults();
18941 this.clearTouchView();
18943 var rawValue = this.getRawValue();
18945 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
18947 this.tickItems = [];
18949 this.store.data.each(function(d, rowIndex){
18950 var row = this.touchViewListGroup.createChild(template);
18952 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
18953 row.addClass(d.data.cls);
18956 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
18959 html : d.data[this.displayField]
18962 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
18963 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
18966 row.removeClass('selected');
18967 if(!this.multiple && this.valueField &&
18968 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
18971 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18972 row.addClass('selected');
18975 if(this.multiple && this.valueField &&
18976 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
18980 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18981 this.tickItems.push(d.data);
18984 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
18988 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
18990 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18992 if(this.modalTitle.length){
18993 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18996 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
18998 if(this.mobile_restrict_height && listHeight < bodyHeight){
18999 this.touchViewBodyEl.setHeight(listHeight);
19004 if(firstChecked && listHeight > bodyHeight){
19005 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19010 onTouchViewLoadException : function()
19012 this.hideTouchView();
19015 onTouchViewEmptyResults : function()
19017 this.clearTouchView();
19019 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
19021 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19025 clearTouchView : function()
19027 this.touchViewListGroup.dom.innerHTML = '';
19030 onTouchViewClick : function(e, el, o)
19032 e.preventDefault();
19035 var rowIndex = o.rowIndex;
19037 var r = this.store.getAt(rowIndex);
19039 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19041 if(!this.multiple){
19042 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19043 c.dom.removeAttribute('checked');
19046 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19048 this.setFromData(r.data);
19050 var close = this.closeTriggerEl();
19056 this.hideTouchView();
19058 this.fireEvent('select', this, r, rowIndex);
19063 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19064 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19065 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19069 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19070 this.addItem(r.data);
19071 this.tickItems.push(r.data);
19075 getAutoCreateNativeIOS : function()
19078 cls: 'form-group' //input-group,
19083 cls : 'roo-ios-select'
19087 combobox.name = this.name;
19090 if (this.disabled) {
19091 combobox.disabled = true;
19094 var settings = this;
19096 ['xs','sm','md','lg'].map(function(size){
19097 if (settings[size]) {
19098 cfg.cls += ' col-' + size + '-' + settings[size];
19108 initIOSView : function()
19110 this.store.on('load', this.onIOSViewLoad, this);
19115 onIOSViewLoad : function()
19117 if(this.store.getCount() < 1){
19121 this.clearIOSView();
19123 if(this.allowBlank) {
19125 var default_text = '-- SELECT --';
19127 if(this.placeholder.length){
19128 default_text = this.placeholder;
19131 if(this.emptyTitle.length){
19132 default_text += ' - ' + this.emptyTitle + ' -';
19135 var opt = this.inputEl().createChild({
19138 html : default_text
19142 o[this.valueField] = 0;
19143 o[this.displayField] = default_text;
19145 this.ios_options.push({
19152 this.store.data.each(function(d, rowIndex){
19156 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19157 html = d.data[this.displayField];
19162 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19163 value = d.data[this.valueField];
19172 if(this.value == d.data[this.valueField]){
19173 option['selected'] = true;
19176 var opt = this.inputEl().createChild(option);
19178 this.ios_options.push({
19185 this.inputEl().on('change', function(){
19186 this.fireEvent('select', this);
19191 clearIOSView: function()
19193 this.inputEl().dom.innerHTML = '';
19195 this.ios_options = [];
19198 setIOSValue: function(v)
19202 if(!this.ios_options){
19206 Roo.each(this.ios_options, function(opts){
19208 opts.el.dom.removeAttribute('selected');
19210 if(opts.data[this.valueField] != v){
19214 opts.el.dom.setAttribute('selected', true);
19220 * @cfg {Boolean} grow
19224 * @cfg {Number} growMin
19228 * @cfg {Number} growMax
19237 Roo.apply(Roo.bootstrap.ComboBox, {
19241 cls: 'modal-header',
19263 cls: 'list-group-item',
19267 cls: 'roo-combobox-list-group-item-value'
19271 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19285 listItemCheckbox : {
19287 cls: 'list-group-item',
19291 cls: 'roo-combobox-list-group-item-value'
19295 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19311 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19316 cls: 'modal-footer',
19324 cls: 'col-xs-6 text-left',
19327 cls: 'btn btn-danger roo-touch-view-cancel',
19333 cls: 'col-xs-6 text-right',
19336 cls: 'btn btn-success roo-touch-view-ok',
19347 Roo.apply(Roo.bootstrap.ComboBox, {
19349 touchViewTemplate : {
19351 cls: 'modal fade roo-combobox-touch-view',
19355 cls: 'modal-dialog',
19356 style : 'position:fixed', // we have to fix position....
19360 cls: 'modal-content',
19362 Roo.bootstrap.ComboBox.header,
19363 Roo.bootstrap.ComboBox.body,
19364 Roo.bootstrap.ComboBox.footer
19373 * Ext JS Library 1.1.1
19374 * Copyright(c) 2006-2007, Ext JS, LLC.
19376 * Originally Released Under LGPL - original licence link has changed is not relivant.
19379 * <script type="text/javascript">
19384 * @extends Roo.util.Observable
19385 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
19386 * This class also supports single and multi selection modes. <br>
19387 * Create a data model bound view:
19389 var store = new Roo.data.Store(...);
19391 var view = new Roo.View({
19393 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
19395 singleSelect: true,
19396 selectedClass: "ydataview-selected",
19400 // listen for node click?
19401 view.on("click", function(vw, index, node, e){
19402 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19406 dataModel.load("foobar.xml");
19408 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19410 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19411 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19413 * Note: old style constructor is still suported (container, template, config)
19416 * Create a new View
19417 * @param {Object} config The config object
19420 Roo.View = function(config, depreciated_tpl, depreciated_config){
19422 this.parent = false;
19424 if (typeof(depreciated_tpl) == 'undefined') {
19425 // new way.. - universal constructor.
19426 Roo.apply(this, config);
19427 this.el = Roo.get(this.el);
19430 this.el = Roo.get(config);
19431 this.tpl = depreciated_tpl;
19432 Roo.apply(this, depreciated_config);
19434 this.wrapEl = this.el.wrap().wrap();
19435 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19438 if(typeof(this.tpl) == "string"){
19439 this.tpl = new Roo.Template(this.tpl);
19441 // support xtype ctors..
19442 this.tpl = new Roo.factory(this.tpl, Roo);
19446 this.tpl.compile();
19451 * @event beforeclick
19452 * Fires before a click is processed. Returns false to cancel the default action.
19453 * @param {Roo.View} this
19454 * @param {Number} index The index of the target node
19455 * @param {HTMLElement} node The target node
19456 * @param {Roo.EventObject} e The raw event object
19458 "beforeclick" : true,
19461 * Fires when a template node is clicked.
19462 * @param {Roo.View} this
19463 * @param {Number} index The index of the target node
19464 * @param {HTMLElement} node The target node
19465 * @param {Roo.EventObject} e The raw event object
19470 * Fires when a template node is double clicked.
19471 * @param {Roo.View} this
19472 * @param {Number} index The index of the target node
19473 * @param {HTMLElement} node The target node
19474 * @param {Roo.EventObject} e The raw event object
19478 * @event contextmenu
19479 * Fires when a template node is right clicked.
19480 * @param {Roo.View} this
19481 * @param {Number} index The index of the target node
19482 * @param {HTMLElement} node The target node
19483 * @param {Roo.EventObject} e The raw event object
19485 "contextmenu" : true,
19487 * @event selectionchange
19488 * Fires when the selected nodes change.
19489 * @param {Roo.View} this
19490 * @param {Array} selections Array of the selected nodes
19492 "selectionchange" : true,
19495 * @event beforeselect
19496 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
19497 * @param {Roo.View} this
19498 * @param {HTMLElement} node The node to be selected
19499 * @param {Array} selections Array of currently selected nodes
19501 "beforeselect" : true,
19503 * @event preparedata
19504 * Fires on every row to render, to allow you to change the data.
19505 * @param {Roo.View} this
19506 * @param {Object} data to be rendered (change this)
19508 "preparedata" : true
19516 "click": this.onClick,
19517 "dblclick": this.onDblClick,
19518 "contextmenu": this.onContextMenu,
19522 this.selections = [];
19524 this.cmp = new Roo.CompositeElementLite([]);
19526 this.store = Roo.factory(this.store, Roo.data);
19527 this.setStore(this.store, true);
19530 if ( this.footer && this.footer.xtype) {
19532 var fctr = this.wrapEl.appendChild(document.createElement("div"));
19534 this.footer.dataSource = this.store;
19535 this.footer.container = fctr;
19536 this.footer = Roo.factory(this.footer, Roo);
19537 fctr.insertFirst(this.el);
19539 // this is a bit insane - as the paging toolbar seems to detach the el..
19540 // dom.parentNode.parentNode.parentNode
19541 // they get detached?
19545 Roo.View.superclass.constructor.call(this);
19550 Roo.extend(Roo.View, Roo.util.Observable, {
19553 * @cfg {Roo.data.Store} store Data store to load data from.
19558 * @cfg {String|Roo.Element} el The container element.
19563 * @cfg {String|Roo.Template} tpl The template used by this View
19567 * @cfg {String} dataName the named area of the template to use as the data area
19568 * Works with domtemplates roo-name="name"
19572 * @cfg {String} selectedClass The css class to add to selected nodes
19574 selectedClass : "x-view-selected",
19576 * @cfg {String} emptyText The empty text to show when nothing is loaded.
19581 * @cfg {String} text to display on mask (default Loading)
19585 * @cfg {Boolean} multiSelect Allow multiple selection
19587 multiSelect : false,
19589 * @cfg {Boolean} singleSelect Allow single selection
19591 singleSelect: false,
19594 * @cfg {Boolean} toggleSelect - selecting
19596 toggleSelect : false,
19599 * @cfg {Boolean} tickable - selecting
19604 * Returns the element this view is bound to.
19605 * @return {Roo.Element}
19607 getEl : function(){
19608 return this.wrapEl;
19614 * Refreshes the view. - called by datachanged on the store. - do not call directly.
19616 refresh : function(){
19617 //Roo.log('refresh');
19620 // if we are using something like 'domtemplate', then
19621 // the what gets used is:
19622 // t.applySubtemplate(NAME, data, wrapping data..)
19623 // the outer template then get' applied with
19624 // the store 'extra data'
19625 // and the body get's added to the
19626 // roo-name="data" node?
19627 // <span class='roo-tpl-{name}'></span> ?????
19631 this.clearSelections();
19632 this.el.update("");
19634 var records = this.store.getRange();
19635 if(records.length < 1) {
19637 // is this valid?? = should it render a template??
19639 this.el.update(this.emptyText);
19643 if (this.dataName) {
19644 this.el.update(t.apply(this.store.meta)); //????
19645 el = this.el.child('.roo-tpl-' + this.dataName);
19648 for(var i = 0, len = records.length; i < len; i++){
19649 var data = this.prepareData(records[i].data, i, records[i]);
19650 this.fireEvent("preparedata", this, data, i, records[i]);
19652 var d = Roo.apply({}, data);
19655 Roo.apply(d, {'roo-id' : Roo.id()});
19659 Roo.each(this.parent.item, function(item){
19660 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
19663 Roo.apply(d, {'roo-data-checked' : 'checked'});
19667 html[html.length] = Roo.util.Format.trim(
19669 t.applySubtemplate(this.dataName, d, this.store.meta) :
19676 el.update(html.join(""));
19677 this.nodes = el.dom.childNodes;
19678 this.updateIndexes(0);
19683 * Function to override to reformat the data that is sent to
19684 * the template for each node.
19685 * DEPRICATED - use the preparedata event handler.
19686 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
19687 * a JSON object for an UpdateManager bound view).
19689 prepareData : function(data, index, record)
19691 this.fireEvent("preparedata", this, data, index, record);
19695 onUpdate : function(ds, record){
19696 // Roo.log('on update');
19697 this.clearSelections();
19698 var index = this.store.indexOf(record);
19699 var n = this.nodes[index];
19700 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
19701 n.parentNode.removeChild(n);
19702 this.updateIndexes(index, index);
19708 onAdd : function(ds, records, index)
19710 //Roo.log(['on Add', ds, records, index] );
19711 this.clearSelections();
19712 if(this.nodes.length == 0){
19716 var n = this.nodes[index];
19717 for(var i = 0, len = records.length; i < len; i++){
19718 var d = this.prepareData(records[i].data, i, records[i]);
19720 this.tpl.insertBefore(n, d);
19723 this.tpl.append(this.el, d);
19726 this.updateIndexes(index);
19729 onRemove : function(ds, record, index){
19730 // Roo.log('onRemove');
19731 this.clearSelections();
19732 var el = this.dataName ?
19733 this.el.child('.roo-tpl-' + this.dataName) :
19736 el.dom.removeChild(this.nodes[index]);
19737 this.updateIndexes(index);
19741 * Refresh an individual node.
19742 * @param {Number} index
19744 refreshNode : function(index){
19745 this.onUpdate(this.store, this.store.getAt(index));
19748 updateIndexes : function(startIndex, endIndex){
19749 var ns = this.nodes;
19750 startIndex = startIndex || 0;
19751 endIndex = endIndex || ns.length - 1;
19752 for(var i = startIndex; i <= endIndex; i++){
19753 ns[i].nodeIndex = i;
19758 * Changes the data store this view uses and refresh the view.
19759 * @param {Store} store
19761 setStore : function(store, initial){
19762 if(!initial && this.store){
19763 this.store.un("datachanged", this.refresh);
19764 this.store.un("add", this.onAdd);
19765 this.store.un("remove", this.onRemove);
19766 this.store.un("update", this.onUpdate);
19767 this.store.un("clear", this.refresh);
19768 this.store.un("beforeload", this.onBeforeLoad);
19769 this.store.un("load", this.onLoad);
19770 this.store.un("loadexception", this.onLoad);
19774 store.on("datachanged", this.refresh, this);
19775 store.on("add", this.onAdd, this);
19776 store.on("remove", this.onRemove, this);
19777 store.on("update", this.onUpdate, this);
19778 store.on("clear", this.refresh, this);
19779 store.on("beforeload", this.onBeforeLoad, this);
19780 store.on("load", this.onLoad, this);
19781 store.on("loadexception", this.onLoad, this);
19789 * onbeforeLoad - masks the loading area.
19792 onBeforeLoad : function(store,opts)
19794 //Roo.log('onBeforeLoad');
19796 this.el.update("");
19798 this.el.mask(this.mask ? this.mask : "Loading" );
19800 onLoad : function ()
19807 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
19808 * @param {HTMLElement} node
19809 * @return {HTMLElement} The template node
19811 findItemFromChild : function(node){
19812 var el = this.dataName ?
19813 this.el.child('.roo-tpl-' + this.dataName,true) :
19816 if(!node || node.parentNode == el){
19819 var p = node.parentNode;
19820 while(p && p != el){
19821 if(p.parentNode == el){
19830 onClick : function(e){
19831 var item = this.findItemFromChild(e.getTarget());
19833 var index = this.indexOf(item);
19834 if(this.onItemClick(item, index, e) !== false){
19835 this.fireEvent("click", this, index, item, e);
19838 this.clearSelections();
19843 onContextMenu : function(e){
19844 var item = this.findItemFromChild(e.getTarget());
19846 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
19851 onDblClick : function(e){
19852 var item = this.findItemFromChild(e.getTarget());
19854 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
19858 onItemClick : function(item, index, e)
19860 if(this.fireEvent("beforeclick", this, index, item, e) === false){
19863 if (this.toggleSelect) {
19864 var m = this.isSelected(item) ? 'unselect' : 'select';
19867 _t[m](item, true, false);
19870 if(this.multiSelect || this.singleSelect){
19871 if(this.multiSelect && e.shiftKey && this.lastSelection){
19872 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
19874 this.select(item, this.multiSelect && e.ctrlKey);
19875 this.lastSelection = item;
19878 if(!this.tickable){
19879 e.preventDefault();
19887 * Get the number of selected nodes.
19890 getSelectionCount : function(){
19891 return this.selections.length;
19895 * Get the currently selected nodes.
19896 * @return {Array} An array of HTMLElements
19898 getSelectedNodes : function(){
19899 return this.selections;
19903 * Get the indexes of the selected nodes.
19906 getSelectedIndexes : function(){
19907 var indexes = [], s = this.selections;
19908 for(var i = 0, len = s.length; i < len; i++){
19909 indexes.push(s[i].nodeIndex);
19915 * Clear all selections
19916 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
19918 clearSelections : function(suppressEvent){
19919 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
19920 this.cmp.elements = this.selections;
19921 this.cmp.removeClass(this.selectedClass);
19922 this.selections = [];
19923 if(!suppressEvent){
19924 this.fireEvent("selectionchange", this, this.selections);
19930 * Returns true if the passed node is selected
19931 * @param {HTMLElement/Number} node The node or node index
19932 * @return {Boolean}
19934 isSelected : function(node){
19935 var s = this.selections;
19939 node = this.getNode(node);
19940 return s.indexOf(node) !== -1;
19945 * @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
19946 * @param {Boolean} keepExisting (optional) true to keep existing selections
19947 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
19949 select : function(nodeInfo, keepExisting, suppressEvent){
19950 if(nodeInfo instanceof Array){
19952 this.clearSelections(true);
19954 for(var i = 0, len = nodeInfo.length; i < len; i++){
19955 this.select(nodeInfo[i], true, true);
19959 var node = this.getNode(nodeInfo);
19960 if(!node || this.isSelected(node)){
19961 return; // already selected.
19964 this.clearSelections(true);
19967 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
19968 Roo.fly(node).addClass(this.selectedClass);
19969 this.selections.push(node);
19970 if(!suppressEvent){
19971 this.fireEvent("selectionchange", this, this.selections);
19979 * @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
19980 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
19981 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
19983 unselect : function(nodeInfo, keepExisting, suppressEvent)
19985 if(nodeInfo instanceof Array){
19986 Roo.each(this.selections, function(s) {
19987 this.unselect(s, nodeInfo);
19991 var node = this.getNode(nodeInfo);
19992 if(!node || !this.isSelected(node)){
19993 //Roo.log("not selected");
19994 return; // not selected.
19998 Roo.each(this.selections, function(s) {
20000 Roo.fly(node).removeClass(this.selectedClass);
20007 this.selections= ns;
20008 this.fireEvent("selectionchange", this, this.selections);
20012 * Gets a template node.
20013 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20014 * @return {HTMLElement} The node or null if it wasn't found
20016 getNode : function(nodeInfo){
20017 if(typeof nodeInfo == "string"){
20018 return document.getElementById(nodeInfo);
20019 }else if(typeof nodeInfo == "number"){
20020 return this.nodes[nodeInfo];
20026 * Gets a range template nodes.
20027 * @param {Number} startIndex
20028 * @param {Number} endIndex
20029 * @return {Array} An array of nodes
20031 getNodes : function(start, end){
20032 var ns = this.nodes;
20033 start = start || 0;
20034 end = typeof end == "undefined" ? ns.length - 1 : end;
20037 for(var i = start; i <= end; i++){
20041 for(var i = start; i >= end; i--){
20049 * Finds the index of the passed node
20050 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20051 * @return {Number} The index of the node or -1
20053 indexOf : function(node){
20054 node = this.getNode(node);
20055 if(typeof node.nodeIndex == "number"){
20056 return node.nodeIndex;
20058 var ns = this.nodes;
20059 for(var i = 0, len = ns.length; i < len; i++){
20070 * based on jquery fullcalendar
20074 Roo.bootstrap = Roo.bootstrap || {};
20076 * @class Roo.bootstrap.Calendar
20077 * @extends Roo.bootstrap.Component
20078 * Bootstrap Calendar class
20079 * @cfg {Boolean} loadMask (true|false) default false
20080 * @cfg {Object} header generate the user specific header of the calendar, default false
20083 * Create a new Container
20084 * @param {Object} config The config object
20089 Roo.bootstrap.Calendar = function(config){
20090 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20094 * Fires when a date is selected
20095 * @param {DatePicker} this
20096 * @param {Date} date The selected date
20100 * @event monthchange
20101 * Fires when the displayed month changes
20102 * @param {DatePicker} this
20103 * @param {Date} date The selected month
20105 'monthchange': true,
20107 * @event evententer
20108 * Fires when mouse over an event
20109 * @param {Calendar} this
20110 * @param {event} Event
20112 'evententer': true,
20114 * @event eventleave
20115 * Fires when the mouse leaves an
20116 * @param {Calendar} this
20119 'eventleave': true,
20121 * @event eventclick
20122 * Fires when the mouse click an
20123 * @param {Calendar} this
20132 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
20135 * @cfg {Number} startDay
20136 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20144 getAutoCreate : function(){
20147 var fc_button = function(name, corner, style, content ) {
20148 return Roo.apply({},{
20150 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
20152 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20155 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20166 style : 'width:100%',
20173 cls : 'fc-header-left',
20175 fc_button('prev', 'left', 'arrow', '‹' ),
20176 fc_button('next', 'right', 'arrow', '›' ),
20177 { tag: 'span', cls: 'fc-header-space' },
20178 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
20186 cls : 'fc-header-center',
20190 cls: 'fc-header-title',
20193 html : 'month / year'
20201 cls : 'fc-header-right',
20203 /* fc_button('month', 'left', '', 'month' ),
20204 fc_button('week', '', '', 'week' ),
20205 fc_button('day', 'right', '', 'day' )
20217 header = this.header;
20220 var cal_heads = function() {
20222 // fixme - handle this.
20224 for (var i =0; i < Date.dayNames.length; i++) {
20225 var d = Date.dayNames[i];
20228 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20229 html : d.substring(0,3)
20233 ret[0].cls += ' fc-first';
20234 ret[6].cls += ' fc-last';
20237 var cal_cell = function(n) {
20240 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20245 cls: 'fc-day-number',
20249 cls: 'fc-day-content',
20253 style: 'position: relative;' // height: 17px;
20265 var cal_rows = function() {
20268 for (var r = 0; r < 6; r++) {
20275 for (var i =0; i < Date.dayNames.length; i++) {
20276 var d = Date.dayNames[i];
20277 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20280 row.cn[0].cls+=' fc-first';
20281 row.cn[0].cn[0].style = 'min-height:90px';
20282 row.cn[6].cls+=' fc-last';
20286 ret[0].cls += ' fc-first';
20287 ret[4].cls += ' fc-prev-last';
20288 ret[5].cls += ' fc-last';
20295 cls: 'fc-border-separate',
20296 style : 'width:100%',
20304 cls : 'fc-first fc-last',
20322 cls : 'fc-content',
20323 style : "position: relative;",
20326 cls : 'fc-view fc-view-month fc-grid',
20327 style : 'position: relative',
20328 unselectable : 'on',
20331 cls : 'fc-event-container',
20332 style : 'position:absolute;z-index:8;top:0;left:0;'
20350 initEvents : function()
20353 throw "can not find store for calendar";
20359 style: "text-align:center",
20363 style: "background-color:white;width:50%;margin:250 auto",
20367 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
20378 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20380 var size = this.el.select('.fc-content', true).first().getSize();
20381 this.maskEl.setSize(size.width, size.height);
20382 this.maskEl.enableDisplayMode("block");
20383 if(!this.loadMask){
20384 this.maskEl.hide();
20387 this.store = Roo.factory(this.store, Roo.data);
20388 this.store.on('load', this.onLoad, this);
20389 this.store.on('beforeload', this.onBeforeLoad, this);
20393 this.cells = this.el.select('.fc-day',true);
20394 //Roo.log(this.cells);
20395 this.textNodes = this.el.query('.fc-day-number');
20396 this.cells.addClassOnOver('fc-state-hover');
20398 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20399 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20400 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20401 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20403 this.on('monthchange', this.onMonthChange, this);
20405 this.update(new Date().clearTime());
20408 resize : function() {
20409 var sz = this.el.getSize();
20411 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20412 this.el.select('.fc-day-content div',true).setHeight(34);
20417 showPrevMonth : function(e){
20418 this.update(this.activeDate.add("mo", -1));
20420 showToday : function(e){
20421 this.update(new Date().clearTime());
20424 showNextMonth : function(e){
20425 this.update(this.activeDate.add("mo", 1));
20429 showPrevYear : function(){
20430 this.update(this.activeDate.add("y", -1));
20434 showNextYear : function(){
20435 this.update(this.activeDate.add("y", 1));
20440 update : function(date)
20442 var vd = this.activeDate;
20443 this.activeDate = date;
20444 // if(vd && this.el){
20445 // var t = date.getTime();
20446 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20447 // Roo.log('using add remove');
20449 // this.fireEvent('monthchange', this, date);
20451 // this.cells.removeClass("fc-state-highlight");
20452 // this.cells.each(function(c){
20453 // if(c.dateValue == t){
20454 // c.addClass("fc-state-highlight");
20455 // setTimeout(function(){
20456 // try{c.dom.firstChild.focus();}catch(e){}
20466 var days = date.getDaysInMonth();
20468 var firstOfMonth = date.getFirstDateOfMonth();
20469 var startingPos = firstOfMonth.getDay()-this.startDay;
20471 if(startingPos < this.startDay){
20475 var pm = date.add(Date.MONTH, -1);
20476 var prevStart = pm.getDaysInMonth()-startingPos;
20478 this.cells = this.el.select('.fc-day',true);
20479 this.textNodes = this.el.query('.fc-day-number');
20480 this.cells.addClassOnOver('fc-state-hover');
20482 var cells = this.cells.elements;
20483 var textEls = this.textNodes;
20485 Roo.each(cells, function(cell){
20486 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
20489 days += startingPos;
20491 // convert everything to numbers so it's fast
20492 var day = 86400000;
20493 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
20496 //Roo.log(prevStart);
20498 var today = new Date().clearTime().getTime();
20499 var sel = date.clearTime().getTime();
20500 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
20501 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
20502 var ddMatch = this.disabledDatesRE;
20503 var ddText = this.disabledDatesText;
20504 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
20505 var ddaysText = this.disabledDaysText;
20506 var format = this.format;
20508 var setCellClass = function(cal, cell){
20512 //Roo.log('set Cell Class');
20514 var t = d.getTime();
20518 cell.dateValue = t;
20520 cell.className += " fc-today";
20521 cell.className += " fc-state-highlight";
20522 cell.title = cal.todayText;
20525 // disable highlight in other month..
20526 //cell.className += " fc-state-highlight";
20531 cell.className = " fc-state-disabled";
20532 cell.title = cal.minText;
20536 cell.className = " fc-state-disabled";
20537 cell.title = cal.maxText;
20541 if(ddays.indexOf(d.getDay()) != -1){
20542 cell.title = ddaysText;
20543 cell.className = " fc-state-disabled";
20546 if(ddMatch && format){
20547 var fvalue = d.dateFormat(format);
20548 if(ddMatch.test(fvalue)){
20549 cell.title = ddText.replace("%0", fvalue);
20550 cell.className = " fc-state-disabled";
20554 if (!cell.initialClassName) {
20555 cell.initialClassName = cell.dom.className;
20558 cell.dom.className = cell.initialClassName + ' ' + cell.className;
20563 for(; i < startingPos; i++) {
20564 textEls[i].innerHTML = (++prevStart);
20565 d.setDate(d.getDate()+1);
20567 cells[i].className = "fc-past fc-other-month";
20568 setCellClass(this, cells[i]);
20573 for(; i < days; i++){
20574 intDay = i - startingPos + 1;
20575 textEls[i].innerHTML = (intDay);
20576 d.setDate(d.getDate()+1);
20578 cells[i].className = ''; // "x-date-active";
20579 setCellClass(this, cells[i]);
20583 for(; i < 42; i++) {
20584 textEls[i].innerHTML = (++extraDays);
20585 d.setDate(d.getDate()+1);
20587 cells[i].className = "fc-future fc-other-month";
20588 setCellClass(this, cells[i]);
20591 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
20593 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
20595 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
20596 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
20598 if(totalRows != 6){
20599 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
20600 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
20603 this.fireEvent('monthchange', this, date);
20607 if(!this.internalRender){
20608 var main = this.el.dom.firstChild;
20609 var w = main.offsetWidth;
20610 this.el.setWidth(w + this.el.getBorderWidth("lr"));
20611 Roo.fly(main).setWidth(w);
20612 this.internalRender = true;
20613 // opera does not respect the auto grow header center column
20614 // then, after it gets a width opera refuses to recalculate
20615 // without a second pass
20616 if(Roo.isOpera && !this.secondPass){
20617 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
20618 this.secondPass = true;
20619 this.update.defer(10, this, [date]);
20626 findCell : function(dt) {
20627 dt = dt.clearTime().getTime();
20629 this.cells.each(function(c){
20630 //Roo.log("check " +c.dateValue + '?=' + dt);
20631 if(c.dateValue == dt){
20641 findCells : function(ev) {
20642 var s = ev.start.clone().clearTime().getTime();
20644 var e= ev.end.clone().clearTime().getTime();
20647 this.cells.each(function(c){
20648 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
20650 if(c.dateValue > e){
20653 if(c.dateValue < s){
20662 // findBestRow: function(cells)
20666 // for (var i =0 ; i < cells.length;i++) {
20667 // ret = Math.max(cells[i].rows || 0,ret);
20674 addItem : function(ev)
20676 // look for vertical location slot in
20677 var cells = this.findCells(ev);
20679 // ev.row = this.findBestRow(cells);
20681 // work out the location.
20685 for(var i =0; i < cells.length; i++) {
20687 cells[i].row = cells[0].row;
20690 cells[i].row = cells[i].row + 1;
20700 if (crow.start.getY() == cells[i].getY()) {
20702 crow.end = cells[i];
20719 cells[0].events.push(ev);
20721 this.calevents.push(ev);
20724 clearEvents: function() {
20726 if(!this.calevents){
20730 Roo.each(this.cells.elements, function(c){
20736 Roo.each(this.calevents, function(e) {
20737 Roo.each(e.els, function(el) {
20738 el.un('mouseenter' ,this.onEventEnter, this);
20739 el.un('mouseleave' ,this.onEventLeave, this);
20744 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
20750 renderEvents: function()
20754 this.cells.each(function(c) {
20763 if(c.row != c.events.length){
20764 r = 4 - (4 - (c.row - c.events.length));
20767 c.events = ev.slice(0, r);
20768 c.more = ev.slice(r);
20770 if(c.more.length && c.more.length == 1){
20771 c.events.push(c.more.pop());
20774 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
20778 this.cells.each(function(c) {
20780 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
20783 for (var e = 0; e < c.events.length; e++){
20784 var ev = c.events[e];
20785 var rows = ev.rows;
20787 for(var i = 0; i < rows.length; i++) {
20789 // how many rows should it span..
20792 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
20793 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
20795 unselectable : "on",
20798 cls: 'fc-event-inner',
20802 // cls: 'fc-event-time',
20803 // html : cells.length > 1 ? '' : ev.time
20807 cls: 'fc-event-title',
20808 html : String.format('{0}', ev.title)
20815 cls: 'ui-resizable-handle ui-resizable-e',
20816 html : '  '
20823 cfg.cls += ' fc-event-start';
20825 if ((i+1) == rows.length) {
20826 cfg.cls += ' fc-event-end';
20829 var ctr = _this.el.select('.fc-event-container',true).first();
20830 var cg = ctr.createChild(cfg);
20832 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
20833 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
20835 var r = (c.more.length) ? 1 : 0;
20836 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
20837 cg.setWidth(ebox.right - sbox.x -2);
20839 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
20840 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
20841 cg.on('click', _this.onEventClick, _this, ev);
20852 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
20853 style : 'position: absolute',
20854 unselectable : "on",
20857 cls: 'fc-event-inner',
20861 cls: 'fc-event-title',
20869 cls: 'ui-resizable-handle ui-resizable-e',
20870 html : '  '
20876 var ctr = _this.el.select('.fc-event-container',true).first();
20877 var cg = ctr.createChild(cfg);
20879 var sbox = c.select('.fc-day-content',true).first().getBox();
20880 var ebox = c.select('.fc-day-content',true).first().getBox();
20882 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
20883 cg.setWidth(ebox.right - sbox.x -2);
20885 cg.on('click', _this.onMoreEventClick, _this, c.more);
20895 onEventEnter: function (e, el,event,d) {
20896 this.fireEvent('evententer', this, el, event);
20899 onEventLeave: function (e, el,event,d) {
20900 this.fireEvent('eventleave', this, el, event);
20903 onEventClick: function (e, el,event,d) {
20904 this.fireEvent('eventclick', this, el, event);
20907 onMonthChange: function () {
20911 onMoreEventClick: function(e, el, more)
20915 this.calpopover.placement = 'right';
20916 this.calpopover.setTitle('More');
20918 this.calpopover.setContent('');
20920 var ctr = this.calpopover.el.select('.popover-content', true).first();
20922 Roo.each(more, function(m){
20924 cls : 'fc-event-hori fc-event-draggable',
20927 var cg = ctr.createChild(cfg);
20929 cg.on('click', _this.onEventClick, _this, m);
20932 this.calpopover.show(el);
20937 onLoad: function ()
20939 this.calevents = [];
20942 if(this.store.getCount() > 0){
20943 this.store.data.each(function(d){
20946 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
20947 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
20948 time : d.data.start_time,
20949 title : d.data.title,
20950 description : d.data.description,
20951 venue : d.data.venue
20956 this.renderEvents();
20958 if(this.calevents.length && this.loadMask){
20959 this.maskEl.hide();
20963 onBeforeLoad: function()
20965 this.clearEvents();
20967 this.maskEl.show();
20981 * @class Roo.bootstrap.Popover
20982 * @extends Roo.bootstrap.Component
20983 * Bootstrap Popover class
20984 * @cfg {String} html contents of the popover (or false to use children..)
20985 * @cfg {String} title of popover (or false to hide)
20986 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
20987 * @cfg {String} trigger click || hover (or false to trigger manually)
20988 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
20989 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
20990 * - if false and it has a 'parent' then it will be automatically added to that element
20991 * - if string - Roo.get will be called
20992 * @cfg {Number} delay - delay before showing
20995 * Create a new Popover
20996 * @param {Object} config The config object
20999 Roo.bootstrap.Popover = function(config){
21000 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21006 * After the popover show
21008 * @param {Roo.bootstrap.Popover} this
21013 * After the popover hide
21015 * @param {Roo.bootstrap.Popover} this
21021 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
21026 placement : 'right',
21027 trigger : 'hover', // hover
21033 can_build_overlaid : false,
21035 maskEl : false, // the mask element
21038 alignEl : false, // when show is called with an element - this get's stored.
21040 getChildContainer : function()
21042 return this.contentEl;
21045 getPopoverHeader : function()
21047 this.title = true; // flag not to hide it..
21048 this.headerEl.addClass('p-0');
21049 return this.headerEl
21053 getAutoCreate : function(){
21056 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21057 style: 'display:block',
21063 cls : 'popover-inner ',
21067 cls: 'popover-title popover-header',
21068 html : this.title === false ? '' : this.title
21071 cls : 'popover-content popover-body ' + (this.cls || ''),
21072 html : this.html || ''
21083 * @param {string} the title
21085 setTitle: function(str)
21089 this.headerEl.dom.innerHTML = str;
21094 * @param {string} the body content
21096 setContent: function(str)
21099 if (this.contentEl) {
21100 this.contentEl.dom.innerHTML = str;
21104 // as it get's added to the bottom of the page.
21105 onRender : function(ct, position)
21107 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21112 var cfg = Roo.apply({}, this.getAutoCreate());
21116 cfg.cls += ' ' + this.cls;
21119 cfg.style = this.style;
21121 //Roo.log("adding to ");
21122 this.el = Roo.get(document.body).createChild(cfg, position);
21123 // Roo.log(this.el);
21126 this.contentEl = this.el.select('.popover-content',true).first();
21127 this.headerEl = this.el.select('.popover-title',true).first();
21130 if(typeof(this.items) != 'undefined'){
21131 var items = this.items;
21134 for(var i =0;i < items.length;i++) {
21135 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21139 this.items = nitems;
21141 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21142 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21149 resizeMask : function()
21151 this.maskEl.setSize(
21152 Roo.lib.Dom.getViewWidth(true),
21153 Roo.lib.Dom.getViewHeight(true)
21157 initEvents : function()
21161 Roo.bootstrap.Popover.register(this);
21164 this.arrowEl = this.el.select('.arrow',true).first();
21165 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21166 this.el.enableDisplayMode('block');
21170 if (this.over === false && !this.parent()) {
21173 if (this.triggers === false) {
21178 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21179 var triggers = this.trigger ? this.trigger.split(' ') : [];
21180 Roo.each(triggers, function(trigger) {
21182 if (trigger == 'click') {
21183 on_el.on('click', this.toggle, this);
21184 } else if (trigger != 'manual') {
21185 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
21186 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21188 on_el.on(eventIn ,this.enter, this);
21189 on_el.on(eventOut, this.leave, this);
21199 toggle : function () {
21200 this.hoverState == 'in' ? this.leave() : this.enter();
21203 enter : function () {
21205 clearTimeout(this.timeout);
21207 this.hoverState = 'in';
21209 if (!this.delay || !this.delay.show) {
21214 this.timeout = setTimeout(function () {
21215 if (_t.hoverState == 'in') {
21218 }, this.delay.show)
21221 leave : function() {
21222 clearTimeout(this.timeout);
21224 this.hoverState = 'out';
21226 if (!this.delay || !this.delay.hide) {
21231 this.timeout = setTimeout(function () {
21232 if (_t.hoverState == 'out') {
21235 }, this.delay.hide)
21239 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21240 * @param {string} (left|right|top|bottom) position
21242 show : function (on_el, placement)
21244 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
21245 on_el = on_el || false; // default to false
21248 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21249 on_el = this.parent().el;
21250 } else if (this.over) {
21251 on_el = Roo.get(this.over);
21256 this.alignEl = Roo.get( on_el );
21259 this.render(document.body);
21265 if (this.title === false) {
21266 this.headerEl.hide();
21271 this.el.dom.style.display = 'block';
21274 if (this.alignEl) {
21275 this.updatePosition(this.placement, true);
21278 // this is usually just done by the builder = to show the popoup in the middle of the scren.
21279 var es = this.el.getSize();
21280 var x = Roo.lib.Dom.getViewWidth()/2;
21281 var y = Roo.lib.Dom.getViewHeight()/2;
21282 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
21287 //var arrow = this.el.select('.arrow',true).first();
21288 //arrow.set(align[2],
21290 this.el.addClass('in');
21294 this.hoverState = 'in';
21297 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
21298 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21299 this.maskEl.dom.style.display = 'block';
21300 this.maskEl.addClass('show');
21302 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21304 this.fireEvent('show', this);
21308 * fire this manually after loading a grid in the table for example
21309 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21310 * @param {Boolean} try and move it if we cant get right position.
21312 updatePosition : function(placement, try_move)
21314 // allow for calling with no parameters
21315 placement = placement ? placement : this.placement;
21316 try_move = typeof(try_move) == 'undefined' ? true : try_move;
21318 this.el.removeClass([
21319 'fade','top','bottom', 'left', 'right','in',
21320 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21322 this.el.addClass(placement + ' bs-popover-' + placement);
21324 if (!this.alignEl ) {
21328 switch (placement) {
21330 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21331 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21332 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21333 //normal display... or moved up/down.
21334 this.el.setXY(offset);
21335 var xy = this.alignEl.getAnchorXY('tr', false);
21337 this.arrowEl.setXY(xy);
21340 // continue through...
21341 return this.updatePosition('left', false);
21345 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21346 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21347 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21348 //normal display... or moved up/down.
21349 this.el.setXY(offset);
21350 var xy = this.alignEl.getAnchorXY('tl', false);
21351 xy[0]-=10;xy[1]+=5; // << fix me
21352 this.arrowEl.setXY(xy);
21356 return this.updatePosition('right', false);
21359 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21360 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21361 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21362 //normal display... or moved up/down.
21363 this.el.setXY(offset);
21364 var xy = this.alignEl.getAnchorXY('t', false);
21365 xy[1]-=10; // << fix me
21366 this.arrowEl.setXY(xy);
21370 return this.updatePosition('bottom', false);
21373 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21374 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21375 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21376 //normal display... or moved up/down.
21377 this.el.setXY(offset);
21378 var xy = this.alignEl.getAnchorXY('b', false);
21379 xy[1]+=2; // << fix me
21380 this.arrowEl.setXY(xy);
21384 return this.updatePosition('top', false);
21395 this.el.setXY([0,0]);
21396 this.el.removeClass('in');
21398 this.hoverState = null;
21399 this.maskEl.hide(); // always..
21400 this.fireEvent('hide', this);
21406 Roo.apply(Roo.bootstrap.Popover, {
21409 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21410 'right' : ['l-br', [10,0], 'right bs-popover-right'],
21411 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21412 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21417 clickHander : false,
21421 onMouseDown : function(e)
21423 if (this.popups.length && !e.getTarget(".roo-popover")) {
21424 /// what is nothing is showing..
21433 register : function(popup)
21435 if (!Roo.bootstrap.Popover.clickHandler) {
21436 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21438 // hide other popups.
21439 popup.on('show', Roo.bootstrap.Popover.onShow, popup);
21440 popup.on('hide', Roo.bootstrap.Popover.onHide, popup);
21441 this.hideAll(); //<< why?
21442 //this.popups.push(popup);
21444 hideAll : function()
21446 this.popups.forEach(function(p) {
21450 onShow : function() {
21451 Roo.bootstrap.Popover.popups.push(this);
21453 onHide : function() {
21454 Roo.bootstrap.Popover.popups.remove(this);
21460 * Card header - holder for the card header elements.
21465 * @class Roo.bootstrap.PopoverNav
21466 * @extends Roo.bootstrap.NavGroup
21467 * Bootstrap Popover header navigation class
21469 * Create a new Popover Header Navigation
21470 * @param {Object} config The config object
21473 Roo.bootstrap.PopoverNav = function(config){
21474 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
21477 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
21480 container_method : 'getPopoverHeader'
21498 * @class Roo.bootstrap.Progress
21499 * @extends Roo.bootstrap.Component
21500 * Bootstrap Progress class
21501 * @cfg {Boolean} striped striped of the progress bar
21502 * @cfg {Boolean} active animated of the progress bar
21506 * Create a new Progress
21507 * @param {Object} config The config object
21510 Roo.bootstrap.Progress = function(config){
21511 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
21514 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
21519 getAutoCreate : function(){
21527 cfg.cls += ' progress-striped';
21531 cfg.cls += ' active';
21550 * @class Roo.bootstrap.ProgressBar
21551 * @extends Roo.bootstrap.Component
21552 * Bootstrap ProgressBar class
21553 * @cfg {Number} aria_valuenow aria-value now
21554 * @cfg {Number} aria_valuemin aria-value min
21555 * @cfg {Number} aria_valuemax aria-value max
21556 * @cfg {String} label label for the progress bar
21557 * @cfg {String} panel (success | info | warning | danger )
21558 * @cfg {String} role role of the progress bar
21559 * @cfg {String} sr_only text
21563 * Create a new ProgressBar
21564 * @param {Object} config The config object
21567 Roo.bootstrap.ProgressBar = function(config){
21568 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
21571 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
21575 aria_valuemax : 100,
21581 getAutoCreate : function()
21586 cls: 'progress-bar',
21587 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
21599 cfg.role = this.role;
21602 if(this.aria_valuenow){
21603 cfg['aria-valuenow'] = this.aria_valuenow;
21606 if(this.aria_valuemin){
21607 cfg['aria-valuemin'] = this.aria_valuemin;
21610 if(this.aria_valuemax){
21611 cfg['aria-valuemax'] = this.aria_valuemax;
21614 if(this.label && !this.sr_only){
21615 cfg.html = this.label;
21619 cfg.cls += ' progress-bar-' + this.panel;
21625 update : function(aria_valuenow)
21627 this.aria_valuenow = aria_valuenow;
21629 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
21644 * @class Roo.bootstrap.TabGroup
21645 * @extends Roo.bootstrap.Column
21646 * Bootstrap Column class
21647 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
21648 * @cfg {Boolean} carousel true to make the group behave like a carousel
21649 * @cfg {Boolean} bullets show bullets for the panels
21650 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
21651 * @cfg {Number} timer auto slide timer .. default 0 millisecond
21652 * @cfg {Boolean} showarrow (true|false) show arrow default true
21655 * Create a new TabGroup
21656 * @param {Object} config The config object
21659 Roo.bootstrap.TabGroup = function(config){
21660 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
21662 this.navId = Roo.id();
21665 Roo.bootstrap.TabGroup.register(this);
21669 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
21672 transition : false,
21677 slideOnTouch : false,
21680 getAutoCreate : function()
21682 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
21684 cfg.cls += ' tab-content';
21686 if (this.carousel) {
21687 cfg.cls += ' carousel slide';
21690 cls : 'carousel-inner',
21694 if(this.bullets && !Roo.isTouch){
21697 cls : 'carousel-bullets',
21701 if(this.bullets_cls){
21702 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
21709 cfg.cn[0].cn.push(bullets);
21712 if(this.showarrow){
21713 cfg.cn[0].cn.push({
21715 class : 'carousel-arrow',
21719 class : 'carousel-prev',
21723 class : 'fa fa-chevron-left'
21729 class : 'carousel-next',
21733 class : 'fa fa-chevron-right'
21746 initEvents: function()
21748 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
21749 // this.el.on("touchstart", this.onTouchStart, this);
21752 if(this.autoslide){
21755 this.slideFn = window.setInterval(function() {
21756 _this.showPanelNext();
21760 if(this.showarrow){
21761 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
21762 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
21768 // onTouchStart : function(e, el, o)
21770 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
21774 // this.showPanelNext();
21778 getChildContainer : function()
21780 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
21784 * register a Navigation item
21785 * @param {Roo.bootstrap.NavItem} the navitem to add
21787 register : function(item)
21789 this.tabs.push( item);
21790 item.navId = this.navId; // not really needed..
21795 getActivePanel : function()
21798 Roo.each(this.tabs, function(t) {
21808 getPanelByName : function(n)
21811 Roo.each(this.tabs, function(t) {
21812 if (t.tabId == n) {
21820 indexOfPanel : function(p)
21823 Roo.each(this.tabs, function(t,i) {
21824 if (t.tabId == p.tabId) {
21833 * show a specific panel
21834 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
21835 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
21837 showPanel : function (pan)
21839 if(this.transition || typeof(pan) == 'undefined'){
21840 Roo.log("waiting for the transitionend");
21844 if (typeof(pan) == 'number') {
21845 pan = this.tabs[pan];
21848 if (typeof(pan) == 'string') {
21849 pan = this.getPanelByName(pan);
21852 var cur = this.getActivePanel();
21855 Roo.log('pan or acitve pan is undefined');
21859 if (pan.tabId == this.getActivePanel().tabId) {
21863 if (false === cur.fireEvent('beforedeactivate')) {
21867 if(this.bullets > 0 && !Roo.isTouch){
21868 this.setActiveBullet(this.indexOfPanel(pan));
21871 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
21873 //class="carousel-item carousel-item-next carousel-item-left"
21875 this.transition = true;
21876 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
21877 var lr = dir == 'next' ? 'left' : 'right';
21878 pan.el.addClass(dir); // or prev
21879 pan.el.addClass('carousel-item-' + dir); // or prev
21880 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
21881 cur.el.addClass(lr); // or right
21882 pan.el.addClass(lr);
21883 cur.el.addClass('carousel-item-' +lr); // or right
21884 pan.el.addClass('carousel-item-' +lr);
21888 cur.el.on('transitionend', function() {
21889 Roo.log("trans end?");
21891 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
21892 pan.setActive(true);
21894 cur.el.removeClass([lr, 'carousel-item-' + lr]);
21895 cur.setActive(false);
21897 _this.transition = false;
21899 }, this, { single: true } );
21904 cur.setActive(false);
21905 pan.setActive(true);
21910 showPanelNext : function()
21912 var i = this.indexOfPanel(this.getActivePanel());
21914 if (i >= this.tabs.length - 1 && !this.autoslide) {
21918 if (i >= this.tabs.length - 1 && this.autoslide) {
21922 this.showPanel(this.tabs[i+1]);
21925 showPanelPrev : function()
21927 var i = this.indexOfPanel(this.getActivePanel());
21929 if (i < 1 && !this.autoslide) {
21933 if (i < 1 && this.autoslide) {
21934 i = this.tabs.length;
21937 this.showPanel(this.tabs[i-1]);
21941 addBullet: function()
21943 if(!this.bullets || Roo.isTouch){
21946 var ctr = this.el.select('.carousel-bullets',true).first();
21947 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
21948 var bullet = ctr.createChild({
21949 cls : 'bullet bullet-' + i
21950 },ctr.dom.lastChild);
21955 bullet.on('click', (function(e, el, o, ii, t){
21957 e.preventDefault();
21959 this.showPanel(ii);
21961 if(this.autoslide && this.slideFn){
21962 clearInterval(this.slideFn);
21963 this.slideFn = window.setInterval(function() {
21964 _this.showPanelNext();
21968 }).createDelegate(this, [i, bullet], true));
21973 setActiveBullet : function(i)
21979 Roo.each(this.el.select('.bullet', true).elements, function(el){
21980 el.removeClass('selected');
21983 var bullet = this.el.select('.bullet-' + i, true).first();
21989 bullet.addClass('selected');
22000 Roo.apply(Roo.bootstrap.TabGroup, {
22004 * register a Navigation Group
22005 * @param {Roo.bootstrap.NavGroup} the navgroup to add
22007 register : function(navgrp)
22009 this.groups[navgrp.navId] = navgrp;
22013 * fetch a Navigation Group based on the navigation ID
22014 * if one does not exist , it will get created.
22015 * @param {string} the navgroup to add
22016 * @returns {Roo.bootstrap.NavGroup} the navgroup
22018 get: function(navId) {
22019 if (typeof(this.groups[navId]) == 'undefined') {
22020 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22022 return this.groups[navId] ;
22037 * @class Roo.bootstrap.TabPanel
22038 * @extends Roo.bootstrap.Component
22039 * Bootstrap TabPanel class
22040 * @cfg {Boolean} active panel active
22041 * @cfg {String} html panel content
22042 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22043 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
22044 * @cfg {String} href click to link..
22045 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22049 * Create a new TabPanel
22050 * @param {Object} config The config object
22053 Roo.bootstrap.TabPanel = function(config){
22054 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22058 * Fires when the active status changes
22059 * @param {Roo.bootstrap.TabPanel} this
22060 * @param {Boolean} state the new state
22065 * @event beforedeactivate
22066 * Fires before a tab is de-activated - can be used to do validation on a form.
22067 * @param {Roo.bootstrap.TabPanel} this
22068 * @return {Boolean} false if there is an error
22071 'beforedeactivate': true
22074 this.tabId = this.tabId || Roo.id();
22078 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
22085 touchSlide : false,
22086 getAutoCreate : function(){
22091 // item is needed for carousel - not sure if it has any effect otherwise
22092 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22093 html: this.html || ''
22097 cfg.cls += ' active';
22101 cfg.tabId = this.tabId;
22109 initEvents: function()
22111 var p = this.parent();
22113 this.navId = this.navId || p.navId;
22115 if (typeof(this.navId) != 'undefined') {
22116 // not really needed.. but just in case.. parent should be a NavGroup.
22117 var tg = Roo.bootstrap.TabGroup.get(this.navId);
22121 var i = tg.tabs.length - 1;
22123 if(this.active && tg.bullets > 0 && i < tg.bullets){
22124 tg.setActiveBullet(i);
22128 this.el.on('click', this.onClick, this);
22130 if(Roo.isTouch && this.touchSlide){
22131 this.el.on("touchstart", this.onTouchStart, this);
22132 this.el.on("touchmove", this.onTouchMove, this);
22133 this.el.on("touchend", this.onTouchEnd, this);
22138 onRender : function(ct, position)
22140 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22143 setActive : function(state)
22145 Roo.log("panel - set active " + this.tabId + "=" + state);
22147 this.active = state;
22149 this.el.removeClass('active');
22151 } else if (!this.el.hasClass('active')) {
22152 this.el.addClass('active');
22155 this.fireEvent('changed', this, state);
22158 onClick : function(e)
22160 e.preventDefault();
22162 if(!this.href.length){
22166 window.location.href = this.href;
22175 onTouchStart : function(e)
22177 this.swiping = false;
22179 this.startX = e.browserEvent.touches[0].clientX;
22180 this.startY = e.browserEvent.touches[0].clientY;
22183 onTouchMove : function(e)
22185 this.swiping = true;
22187 this.endX = e.browserEvent.touches[0].clientX;
22188 this.endY = e.browserEvent.touches[0].clientY;
22191 onTouchEnd : function(e)
22198 var tabGroup = this.parent();
22200 if(this.endX > this.startX){ // swiping right
22201 tabGroup.showPanelPrev();
22205 if(this.startX > this.endX){ // swiping left
22206 tabGroup.showPanelNext();
22225 * @class Roo.bootstrap.DateField
22226 * @extends Roo.bootstrap.Input
22227 * Bootstrap DateField class
22228 * @cfg {Number} weekStart default 0
22229 * @cfg {String} viewMode default empty, (months|years)
22230 * @cfg {String} minViewMode default empty, (months|years)
22231 * @cfg {Number} startDate default -Infinity
22232 * @cfg {Number} endDate default Infinity
22233 * @cfg {Boolean} todayHighlight default false
22234 * @cfg {Boolean} todayBtn default false
22235 * @cfg {Boolean} calendarWeeks default false
22236 * @cfg {Object} daysOfWeekDisabled default empty
22237 * @cfg {Boolean} singleMode default false (true | false)
22239 * @cfg {Boolean} keyboardNavigation default true
22240 * @cfg {String} language default en
22243 * Create a new DateField
22244 * @param {Object} config The config object
22247 Roo.bootstrap.DateField = function(config){
22248 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
22252 * Fires when this field show.
22253 * @param {Roo.bootstrap.DateField} this
22254 * @param {Mixed} date The date value
22259 * Fires when this field hide.
22260 * @param {Roo.bootstrap.DateField} this
22261 * @param {Mixed} date The date value
22266 * Fires when select a date.
22267 * @param {Roo.bootstrap.DateField} this
22268 * @param {Mixed} date The date value
22272 * @event beforeselect
22273 * Fires when before select a date.
22274 * @param {Roo.bootstrap.DateField} this
22275 * @param {Mixed} date The date value
22277 beforeselect : true
22281 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
22284 * @cfg {String} format
22285 * The default date format string which can be overriden for localization support. The format must be
22286 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22290 * @cfg {String} altFormats
22291 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22292 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22294 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22302 todayHighlight : false,
22308 keyboardNavigation: true,
22310 calendarWeeks: false,
22312 startDate: -Infinity,
22316 daysOfWeekDisabled: [],
22320 singleMode : false,
22322 UTCDate: function()
22324 return new Date(Date.UTC.apply(Date, arguments));
22327 UTCToday: function()
22329 var today = new Date();
22330 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22333 getDate: function() {
22334 var d = this.getUTCDate();
22335 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22338 getUTCDate: function() {
22342 setDate: function(d) {
22343 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22346 setUTCDate: function(d) {
22348 this.setValue(this.formatDate(this.date));
22351 onRender: function(ct, position)
22354 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
22356 this.language = this.language || 'en';
22357 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
22358 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
22360 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
22361 this.format = this.format || 'm/d/y';
22362 this.isInline = false;
22363 this.isInput = true;
22364 this.component = this.el.select('.add-on', true).first() || false;
22365 this.component = (this.component && this.component.length === 0) ? false : this.component;
22366 this.hasInput = this.component && this.inputEl().length;
22368 if (typeof(this.minViewMode === 'string')) {
22369 switch (this.minViewMode) {
22371 this.minViewMode = 1;
22374 this.minViewMode = 2;
22377 this.minViewMode = 0;
22382 if (typeof(this.viewMode === 'string')) {
22383 switch (this.viewMode) {
22396 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
22398 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
22400 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22402 this.picker().on('mousedown', this.onMousedown, this);
22403 this.picker().on('click', this.onClick, this);
22405 this.picker().addClass('datepicker-dropdown');
22407 this.startViewMode = this.viewMode;
22409 if(this.singleMode){
22410 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22411 v.setVisibilityMode(Roo.Element.DISPLAY);
22415 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22416 v.setStyle('width', '189px');
22420 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22421 if(!this.calendarWeeks){
22426 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22427 v.attr('colspan', function(i, val){
22428 return parseInt(val) + 1;
22433 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22435 this.setStartDate(this.startDate);
22436 this.setEndDate(this.endDate);
22438 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22445 if(this.isInline) {
22450 picker : function()
22452 return this.pickerEl;
22453 // return this.el.select('.datepicker', true).first();
22456 fillDow: function()
22458 var dowCnt = this.weekStart;
22467 if(this.calendarWeeks){
22475 while (dowCnt < this.weekStart + 7) {
22479 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
22483 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
22486 fillMonths: function()
22489 var months = this.picker().select('>.datepicker-months td', true).first();
22491 months.dom.innerHTML = '';
22497 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
22500 months.createChild(month);
22507 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;
22509 if (this.date < this.startDate) {
22510 this.viewDate = new Date(this.startDate);
22511 } else if (this.date > this.endDate) {
22512 this.viewDate = new Date(this.endDate);
22514 this.viewDate = new Date(this.date);
22522 var d = new Date(this.viewDate),
22523 year = d.getUTCFullYear(),
22524 month = d.getUTCMonth(),
22525 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
22526 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
22527 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
22528 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
22529 currentDate = this.date && this.date.valueOf(),
22530 today = this.UTCToday();
22532 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
22534 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22536 // this.picker.select('>tfoot th.today').
22537 // .text(dates[this.language].today)
22538 // .toggle(this.todayBtn !== false);
22540 this.updateNavArrows();
22543 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
22545 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
22547 prevMonth.setUTCDate(day);
22549 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
22551 var nextMonth = new Date(prevMonth);
22553 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
22555 nextMonth = nextMonth.valueOf();
22557 var fillMonths = false;
22559 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
22561 while(prevMonth.valueOf() <= nextMonth) {
22564 if (prevMonth.getUTCDay() === this.weekStart) {
22566 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
22574 if(this.calendarWeeks){
22575 // ISO 8601: First week contains first thursday.
22576 // ISO also states week starts on Monday, but we can be more abstract here.
22578 // Start of current week: based on weekstart/current date
22579 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
22580 // Thursday of this week
22581 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
22582 // First Thursday of year, year from thursday
22583 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
22584 // Calendar week: ms between thursdays, div ms per day, div 7 days
22585 calWeek = (th - yth) / 864e5 / 7 + 1;
22587 fillMonths.cn.push({
22595 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
22597 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
22600 if (this.todayHighlight &&
22601 prevMonth.getUTCFullYear() == today.getFullYear() &&
22602 prevMonth.getUTCMonth() == today.getMonth() &&
22603 prevMonth.getUTCDate() == today.getDate()) {
22604 clsName += ' today';
22607 if (currentDate && prevMonth.valueOf() === currentDate) {
22608 clsName += ' active';
22611 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
22612 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
22613 clsName += ' disabled';
22616 fillMonths.cn.push({
22618 cls: 'day ' + clsName,
22619 html: prevMonth.getDate()
22622 prevMonth.setDate(prevMonth.getDate()+1);
22625 var currentYear = this.date && this.date.getUTCFullYear();
22626 var currentMonth = this.date && this.date.getUTCMonth();
22628 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
22630 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
22631 v.removeClass('active');
22633 if(currentYear === year && k === currentMonth){
22634 v.addClass('active');
22637 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
22638 v.addClass('disabled');
22644 year = parseInt(year/10, 10) * 10;
22646 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
22648 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
22651 for (var i = -1; i < 11; i++) {
22652 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
22654 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
22662 showMode: function(dir)
22665 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
22668 Roo.each(this.picker().select('>div',true).elements, function(v){
22669 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22672 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
22677 if(this.isInline) {
22681 this.picker().removeClass(['bottom', 'top']);
22683 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22685 * place to the top of element!
22689 this.picker().addClass('top');
22690 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22695 this.picker().addClass('bottom');
22697 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22700 parseDate : function(value)
22702 if(!value || value instanceof Date){
22705 var v = Date.parseDate(value, this.format);
22706 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
22707 v = Date.parseDate(value, 'Y-m-d');
22709 if(!v && this.altFormats){
22710 if(!this.altFormatsArray){
22711 this.altFormatsArray = this.altFormats.split("|");
22713 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22714 v = Date.parseDate(value, this.altFormatsArray[i]);
22720 formatDate : function(date, fmt)
22722 return (!date || !(date instanceof Date)) ?
22723 date : date.dateFormat(fmt || this.format);
22726 onFocus : function()
22728 Roo.bootstrap.DateField.superclass.onFocus.call(this);
22732 onBlur : function()
22734 Roo.bootstrap.DateField.superclass.onBlur.call(this);
22736 var d = this.inputEl().getValue();
22743 showPopup : function()
22745 this.picker().show();
22749 this.fireEvent('showpopup', this, this.date);
22752 hidePopup : function()
22754 if(this.isInline) {
22757 this.picker().hide();
22758 this.viewMode = this.startViewMode;
22761 this.fireEvent('hidepopup', this, this.date);
22765 onMousedown: function(e)
22767 e.stopPropagation();
22768 e.preventDefault();
22773 Roo.bootstrap.DateField.superclass.keyup.call(this);
22777 setValue: function(v)
22779 if(this.fireEvent('beforeselect', this, v) !== false){
22780 var d = new Date(this.parseDate(v) ).clearTime();
22782 if(isNaN(d.getTime())){
22783 this.date = this.viewDate = '';
22784 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22788 v = this.formatDate(d);
22790 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
22792 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
22796 this.fireEvent('select', this, this.date);
22800 getValue: function()
22802 return this.formatDate(this.date);
22805 fireKey: function(e)
22807 if (!this.picker().isVisible()){
22808 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22814 var dateChanged = false,
22816 newDate, newViewDate;
22821 e.preventDefault();
22825 if (!this.keyboardNavigation) {
22828 dir = e.keyCode == 37 ? -1 : 1;
22831 newDate = this.moveYear(this.date, dir);
22832 newViewDate = this.moveYear(this.viewDate, dir);
22833 } else if (e.shiftKey){
22834 newDate = this.moveMonth(this.date, dir);
22835 newViewDate = this.moveMonth(this.viewDate, dir);
22837 newDate = new Date(this.date);
22838 newDate.setUTCDate(this.date.getUTCDate() + dir);
22839 newViewDate = new Date(this.viewDate);
22840 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
22842 if (this.dateWithinRange(newDate)){
22843 this.date = newDate;
22844 this.viewDate = newViewDate;
22845 this.setValue(this.formatDate(this.date));
22847 e.preventDefault();
22848 dateChanged = true;
22853 if (!this.keyboardNavigation) {
22856 dir = e.keyCode == 38 ? -1 : 1;
22858 newDate = this.moveYear(this.date, dir);
22859 newViewDate = this.moveYear(this.viewDate, dir);
22860 } else if (e.shiftKey){
22861 newDate = this.moveMonth(this.date, dir);
22862 newViewDate = this.moveMonth(this.viewDate, dir);
22864 newDate = new Date(this.date);
22865 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
22866 newViewDate = new Date(this.viewDate);
22867 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
22869 if (this.dateWithinRange(newDate)){
22870 this.date = newDate;
22871 this.viewDate = newViewDate;
22872 this.setValue(this.formatDate(this.date));
22874 e.preventDefault();
22875 dateChanged = true;
22879 this.setValue(this.formatDate(this.date));
22881 e.preventDefault();
22884 this.setValue(this.formatDate(this.date));
22898 onClick: function(e)
22900 e.stopPropagation();
22901 e.preventDefault();
22903 var target = e.getTarget();
22905 if(target.nodeName.toLowerCase() === 'i'){
22906 target = Roo.get(target).dom.parentNode;
22909 var nodeName = target.nodeName;
22910 var className = target.className;
22911 var html = target.innerHTML;
22912 //Roo.log(nodeName);
22914 switch(nodeName.toLowerCase()) {
22916 switch(className) {
22922 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
22923 switch(this.viewMode){
22925 this.viewDate = this.moveMonth(this.viewDate, dir);
22929 this.viewDate = this.moveYear(this.viewDate, dir);
22935 var date = new Date();
22936 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
22938 this.setValue(this.formatDate(this.date));
22945 if (className.indexOf('disabled') < 0) {
22946 if (!this.viewDate) {
22947 this.viewDate = new Date();
22949 this.viewDate.setUTCDate(1);
22950 if (className.indexOf('month') > -1) {
22951 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
22953 var year = parseInt(html, 10) || 0;
22954 this.viewDate.setUTCFullYear(year);
22958 if(this.singleMode){
22959 this.setValue(this.formatDate(this.viewDate));
22970 //Roo.log(className);
22971 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
22972 var day = parseInt(html, 10) || 1;
22973 var year = (this.viewDate || new Date()).getUTCFullYear(),
22974 month = (this.viewDate || new Date()).getUTCMonth();
22976 if (className.indexOf('old') > -1) {
22983 } else if (className.indexOf('new') > -1) {
22991 //Roo.log([year,month,day]);
22992 this.date = this.UTCDate(year, month, day,0,0,0,0);
22993 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
22995 //Roo.log(this.formatDate(this.date));
22996 this.setValue(this.formatDate(this.date));
23003 setStartDate: function(startDate)
23005 this.startDate = startDate || -Infinity;
23006 if (this.startDate !== -Infinity) {
23007 this.startDate = this.parseDate(this.startDate);
23010 this.updateNavArrows();
23013 setEndDate: function(endDate)
23015 this.endDate = endDate || Infinity;
23016 if (this.endDate !== Infinity) {
23017 this.endDate = this.parseDate(this.endDate);
23020 this.updateNavArrows();
23023 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23025 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23026 if (typeof(this.daysOfWeekDisabled) !== 'object') {
23027 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23029 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23030 return parseInt(d, 10);
23033 this.updateNavArrows();
23036 updateNavArrows: function()
23038 if(this.singleMode){
23042 var d = new Date(this.viewDate),
23043 year = d.getUTCFullYear(),
23044 month = d.getUTCMonth();
23046 Roo.each(this.picker().select('.prev', true).elements, function(v){
23048 switch (this.viewMode) {
23051 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23057 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23064 Roo.each(this.picker().select('.next', true).elements, function(v){
23066 switch (this.viewMode) {
23069 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23075 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23083 moveMonth: function(date, dir)
23088 var new_date = new Date(date.valueOf()),
23089 day = new_date.getUTCDate(),
23090 month = new_date.getUTCMonth(),
23091 mag = Math.abs(dir),
23093 dir = dir > 0 ? 1 : -1;
23096 // If going back one month, make sure month is not current month
23097 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23099 return new_date.getUTCMonth() == month;
23101 // If going forward one month, make sure month is as expected
23102 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23104 return new_date.getUTCMonth() != new_month;
23106 new_month = month + dir;
23107 new_date.setUTCMonth(new_month);
23108 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23109 if (new_month < 0 || new_month > 11) {
23110 new_month = (new_month + 12) % 12;
23113 // For magnitudes >1, move one month at a time...
23114 for (var i=0; i<mag; i++) {
23115 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23116 new_date = this.moveMonth(new_date, dir);
23118 // ...then reset the day, keeping it in the new month
23119 new_month = new_date.getUTCMonth();
23120 new_date.setUTCDate(day);
23122 return new_month != new_date.getUTCMonth();
23125 // Common date-resetting loop -- if date is beyond end of month, make it
23128 new_date.setUTCDate(--day);
23129 new_date.setUTCMonth(new_month);
23134 moveYear: function(date, dir)
23136 return this.moveMonth(date, dir*12);
23139 dateWithinRange: function(date)
23141 return date >= this.startDate && date <= this.endDate;
23147 this.picker().remove();
23150 validateValue : function(value)
23152 if(this.getVisibilityEl().hasClass('hidden')){
23156 if(value.length < 1) {
23157 if(this.allowBlank){
23163 if(value.length < this.minLength){
23166 if(value.length > this.maxLength){
23170 var vt = Roo.form.VTypes;
23171 if(!vt[this.vtype](value, this)){
23175 if(typeof this.validator == "function"){
23176 var msg = this.validator(value);
23182 if(this.regex && !this.regex.test(value)){
23186 if(typeof(this.parseDate(value)) == 'undefined'){
23190 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23194 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23204 this.date = this.viewDate = '';
23206 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
23211 Roo.apply(Roo.bootstrap.DateField, {
23222 html: '<i class="fa fa-arrow-left"/>'
23232 html: '<i class="fa fa-arrow-right"/>'
23274 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23275 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23276 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23277 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23278 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23291 navFnc: 'FullYear',
23296 navFnc: 'FullYear',
23301 Roo.apply(Roo.bootstrap.DateField, {
23305 cls: 'datepicker dropdown-menu roo-dynamic shadow',
23309 cls: 'datepicker-days',
23313 cls: 'table-condensed',
23315 Roo.bootstrap.DateField.head,
23319 Roo.bootstrap.DateField.footer
23326 cls: 'datepicker-months',
23330 cls: 'table-condensed',
23332 Roo.bootstrap.DateField.head,
23333 Roo.bootstrap.DateField.content,
23334 Roo.bootstrap.DateField.footer
23341 cls: 'datepicker-years',
23345 cls: 'table-condensed',
23347 Roo.bootstrap.DateField.head,
23348 Roo.bootstrap.DateField.content,
23349 Roo.bootstrap.DateField.footer
23368 * @class Roo.bootstrap.TimeField
23369 * @extends Roo.bootstrap.Input
23370 * Bootstrap DateField class
23374 * Create a new TimeField
23375 * @param {Object} config The config object
23378 Roo.bootstrap.TimeField = function(config){
23379 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
23383 * Fires when this field show.
23384 * @param {Roo.bootstrap.DateField} thisthis
23385 * @param {Mixed} date The date value
23390 * Fires when this field hide.
23391 * @param {Roo.bootstrap.DateField} this
23392 * @param {Mixed} date The date value
23397 * Fires when select a date.
23398 * @param {Roo.bootstrap.DateField} this
23399 * @param {Mixed} date The date value
23405 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
23408 * @cfg {String} format
23409 * The default time format string which can be overriden for localization support. The format must be
23410 * valid according to {@link Date#parseDate} (defaults to 'H:i').
23414 getAutoCreate : function()
23416 this.after = '<i class="fa far fa-clock"></i>';
23417 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
23421 onRender: function(ct, position)
23424 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
23426 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
23428 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23430 this.pop = this.picker().select('>.datepicker-time',true).first();
23431 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23433 this.picker().on('mousedown', this.onMousedown, this);
23434 this.picker().on('click', this.onClick, this);
23436 this.picker().addClass('datepicker-dropdown');
23441 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23442 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23443 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23444 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23445 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23446 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23450 fireKey: function(e){
23451 if (!this.picker().isVisible()){
23452 if (e.keyCode == 27) { // allow escape to hide and re-show picker
23458 e.preventDefault();
23466 this.onTogglePeriod();
23469 this.onIncrementMinutes();
23472 this.onDecrementMinutes();
23481 onClick: function(e) {
23482 e.stopPropagation();
23483 e.preventDefault();
23486 picker : function()
23488 return this.pickerEl;
23491 fillTime: function()
23493 var time = this.pop.select('tbody', true).first();
23495 time.dom.innerHTML = '';
23510 cls: 'hours-up fa fas fa-chevron-up'
23530 cls: 'minutes-up fa fas fa-chevron-up'
23551 cls: 'timepicker-hour',
23566 cls: 'timepicker-minute',
23581 cls: 'btn btn-primary period',
23603 cls: 'hours-down fa fas fa-chevron-down'
23623 cls: 'minutes-down fa fas fa-chevron-down'
23641 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
23648 var hours = this.time.getHours();
23649 var minutes = this.time.getMinutes();
23662 hours = hours - 12;
23666 hours = '0' + hours;
23670 minutes = '0' + minutes;
23673 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
23674 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
23675 this.pop.select('button', true).first().dom.innerHTML = period;
23681 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
23683 var cls = ['bottom'];
23685 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
23692 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
23696 //this.picker().setXY(20000,20000);
23697 this.picker().addClass(cls.join('-'));
23701 Roo.each(cls, function(c){
23706 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
23707 //_this.picker().setTop(_this.inputEl().getHeight());
23711 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
23713 //_this.picker().setTop(0 - _this.picker().getHeight());
23718 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
23722 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
23730 onFocus : function()
23732 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
23736 onBlur : function()
23738 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
23744 this.picker().show();
23749 this.fireEvent('show', this, this.date);
23754 this.picker().hide();
23757 this.fireEvent('hide', this, this.date);
23760 setTime : function()
23763 this.setValue(this.time.format(this.format));
23765 this.fireEvent('select', this, this.date);
23770 onMousedown: function(e){
23771 e.stopPropagation();
23772 e.preventDefault();
23775 onIncrementHours: function()
23777 Roo.log('onIncrementHours');
23778 this.time = this.time.add(Date.HOUR, 1);
23783 onDecrementHours: function()
23785 Roo.log('onDecrementHours');
23786 this.time = this.time.add(Date.HOUR, -1);
23790 onIncrementMinutes: function()
23792 Roo.log('onIncrementMinutes');
23793 this.time = this.time.add(Date.MINUTE, 1);
23797 onDecrementMinutes: function()
23799 Roo.log('onDecrementMinutes');
23800 this.time = this.time.add(Date.MINUTE, -1);
23804 onTogglePeriod: function()
23806 Roo.log('onTogglePeriod');
23807 this.time = this.time.add(Date.HOUR, 12);
23815 Roo.apply(Roo.bootstrap.TimeField, {
23819 cls: 'datepicker dropdown-menu',
23823 cls: 'datepicker-time',
23827 cls: 'table-condensed',
23856 cls: 'btn btn-info ok',
23884 * @class Roo.bootstrap.MonthField
23885 * @extends Roo.bootstrap.Input
23886 * Bootstrap MonthField class
23888 * @cfg {String} language default en
23891 * Create a new MonthField
23892 * @param {Object} config The config object
23895 Roo.bootstrap.MonthField = function(config){
23896 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
23901 * Fires when this field show.
23902 * @param {Roo.bootstrap.MonthField} this
23903 * @param {Mixed} date The date value
23908 * Fires when this field hide.
23909 * @param {Roo.bootstrap.MonthField} this
23910 * @param {Mixed} date The date value
23915 * Fires when select a date.
23916 * @param {Roo.bootstrap.MonthField} this
23917 * @param {String} oldvalue The old value
23918 * @param {String} newvalue The new value
23924 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
23926 onRender: function(ct, position)
23929 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
23931 this.language = this.language || 'en';
23932 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
23933 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
23935 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
23936 this.isInline = false;
23937 this.isInput = true;
23938 this.component = this.el.select('.add-on', true).first() || false;
23939 this.component = (this.component && this.component.length === 0) ? false : this.component;
23940 this.hasInput = this.component && this.inputEL().length;
23942 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
23944 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23946 this.picker().on('mousedown', this.onMousedown, this);
23947 this.picker().on('click', this.onClick, this);
23949 this.picker().addClass('datepicker-dropdown');
23951 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
23952 v.setStyle('width', '189px');
23959 if(this.isInline) {
23965 setValue: function(v, suppressEvent)
23967 var o = this.getValue();
23969 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
23973 if(suppressEvent !== true){
23974 this.fireEvent('select', this, o, v);
23979 getValue: function()
23984 onClick: function(e)
23986 e.stopPropagation();
23987 e.preventDefault();
23989 var target = e.getTarget();
23991 if(target.nodeName.toLowerCase() === 'i'){
23992 target = Roo.get(target).dom.parentNode;
23995 var nodeName = target.nodeName;
23996 var className = target.className;
23997 var html = target.innerHTML;
23999 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24003 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
24005 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24011 picker : function()
24013 return this.pickerEl;
24016 fillMonths: function()
24019 var months = this.picker().select('>.datepicker-months td', true).first();
24021 months.dom.innerHTML = '';
24027 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
24030 months.createChild(month);
24039 if(typeof(this.vIndex) == 'undefined' && this.value.length){
24040 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
24043 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24044 e.removeClass('active');
24046 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24047 e.addClass('active');
24054 if(this.isInline) {
24058 this.picker().removeClass(['bottom', 'top']);
24060 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24062 * place to the top of element!
24066 this.picker().addClass('top');
24067 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24072 this.picker().addClass('bottom');
24074 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24077 onFocus : function()
24079 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
24083 onBlur : function()
24085 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
24087 var d = this.inputEl().getValue();
24096 this.picker().show();
24097 this.picker().select('>.datepicker-months', true).first().show();
24101 this.fireEvent('show', this, this.date);
24106 if(this.isInline) {
24109 this.picker().hide();
24110 this.fireEvent('hide', this, this.date);
24114 onMousedown: function(e)
24116 e.stopPropagation();
24117 e.preventDefault();
24122 Roo.bootstrap.MonthField.superclass.keyup.call(this);
24126 fireKey: function(e)
24128 if (!this.picker().isVisible()){
24129 if (e.keyCode == 27) {// allow escape to hide and re-show picker
24140 e.preventDefault();
24144 dir = e.keyCode == 37 ? -1 : 1;
24146 this.vIndex = this.vIndex + dir;
24148 if(this.vIndex < 0){
24152 if(this.vIndex > 11){
24156 if(isNaN(this.vIndex)){
24160 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24166 dir = e.keyCode == 38 ? -1 : 1;
24168 this.vIndex = this.vIndex + dir * 4;
24170 if(this.vIndex < 0){
24174 if(this.vIndex > 11){
24178 if(isNaN(this.vIndex)){
24182 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24187 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24188 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24192 e.preventDefault();
24195 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24196 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24212 this.picker().remove();
24217 Roo.apply(Roo.bootstrap.MonthField, {
24236 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24237 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24242 Roo.apply(Roo.bootstrap.MonthField, {
24246 cls: 'datepicker dropdown-menu roo-dynamic',
24250 cls: 'datepicker-months',
24254 cls: 'table-condensed',
24256 Roo.bootstrap.DateField.content
24276 * @class Roo.bootstrap.CheckBox
24277 * @extends Roo.bootstrap.Input
24278 * Bootstrap CheckBox class
24280 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24281 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24282 * @cfg {String} boxLabel The text that appears beside the checkbox
24283 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24284 * @cfg {Boolean} checked initnal the element
24285 * @cfg {Boolean} inline inline the element (default false)
24286 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24287 * @cfg {String} tooltip label tooltip
24290 * Create a new CheckBox
24291 * @param {Object} config The config object
24294 Roo.bootstrap.CheckBox = function(config){
24295 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
24300 * Fires when the element is checked or unchecked.
24301 * @param {Roo.bootstrap.CheckBox} this This input
24302 * @param {Boolean} checked The new checked value
24307 * Fires when the element is click.
24308 * @param {Roo.bootstrap.CheckBox} this This input
24315 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
24317 inputType: 'checkbox',
24326 // checkbox success does not make any sense really..
24331 getAutoCreate : function()
24333 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24339 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24342 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
24348 type : this.inputType,
24349 value : this.inputValue,
24350 cls : 'roo-' + this.inputType, //'form-box',
24351 placeholder : this.placeholder || ''
24355 if(this.inputType != 'radio'){
24359 cls : 'roo-hidden-value',
24360 value : this.checked ? this.inputValue : this.valueOff
24365 if (this.weight) { // Validity check?
24366 cfg.cls += " " + this.inputType + "-" + this.weight;
24369 if (this.disabled) {
24370 input.disabled=true;
24374 input.checked = this.checked;
24379 input.name = this.name;
24381 if(this.inputType != 'radio'){
24382 hidden.name = this.name;
24383 input.name = '_hidden_' + this.name;
24388 input.cls += ' input-' + this.size;
24393 ['xs','sm','md','lg'].map(function(size){
24394 if (settings[size]) {
24395 cfg.cls += ' col-' + size + '-' + settings[size];
24399 var inputblock = input;
24401 if (this.before || this.after) {
24404 cls : 'input-group',
24409 inputblock.cn.push({
24411 cls : 'input-group-addon',
24416 inputblock.cn.push(input);
24418 if(this.inputType != 'radio'){
24419 inputblock.cn.push(hidden);
24423 inputblock.cn.push({
24425 cls : 'input-group-addon',
24431 var boxLabelCfg = false;
24437 //'for': id, // box label is handled by onclick - so no for...
24439 html: this.boxLabel
24442 boxLabelCfg.tooltip = this.tooltip;
24448 if (align ==='left' && this.fieldLabel.length) {
24449 // Roo.log("left and has label");
24454 cls : 'control-label',
24455 html : this.fieldLabel
24466 cfg.cn[1].cn.push(boxLabelCfg);
24469 if(this.labelWidth > 12){
24470 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
24473 if(this.labelWidth < 13 && this.labelmd == 0){
24474 this.labelmd = this.labelWidth;
24477 if(this.labellg > 0){
24478 cfg.cn[0].cls += ' col-lg-' + this.labellg;
24479 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
24482 if(this.labelmd > 0){
24483 cfg.cn[0].cls += ' col-md-' + this.labelmd;
24484 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
24487 if(this.labelsm > 0){
24488 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
24489 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
24492 if(this.labelxs > 0){
24493 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
24494 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
24497 } else if ( this.fieldLabel.length) {
24498 // Roo.log(" label");
24502 tag: this.boxLabel ? 'span' : 'label',
24504 cls: 'control-label box-input-label',
24505 //cls : 'input-group-addon',
24506 html : this.fieldLabel
24513 cfg.cn.push(boxLabelCfg);
24518 // Roo.log(" no label && no align");
24519 cfg.cn = [ inputblock ] ;
24521 cfg.cn.push(boxLabelCfg);
24529 if(this.inputType != 'radio'){
24530 cfg.cn.push(hidden);
24538 * return the real input element.
24540 inputEl: function ()
24542 return this.el.select('input.roo-' + this.inputType,true).first();
24544 hiddenEl: function ()
24546 return this.el.select('input.roo-hidden-value',true).first();
24549 labelEl: function()
24551 return this.el.select('label.control-label',true).first();
24553 /* depricated... */
24557 return this.labelEl();
24560 boxLabelEl: function()
24562 return this.el.select('label.box-label',true).first();
24565 initEvents : function()
24567 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
24569 this.inputEl().on('click', this.onClick, this);
24571 if (this.boxLabel) {
24572 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
24575 this.startValue = this.getValue();
24578 Roo.bootstrap.CheckBox.register(this);
24582 onClick : function(e)
24584 if(this.fireEvent('click', this, e) !== false){
24585 this.setChecked(!this.checked);
24590 setChecked : function(state,suppressEvent)
24592 this.startValue = this.getValue();
24594 if(this.inputType == 'radio'){
24596 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24597 e.dom.checked = false;
24600 this.inputEl().dom.checked = true;
24602 this.inputEl().dom.value = this.inputValue;
24604 if(suppressEvent !== true){
24605 this.fireEvent('check', this, true);
24613 this.checked = state;
24615 this.inputEl().dom.checked = state;
24618 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
24620 if(suppressEvent !== true){
24621 this.fireEvent('check', this, state);
24627 getValue : function()
24629 if(this.inputType == 'radio'){
24630 return this.getGroupValue();
24633 return this.hiddenEl().dom.value;
24637 getGroupValue : function()
24639 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
24643 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
24646 setValue : function(v,suppressEvent)
24648 if(this.inputType == 'radio'){
24649 this.setGroupValue(v, suppressEvent);
24653 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
24658 setGroupValue : function(v, suppressEvent)
24660 this.startValue = this.getValue();
24662 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24663 e.dom.checked = false;
24665 if(e.dom.value == v){
24666 e.dom.checked = true;
24670 if(suppressEvent !== true){
24671 this.fireEvent('check', this, true);
24679 validate : function()
24681 if(this.getVisibilityEl().hasClass('hidden')){
24687 (this.inputType == 'radio' && this.validateRadio()) ||
24688 (this.inputType == 'checkbox' && this.validateCheckbox())
24694 this.markInvalid();
24698 validateRadio : function()
24700 if(this.getVisibilityEl().hasClass('hidden')){
24704 if(this.allowBlank){
24710 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24711 if(!e.dom.checked){
24723 validateCheckbox : function()
24726 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
24727 //return (this.getValue() == this.inputValue) ? true : false;
24730 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24738 for(var i in group){
24739 if(group[i].el.isVisible(true)){
24747 for(var i in group){
24752 r = (group[i].getValue() == group[i].inputValue) ? true : false;
24759 * Mark this field as valid
24761 markValid : function()
24765 this.fireEvent('valid', this);
24767 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24770 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24777 if(this.inputType == 'radio'){
24778 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24779 var fg = e.findParent('.form-group', false, true);
24780 if (Roo.bootstrap.version == 3) {
24781 fg.removeClass([_this.invalidClass, _this.validClass]);
24782 fg.addClass(_this.validClass);
24784 fg.removeClass(['is-valid', 'is-invalid']);
24785 fg.addClass('is-valid');
24793 var fg = this.el.findParent('.form-group', false, true);
24794 if (Roo.bootstrap.version == 3) {
24795 fg.removeClass([this.invalidClass, this.validClass]);
24796 fg.addClass(this.validClass);
24798 fg.removeClass(['is-valid', 'is-invalid']);
24799 fg.addClass('is-valid');
24804 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24810 for(var i in group){
24811 var fg = group[i].el.findParent('.form-group', false, true);
24812 if (Roo.bootstrap.version == 3) {
24813 fg.removeClass([this.invalidClass, this.validClass]);
24814 fg.addClass(this.validClass);
24816 fg.removeClass(['is-valid', 'is-invalid']);
24817 fg.addClass('is-valid');
24823 * Mark this field as invalid
24824 * @param {String} msg The validation message
24826 markInvalid : function(msg)
24828 if(this.allowBlank){
24834 this.fireEvent('invalid', this, msg);
24836 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24839 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24843 label.markInvalid();
24846 if(this.inputType == 'radio'){
24848 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24849 var fg = e.findParent('.form-group', false, true);
24850 if (Roo.bootstrap.version == 3) {
24851 fg.removeClass([_this.invalidClass, _this.validClass]);
24852 fg.addClass(_this.invalidClass);
24854 fg.removeClass(['is-invalid', 'is-valid']);
24855 fg.addClass('is-invalid');
24863 var fg = this.el.findParent('.form-group', false, true);
24864 if (Roo.bootstrap.version == 3) {
24865 fg.removeClass([_this.invalidClass, _this.validClass]);
24866 fg.addClass(_this.invalidClass);
24868 fg.removeClass(['is-invalid', 'is-valid']);
24869 fg.addClass('is-invalid');
24874 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24880 for(var i in group){
24881 var fg = group[i].el.findParent('.form-group', false, true);
24882 if (Roo.bootstrap.version == 3) {
24883 fg.removeClass([_this.invalidClass, _this.validClass]);
24884 fg.addClass(_this.invalidClass);
24886 fg.removeClass(['is-invalid', 'is-valid']);
24887 fg.addClass('is-invalid');
24893 clearInvalid : function()
24895 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
24897 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
24899 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24901 if (label && label.iconEl) {
24902 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
24903 label.iconEl.removeClass(['is-invalid', 'is-valid']);
24907 disable : function()
24909 if(this.inputType != 'radio'){
24910 Roo.bootstrap.CheckBox.superclass.disable.call(this);
24917 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24918 _this.getActionEl().addClass(this.disabledClass);
24919 e.dom.disabled = true;
24923 this.disabled = true;
24924 this.fireEvent("disable", this);
24928 enable : function()
24930 if(this.inputType != 'radio'){
24931 Roo.bootstrap.CheckBox.superclass.enable.call(this);
24938 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24939 _this.getActionEl().removeClass(this.disabledClass);
24940 e.dom.disabled = false;
24944 this.disabled = false;
24945 this.fireEvent("enable", this);
24949 setBoxLabel : function(v)
24954 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24960 Roo.apply(Roo.bootstrap.CheckBox, {
24965 * register a CheckBox Group
24966 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
24968 register : function(checkbox)
24970 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
24971 this.groups[checkbox.groupId] = {};
24974 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
24978 this.groups[checkbox.groupId][checkbox.name] = checkbox;
24982 * fetch a CheckBox Group based on the group ID
24983 * @param {string} the group ID
24984 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
24986 get: function(groupId) {
24987 if (typeof(this.groups[groupId]) == 'undefined') {
24991 return this.groups[groupId] ;
25004 * @class Roo.bootstrap.Radio
25005 * @extends Roo.bootstrap.Component
25006 * Bootstrap Radio class
25007 * @cfg {String} boxLabel - the label associated
25008 * @cfg {String} value - the value of radio
25011 * Create a new Radio
25012 * @param {Object} config The config object
25014 Roo.bootstrap.Radio = function(config){
25015 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
25019 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
25025 getAutoCreate : function()
25029 cls : 'form-group radio',
25034 html : this.boxLabel
25042 initEvents : function()
25044 this.parent().register(this);
25046 this.el.on('click', this.onClick, this);
25050 onClick : function(e)
25052 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25053 this.setChecked(true);
25057 setChecked : function(state, suppressEvent)
25059 this.parent().setValue(this.value, suppressEvent);
25063 setBoxLabel : function(v)
25068 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25083 * @class Roo.bootstrap.SecurePass
25084 * @extends Roo.bootstrap.Input
25085 * Bootstrap SecurePass class
25089 * Create a new SecurePass
25090 * @param {Object} config The config object
25093 Roo.bootstrap.SecurePass = function (config) {
25094 // these go here, so the translation tool can replace them..
25096 PwdEmpty: "Please type a password, and then retype it to confirm.",
25097 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25098 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25099 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25100 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25101 FNInPwd: "Your password can't contain your first name. Please type a different password.",
25102 LNInPwd: "Your password can't contain your last name. Please type a different password.",
25103 TooWeak: "Your password is Too Weak."
25105 this.meterLabel = "Password strength:";
25106 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25107 this.meterClass = [
25108 "roo-password-meter-tooweak",
25109 "roo-password-meter-weak",
25110 "roo-password-meter-medium",
25111 "roo-password-meter-strong",
25112 "roo-password-meter-grey"
25117 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
25120 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
25122 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25124 * PwdEmpty: "Please type a password, and then retype it to confirm.",
25125 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25126 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25127 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25128 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25129 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
25130 * LNInPwd: "Your password can't contain your last name. Please type a different password."
25140 * @cfg {String/Object} Label for the strength meter (defaults to
25141 * 'Password strength:')
25146 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25147 * ['Weak', 'Medium', 'Strong'])
25150 pwdStrengths: false,
25163 initEvents: function ()
25165 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
25167 if (this.el.is('input[type=password]') && Roo.isSafari) {
25168 this.el.on('keydown', this.SafariOnKeyDown, this);
25171 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25174 onRender: function (ct, position)
25176 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
25177 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25178 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25180 this.trigger.createChild({
25185 cls: 'roo-password-meter-grey col-xs-12',
25188 //width: this.meterWidth + 'px'
25192 cls: 'roo-password-meter-text'
25198 if (this.hideTrigger) {
25199 this.trigger.setDisplayed(false);
25201 this.setSize(this.width || '', this.height || '');
25204 onDestroy: function ()
25206 if (this.trigger) {
25207 this.trigger.removeAllListeners();
25208 this.trigger.remove();
25211 this.wrap.remove();
25213 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
25216 checkStrength: function ()
25218 var pwd = this.inputEl().getValue();
25219 if (pwd == this._lastPwd) {
25224 if (this.ClientSideStrongPassword(pwd)) {
25226 } else if (this.ClientSideMediumPassword(pwd)) {
25228 } else if (this.ClientSideWeakPassword(pwd)) {
25234 Roo.log('strength1: ' + strength);
25236 //var pm = this.trigger.child('div/div/div').dom;
25237 var pm = this.trigger.child('div/div');
25238 pm.removeClass(this.meterClass);
25239 pm.addClass(this.meterClass[strength]);
25242 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25244 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25246 this._lastPwd = pwd;
25250 Roo.bootstrap.SecurePass.superclass.reset.call(this);
25252 this._lastPwd = '';
25254 var pm = this.trigger.child('div/div');
25255 pm.removeClass(this.meterClass);
25256 pm.addClass('roo-password-meter-grey');
25259 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25262 this.inputEl().dom.type='password';
25265 validateValue: function (value)
25267 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
25270 if (value.length == 0) {
25271 if (this.allowBlank) {
25272 this.clearInvalid();
25276 this.markInvalid(this.errors.PwdEmpty);
25277 this.errorMsg = this.errors.PwdEmpty;
25285 if (!value.match(/[\x21-\x7e]+/)) {
25286 this.markInvalid(this.errors.PwdBadChar);
25287 this.errorMsg = this.errors.PwdBadChar;
25290 if (value.length < 6) {
25291 this.markInvalid(this.errors.PwdShort);
25292 this.errorMsg = this.errors.PwdShort;
25295 if (value.length > 16) {
25296 this.markInvalid(this.errors.PwdLong);
25297 this.errorMsg = this.errors.PwdLong;
25301 if (this.ClientSideStrongPassword(value)) {
25303 } else if (this.ClientSideMediumPassword(value)) {
25305 } else if (this.ClientSideWeakPassword(value)) {
25312 if (strength < 2) {
25313 //this.markInvalid(this.errors.TooWeak);
25314 this.errorMsg = this.errors.TooWeak;
25319 console.log('strength2: ' + strength);
25321 //var pm = this.trigger.child('div/div/div').dom;
25323 var pm = this.trigger.child('div/div');
25324 pm.removeClass(this.meterClass);
25325 pm.addClass(this.meterClass[strength]);
25327 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25329 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25331 this.errorMsg = '';
25335 CharacterSetChecks: function (type)
25338 this.fResult = false;
25341 isctype: function (character, type)
25344 case this.kCapitalLetter:
25345 if (character >= 'A' && character <= 'Z') {
25350 case this.kSmallLetter:
25351 if (character >= 'a' && character <= 'z') {
25357 if (character >= '0' && character <= '9') {
25362 case this.kPunctuation:
25363 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25374 IsLongEnough: function (pwd, size)
25376 return !(pwd == null || isNaN(size) || pwd.length < size);
25379 SpansEnoughCharacterSets: function (word, nb)
25381 if (!this.IsLongEnough(word, nb))
25386 var characterSetChecks = new Array(
25387 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25388 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25391 for (var index = 0; index < word.length; ++index) {
25392 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25393 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25394 characterSetChecks[nCharSet].fResult = true;
25401 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25402 if (characterSetChecks[nCharSet].fResult) {
25407 if (nCharSets < nb) {
25413 ClientSideStrongPassword: function (pwd)
25415 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25418 ClientSideMediumPassword: function (pwd)
25420 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25423 ClientSideWeakPassword: function (pwd)
25425 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25428 })//<script type="text/javascript">
25431 * Based Ext JS Library 1.1.1
25432 * Copyright(c) 2006-2007, Ext JS, LLC.
25438 * @class Roo.HtmlEditorCore
25439 * @extends Roo.Component
25440 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25442 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25445 Roo.HtmlEditorCore = function(config){
25448 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25453 * @event initialize
25454 * Fires when the editor is fully initialized (including the iframe)
25455 * @param {Roo.HtmlEditorCore} this
25460 * Fires when the editor is first receives the focus. Any insertion must wait
25461 * until after this event.
25462 * @param {Roo.HtmlEditorCore} this
25466 * @event beforesync
25467 * Fires before the textarea is updated with content from the editor iframe. Return false
25468 * to cancel the sync.
25469 * @param {Roo.HtmlEditorCore} this
25470 * @param {String} html
25474 * @event beforepush
25475 * Fires before the iframe editor is updated with content from the textarea. Return false
25476 * to cancel the push.
25477 * @param {Roo.HtmlEditorCore} this
25478 * @param {String} html
25483 * Fires when the textarea is updated with content from the editor iframe.
25484 * @param {Roo.HtmlEditorCore} this
25485 * @param {String} html
25490 * Fires when the iframe editor is updated with content from the textarea.
25491 * @param {Roo.HtmlEditorCore} this
25492 * @param {String} html
25497 * @event editorevent
25498 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25499 * @param {Roo.HtmlEditorCore} this
25505 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
25507 // defaults : white / black...
25508 this.applyBlacklists();
25515 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
25519 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
25525 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
25530 * @cfg {Number} height (in pixels)
25534 * @cfg {Number} width (in pixels)
25539 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25542 stylesheets: false,
25547 // private properties
25548 validationEvent : false,
25550 initialized : false,
25552 sourceEditMode : false,
25553 onFocus : Roo.emptyFn,
25555 hideMode:'offsets',
25559 // blacklist + whitelisted elements..
25566 * Protected method that will not generally be called directly. It
25567 * is called when the editor initializes the iframe with HTML contents. Override this method if you
25568 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25570 getDocMarkup : function(){
25574 // inherit styels from page...??
25575 if (this.stylesheets === false) {
25577 Roo.get(document.head).select('style').each(function(node) {
25578 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25581 Roo.get(document.head).select('link').each(function(node) {
25582 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25585 } else if (!this.stylesheets.length) {
25587 st = '<style type="text/css">' +
25588 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25591 for (var i in this.stylesheets) {
25592 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
25597 st += '<style type="text/css">' +
25598 'IMG { cursor: pointer } ' +
25601 var cls = 'roo-htmleditor-body';
25603 if(this.bodyCls.length){
25604 cls += ' ' + this.bodyCls;
25607 return '<html><head>' + st +
25608 //<style type="text/css">' +
25609 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25611 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
25615 onRender : function(ct, position)
25618 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
25619 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
25622 this.el.dom.style.border = '0 none';
25623 this.el.dom.setAttribute('tabIndex', -1);
25624 this.el.addClass('x-hidden hide');
25628 if(Roo.isIE){ // fix IE 1px bogus margin
25629 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25633 this.frameId = Roo.id();
25637 var iframe = this.owner.wrap.createChild({
25639 cls: 'form-control', // bootstrap..
25641 name: this.frameId,
25642 frameBorder : 'no',
25643 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
25648 this.iframe = iframe.dom;
25650 this.assignDocWin();
25652 this.doc.designMode = 'on';
25655 this.doc.write(this.getDocMarkup());
25659 var task = { // must defer to wait for browser to be ready
25661 //console.log("run task?" + this.doc.readyState);
25662 this.assignDocWin();
25663 if(this.doc.body || this.doc.readyState == 'complete'){
25665 this.doc.designMode="on";
25669 Roo.TaskMgr.stop(task);
25670 this.initEditor.defer(10, this);
25677 Roo.TaskMgr.start(task);
25682 onResize : function(w, h)
25684 Roo.log('resize: ' +w + ',' + h );
25685 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
25689 if(typeof w == 'number'){
25691 this.iframe.style.width = w + 'px';
25693 if(typeof h == 'number'){
25695 this.iframe.style.height = h + 'px';
25697 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
25704 * Toggles the editor between standard and source edit mode.
25705 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25707 toggleSourceEdit : function(sourceEditMode){
25709 this.sourceEditMode = sourceEditMode === true;
25711 if(this.sourceEditMode){
25713 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
25716 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
25717 //this.iframe.className = '';
25720 //this.setSize(this.owner.wrap.getSize());
25721 //this.fireEvent('editmodechange', this, this.sourceEditMode);
25728 * Protected method that will not generally be called directly. If you need/want
25729 * custom HTML cleanup, this is the method you should override.
25730 * @param {String} html The HTML to be cleaned
25731 * return {String} The cleaned HTML
25733 cleanHtml : function(html){
25734 html = String(html);
25735 if(html.length > 5){
25736 if(Roo.isSafari){ // strip safari nonsense
25737 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25740 if(html == ' '){
25747 * HTML Editor -> Textarea
25748 * Protected method that will not generally be called directly. Syncs the contents
25749 * of the editor iframe with the textarea.
25751 syncValue : function(){
25752 if(this.initialized){
25753 var bd = (this.doc.body || this.doc.documentElement);
25754 //this.cleanUpPaste(); -- this is done else where and causes havoc..
25755 var html = bd.innerHTML;
25757 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25758 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25760 html = '<div style="'+m[0]+'">' + html + '</div>';
25763 html = this.cleanHtml(html);
25764 // fix up the special chars.. normaly like back quotes in word...
25765 // however we do not want to do this with chinese..
25766 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
25768 var cc = match.charCodeAt();
25770 // Get the character value, handling surrogate pairs
25771 if (match.length == 2) {
25772 // It's a surrogate pair, calculate the Unicode code point
25773 var high = match.charCodeAt(0) - 0xD800;
25774 var low = match.charCodeAt(1) - 0xDC00;
25775 cc = (high * 0x400) + low + 0x10000;
25777 (cc >= 0x4E00 && cc < 0xA000 ) ||
25778 (cc >= 0x3400 && cc < 0x4E00 ) ||
25779 (cc >= 0xf900 && cc < 0xfb00 )
25784 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
25785 return "&#" + cc + ";";
25792 if(this.owner.fireEvent('beforesync', this, html) !== false){
25793 this.el.dom.value = html;
25794 this.owner.fireEvent('sync', this, html);
25800 * Protected method that will not generally be called directly. Pushes the value of the textarea
25801 * into the iframe editor.
25803 pushValue : function(){
25804 if(this.initialized){
25805 var v = this.el.dom.value.trim();
25807 // if(v.length < 1){
25811 if(this.owner.fireEvent('beforepush', this, v) !== false){
25812 var d = (this.doc.body || this.doc.documentElement);
25814 this.cleanUpPaste();
25815 this.el.dom.value = d.innerHTML;
25816 this.owner.fireEvent('push', this, v);
25822 deferFocus : function(){
25823 this.focus.defer(10, this);
25827 focus : function(){
25828 if(this.win && !this.sourceEditMode){
25835 assignDocWin: function()
25837 var iframe = this.iframe;
25840 this.doc = iframe.contentWindow.document;
25841 this.win = iframe.contentWindow;
25843 // if (!Roo.get(this.frameId)) {
25846 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25847 // this.win = Roo.get(this.frameId).dom.contentWindow;
25849 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25853 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25854 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25859 initEditor : function(){
25860 //console.log("INIT EDITOR");
25861 this.assignDocWin();
25865 this.doc.designMode="on";
25867 this.doc.write(this.getDocMarkup());
25870 var dbody = (this.doc.body || this.doc.documentElement);
25871 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25872 // this copies styles from the containing element into thsi one..
25873 // not sure why we need all of this..
25874 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25876 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25877 //ss['background-attachment'] = 'fixed'; // w3c
25878 dbody.bgProperties = 'fixed'; // ie
25879 //Roo.DomHelper.applyStyles(dbody, ss);
25880 Roo.EventManager.on(this.doc, {
25881 //'mousedown': this.onEditorEvent,
25882 'mouseup': this.onEditorEvent,
25883 'dblclick': this.onEditorEvent,
25884 'click': this.onEditorEvent,
25885 'keyup': this.onEditorEvent,
25890 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25892 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25893 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25895 this.initialized = true;
25897 this.owner.fireEvent('initialize', this);
25902 onDestroy : function(){
25908 //for (var i =0; i < this.toolbars.length;i++) {
25909 // // fixme - ask toolbars for heights?
25910 // this.toolbars[i].onDestroy();
25913 //this.wrap.dom.innerHTML = '';
25914 //this.wrap.remove();
25919 onFirstFocus : function(){
25921 this.assignDocWin();
25924 this.activated = true;
25927 if(Roo.isGecko){ // prevent silly gecko errors
25929 var s = this.win.getSelection();
25930 if(!s.focusNode || s.focusNode.nodeType != 3){
25931 var r = s.getRangeAt(0);
25932 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25937 this.execCmd('useCSS', true);
25938 this.execCmd('styleWithCSS', false);
25941 this.owner.fireEvent('activate', this);
25945 adjustFont: function(btn){
25946 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25947 //if(Roo.isSafari){ // safari
25950 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25951 if(Roo.isSafari){ // safari
25952 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25953 v = (v < 10) ? 10 : v;
25954 v = (v > 48) ? 48 : v;
25955 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25960 v = Math.max(1, v+adjust);
25962 this.execCmd('FontSize', v );
25965 onEditorEvent : function(e)
25967 this.owner.fireEvent('editorevent', this, e);
25968 // this.updateToolbar();
25969 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25972 insertTag : function(tg)
25974 // could be a bit smarter... -> wrap the current selected tRoo..
25975 if (tg.toLowerCase() == 'span' ||
25976 tg.toLowerCase() == 'code' ||
25977 tg.toLowerCase() == 'sup' ||
25978 tg.toLowerCase() == 'sub'
25981 range = this.createRange(this.getSelection());
25982 var wrappingNode = this.doc.createElement(tg.toLowerCase());
25983 wrappingNode.appendChild(range.extractContents());
25984 range.insertNode(wrappingNode);
25991 this.execCmd("formatblock", tg);
25995 insertText : function(txt)
25999 var range = this.createRange();
26000 range.deleteContents();
26001 //alert(Sender.getAttribute('label'));
26003 range.insertNode(this.doc.createTextNode(txt));
26009 * Executes a Midas editor command on the editor document and performs necessary focus and
26010 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26011 * @param {String} cmd The Midas command
26012 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26014 relayCmd : function(cmd, value){
26016 this.execCmd(cmd, value);
26017 this.owner.fireEvent('editorevent', this);
26018 //this.updateToolbar();
26019 this.owner.deferFocus();
26023 * Executes a Midas editor command directly on the editor document.
26024 * For visual commands, you should use {@link #relayCmd} instead.
26025 * <b>This should only be called after the editor is initialized.</b>
26026 * @param {String} cmd The Midas command
26027 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26029 execCmd : function(cmd, value){
26030 this.doc.execCommand(cmd, false, value === undefined ? null : value);
26037 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26039 * @param {String} text | dom node..
26041 insertAtCursor : function(text)
26044 if(!this.activated){
26050 var r = this.doc.selection.createRange();
26061 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26065 // from jquery ui (MIT licenced)
26067 var win = this.win;
26069 if (win.getSelection && win.getSelection().getRangeAt) {
26070 range = win.getSelection().getRangeAt(0);
26071 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26072 range.insertNode(node);
26073 } else if (win.document.selection && win.document.selection.createRange) {
26074 // no firefox support
26075 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26076 win.document.selection.createRange().pasteHTML(txt);
26078 // no firefox support
26079 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26080 this.execCmd('InsertHTML', txt);
26089 mozKeyPress : function(e){
26091 var c = e.getCharCode(), cmd;
26094 c = String.fromCharCode(c).toLowerCase();
26108 this.cleanUpPaste.defer(100, this);
26116 e.preventDefault();
26124 fixKeys : function(){ // load time branching for fastest keydown performance
26126 return function(e){
26127 var k = e.getKey(), r;
26130 r = this.doc.selection.createRange();
26133 r.pasteHTML('    ');
26140 r = this.doc.selection.createRange();
26142 var target = r.parentElement();
26143 if(!target || target.tagName.toLowerCase() != 'li'){
26145 r.pasteHTML('<br />');
26151 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26152 this.cleanUpPaste.defer(100, this);
26158 }else if(Roo.isOpera){
26159 return function(e){
26160 var k = e.getKey();
26164 this.execCmd('InsertHTML','    ');
26167 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26168 this.cleanUpPaste.defer(100, this);
26173 }else if(Roo.isSafari){
26174 return function(e){
26175 var k = e.getKey();
26179 this.execCmd('InsertText','\t');
26183 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26184 this.cleanUpPaste.defer(100, this);
26192 getAllAncestors: function()
26194 var p = this.getSelectedNode();
26197 a.push(p); // push blank onto stack..
26198 p = this.getParentElement();
26202 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26206 a.push(this.doc.body);
26210 lastSelNode : false,
26213 getSelection : function()
26215 this.assignDocWin();
26216 return Roo.isIE ? this.doc.selection : this.win.getSelection();
26219 getSelectedNode: function()
26221 // this may only work on Gecko!!!
26223 // should we cache this!!!!
26228 var range = this.createRange(this.getSelection()).cloneRange();
26231 var parent = range.parentElement();
26233 var testRange = range.duplicate();
26234 testRange.moveToElementText(parent);
26235 if (testRange.inRange(range)) {
26238 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26241 parent = parent.parentElement;
26246 // is ancestor a text element.
26247 var ac = range.commonAncestorContainer;
26248 if (ac.nodeType == 3) {
26249 ac = ac.parentNode;
26252 var ar = ac.childNodes;
26255 var other_nodes = [];
26256 var has_other_nodes = false;
26257 for (var i=0;i<ar.length;i++) {
26258 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
26261 // fullly contained node.
26263 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26268 // probably selected..
26269 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26270 other_nodes.push(ar[i]);
26274 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
26279 has_other_nodes = true;
26281 if (!nodes.length && other_nodes.length) {
26282 nodes= other_nodes;
26284 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26290 createRange: function(sel)
26292 // this has strange effects when using with
26293 // top toolbar - not sure if it's a great idea.
26294 //this.editor.contentWindow.focus();
26295 if (typeof sel != "undefined") {
26297 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26299 return this.doc.createRange();
26302 return this.doc.createRange();
26305 getParentElement: function()
26308 this.assignDocWin();
26309 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26311 var range = this.createRange(sel);
26314 var p = range.commonAncestorContainer;
26315 while (p.nodeType == 3) { // text node
26326 * Range intersection.. the hard stuff...
26330 * [ -- selected range --- ]
26334 * if end is before start or hits it. fail.
26335 * if start is after end or hits it fail.
26337 * if either hits (but other is outside. - then it's not
26343 // @see http://www.thismuchiknow.co.uk/?p=64.
26344 rangeIntersectsNode : function(range, node)
26346 var nodeRange = node.ownerDocument.createRange();
26348 nodeRange.selectNode(node);
26350 nodeRange.selectNodeContents(node);
26353 var rangeStartRange = range.cloneRange();
26354 rangeStartRange.collapse(true);
26356 var rangeEndRange = range.cloneRange();
26357 rangeEndRange.collapse(false);
26359 var nodeStartRange = nodeRange.cloneRange();
26360 nodeStartRange.collapse(true);
26362 var nodeEndRange = nodeRange.cloneRange();
26363 nodeEndRange.collapse(false);
26365 return rangeStartRange.compareBoundaryPoints(
26366 Range.START_TO_START, nodeEndRange) == -1 &&
26367 rangeEndRange.compareBoundaryPoints(
26368 Range.START_TO_START, nodeStartRange) == 1;
26372 rangeCompareNode : function(range, node)
26374 var nodeRange = node.ownerDocument.createRange();
26376 nodeRange.selectNode(node);
26378 nodeRange.selectNodeContents(node);
26382 range.collapse(true);
26384 nodeRange.collapse(true);
26386 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26387 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
26389 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26391 var nodeIsBefore = ss == 1;
26392 var nodeIsAfter = ee == -1;
26394 if (nodeIsBefore && nodeIsAfter) {
26397 if (!nodeIsBefore && nodeIsAfter) {
26398 return 1; //right trailed.
26401 if (nodeIsBefore && !nodeIsAfter) {
26402 return 2; // left trailed.
26408 // private? - in a new class?
26409 cleanUpPaste : function()
26411 // cleans up the whole document..
26412 Roo.log('cleanuppaste');
26414 this.cleanUpChildren(this.doc.body);
26415 var clean = this.cleanWordChars(this.doc.body.innerHTML);
26416 if (clean != this.doc.body.innerHTML) {
26417 this.doc.body.innerHTML = clean;
26422 cleanWordChars : function(input) {// change the chars to hex code
26423 var he = Roo.HtmlEditorCore;
26425 var output = input;
26426 Roo.each(he.swapCodes, function(sw) {
26427 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26429 output = output.replace(swapper, sw[1]);
26436 cleanUpChildren : function (n)
26438 if (!n.childNodes.length) {
26441 for (var i = n.childNodes.length-1; i > -1 ; i--) {
26442 this.cleanUpChild(n.childNodes[i]);
26449 cleanUpChild : function (node)
26452 //console.log(node);
26453 if (node.nodeName == "#text") {
26454 // clean up silly Windows -- stuff?
26457 if (node.nodeName == "#comment") {
26458 node.parentNode.removeChild(node);
26459 // clean up silly Windows -- stuff?
26462 var lcname = node.tagName.toLowerCase();
26463 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26464 // whitelist of tags..
26466 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26468 node.parentNode.removeChild(node);
26473 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
26475 // spans with no attributes - just remove them..
26476 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
26477 remove_keep_children = true;
26480 // remove <a name=....> as rendering on yahoo mailer is borked with this.
26481 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26483 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26484 // remove_keep_children = true;
26487 if (remove_keep_children) {
26488 this.cleanUpChildren(node);
26489 // inserts everything just before this node...
26490 while (node.childNodes.length) {
26491 var cn = node.childNodes[0];
26492 node.removeChild(cn);
26493 node.parentNode.insertBefore(cn, node);
26495 node.parentNode.removeChild(node);
26499 if (!node.attributes || !node.attributes.length) {
26504 this.cleanUpChildren(node);
26508 function cleanAttr(n,v)
26511 if (v.match(/^\./) || v.match(/^\//)) {
26514 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
26517 if (v.match(/^#/)) {
26520 if (v.match(/^\{/)) { // allow template editing.
26523 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26524 node.removeAttribute(n);
26528 var cwhite = this.cwhite;
26529 var cblack = this.cblack;
26531 function cleanStyle(n,v)
26533 if (v.match(/expression/)) { //XSS?? should we even bother..
26534 node.removeAttribute(n);
26538 var parts = v.split(/;/);
26541 Roo.each(parts, function(p) {
26542 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26546 var l = p.split(':').shift().replace(/\s+/g,'');
26547 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26549 if ( cwhite.length && cblack.indexOf(l) > -1) {
26550 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26551 //node.removeAttribute(n);
26555 // only allow 'c whitelisted system attributes'
26556 if ( cwhite.length && cwhite.indexOf(l) < 0) {
26557 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26558 //node.removeAttribute(n);
26568 if (clean.length) {
26569 node.setAttribute(n, clean.join(';'));
26571 node.removeAttribute(n);
26577 for (var i = node.attributes.length-1; i > -1 ; i--) {
26578 var a = node.attributes[i];
26581 if (a.name.toLowerCase().substr(0,2)=='on') {
26582 node.removeAttribute(a.name);
26585 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
26586 node.removeAttribute(a.name);
26589 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
26590 cleanAttr(a.name,a.value); // fixme..
26593 if (a.name == 'style') {
26594 cleanStyle(a.name,a.value);
26597 /// clean up MS crap..
26598 // tecnically this should be a list of valid class'es..
26601 if (a.name == 'class') {
26602 if (a.value.match(/^Mso/)) {
26603 node.removeAttribute('class');
26606 if (a.value.match(/^body$/)) {
26607 node.removeAttribute('class');
26618 this.cleanUpChildren(node);
26624 * Clean up MS wordisms...
26626 cleanWord : function(node)
26629 this.cleanWord(this.doc.body);
26634 node.nodeName == 'SPAN' &&
26635 !node.hasAttributes() &&
26636 node.childNodes.length == 1 &&
26637 node.firstChild.nodeName == "#text"
26639 var textNode = node.firstChild;
26640 node.removeChild(textNode);
26641 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
26642 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26644 node.parentNode.insertBefore(textNode, node);
26645 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
26646 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26648 node.parentNode.removeChild(node);
26651 if (node.nodeName == "#text") {
26652 // clean up silly Windows -- stuff?
26655 if (node.nodeName == "#comment") {
26656 node.parentNode.removeChild(node);
26657 // clean up silly Windows -- stuff?
26661 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26662 node.parentNode.removeChild(node);
26665 //Roo.log(node.tagName);
26666 // remove - but keep children..
26667 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26668 //Roo.log('-- removed');
26669 while (node.childNodes.length) {
26670 var cn = node.childNodes[0];
26671 node.removeChild(cn);
26672 node.parentNode.insertBefore(cn, node);
26673 // move node to parent - and clean it..
26674 this.cleanWord(cn);
26676 node.parentNode.removeChild(node);
26677 /// no need to iterate chidlren = it's got none..
26678 //this.iterateChildren(node, this.cleanWord);
26682 if (node.className.length) {
26684 var cn = node.className.split(/\W+/);
26686 Roo.each(cn, function(cls) {
26687 if (cls.match(/Mso[a-zA-Z]+/)) {
26692 node.className = cna.length ? cna.join(' ') : '';
26694 node.removeAttribute("class");
26698 if (node.hasAttribute("lang")) {
26699 node.removeAttribute("lang");
26702 if (node.hasAttribute("style")) {
26704 var styles = node.getAttribute("style").split(";");
26706 Roo.each(styles, function(s) {
26707 if (!s.match(/:/)) {
26710 var kv = s.split(":");
26711 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26714 // what ever is left... we allow.
26717 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26718 if (!nstyle.length) {
26719 node.removeAttribute('style');
26722 this.iterateChildren(node, this.cleanWord);
26728 * iterateChildren of a Node, calling fn each time, using this as the scole..
26729 * @param {DomNode} node node to iterate children of.
26730 * @param {Function} fn method of this class to call on each item.
26732 iterateChildren : function(node, fn)
26734 if (!node.childNodes.length) {
26737 for (var i = node.childNodes.length-1; i > -1 ; i--) {
26738 fn.call(this, node.childNodes[i])
26744 * cleanTableWidths.
26746 * Quite often pasting from word etc.. results in tables with column and widths.
26747 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26750 cleanTableWidths : function(node)
26755 this.cleanTableWidths(this.doc.body);
26760 if (node.nodeName == "#text" || node.nodeName == "#comment") {
26763 Roo.log(node.tagName);
26764 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
26765 this.iterateChildren(node, this.cleanTableWidths);
26768 if (node.hasAttribute('width')) {
26769 node.removeAttribute('width');
26773 if (node.hasAttribute("style")) {
26776 var styles = node.getAttribute("style").split(";");
26778 Roo.each(styles, function(s) {
26779 if (!s.match(/:/)) {
26782 var kv = s.split(":");
26783 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26786 // what ever is left... we allow.
26789 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26790 if (!nstyle.length) {
26791 node.removeAttribute('style');
26795 this.iterateChildren(node, this.cleanTableWidths);
26803 domToHTML : function(currentElement, depth, nopadtext) {
26805 depth = depth || 0;
26806 nopadtext = nopadtext || false;
26808 if (!currentElement) {
26809 return this.domToHTML(this.doc.body);
26812 //Roo.log(currentElement);
26814 var allText = false;
26815 var nodeName = currentElement.nodeName;
26816 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
26818 if (nodeName == '#text') {
26820 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
26825 if (nodeName != 'BODY') {
26828 // Prints the node tagName, such as <A>, <IMG>, etc
26831 for(i = 0; i < currentElement.attributes.length;i++) {
26833 var aname = currentElement.attributes.item(i).name;
26834 if (!currentElement.attributes.item(i).value.length) {
26837 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
26840 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
26849 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
26852 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
26857 // Traverse the tree
26859 var currentElementChild = currentElement.childNodes.item(i);
26860 var allText = true;
26861 var innerHTML = '';
26863 while (currentElementChild) {
26864 // Formatting code (indent the tree so it looks nice on the screen)
26865 var nopad = nopadtext;
26866 if (lastnode == 'SPAN') {
26870 if (currentElementChild.nodeName == '#text') {
26871 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
26872 toadd = nopadtext ? toadd : toadd.trim();
26873 if (!nopad && toadd.length > 80) {
26874 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
26876 innerHTML += toadd;
26879 currentElementChild = currentElement.childNodes.item(i);
26885 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
26887 // Recursively traverse the tree structure of the child node
26888 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
26889 lastnode = currentElementChild.nodeName;
26891 currentElementChild=currentElement.childNodes.item(i);
26897 // The remaining code is mostly for formatting the tree
26898 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
26903 ret+= "</"+tagName+">";
26909 applyBlacklists : function()
26911 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
26912 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
26916 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
26917 if (b.indexOf(tag) > -1) {
26920 this.white.push(tag);
26924 Roo.each(w, function(tag) {
26925 if (b.indexOf(tag) > -1) {
26928 if (this.white.indexOf(tag) > -1) {
26931 this.white.push(tag);
26936 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
26937 if (w.indexOf(tag) > -1) {
26940 this.black.push(tag);
26944 Roo.each(b, function(tag) {
26945 if (w.indexOf(tag) > -1) {
26948 if (this.black.indexOf(tag) > -1) {
26951 this.black.push(tag);
26956 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
26957 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
26961 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
26962 if (b.indexOf(tag) > -1) {
26965 this.cwhite.push(tag);
26969 Roo.each(w, function(tag) {
26970 if (b.indexOf(tag) > -1) {
26973 if (this.cwhite.indexOf(tag) > -1) {
26976 this.cwhite.push(tag);
26981 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
26982 if (w.indexOf(tag) > -1) {
26985 this.cblack.push(tag);
26989 Roo.each(b, function(tag) {
26990 if (w.indexOf(tag) > -1) {
26993 if (this.cblack.indexOf(tag) > -1) {
26996 this.cblack.push(tag);
27001 setStylesheets : function(stylesheets)
27003 if(typeof(stylesheets) == 'string'){
27004 Roo.get(this.iframe.contentDocument.head).createChild({
27006 rel : 'stylesheet',
27015 Roo.each(stylesheets, function(s) {
27020 Roo.get(_this.iframe.contentDocument.head).createChild({
27022 rel : 'stylesheet',
27031 removeStylesheets : function()
27035 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27040 setStyle : function(style)
27042 Roo.get(this.iframe.contentDocument.head).createChild({
27051 // hide stuff that is not compatible
27065 * @event specialkey
27069 * @cfg {String} fieldClass @hide
27072 * @cfg {String} focusClass @hide
27075 * @cfg {String} autoCreate @hide
27078 * @cfg {String} inputType @hide
27081 * @cfg {String} invalidClass @hide
27084 * @cfg {String} invalidText @hide
27087 * @cfg {String} msgFx @hide
27090 * @cfg {String} validateOnBlur @hide
27094 Roo.HtmlEditorCore.white = [
27095 'area', 'br', 'img', 'input', 'hr', 'wbr',
27097 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
27098 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
27099 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
27100 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
27101 'table', 'ul', 'xmp',
27103 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
27106 'dir', 'menu', 'ol', 'ul', 'dl',
27112 Roo.HtmlEditorCore.black = [
27113 // 'embed', 'object', // enable - backend responsiblity to clean thiese
27115 'base', 'basefont', 'bgsound', 'blink', 'body',
27116 'frame', 'frameset', 'head', 'html', 'ilayer',
27117 'iframe', 'layer', 'link', 'meta', 'object',
27118 'script', 'style' ,'title', 'xml' // clean later..
27120 Roo.HtmlEditorCore.clean = [
27121 'script', 'style', 'title', 'xml'
27123 Roo.HtmlEditorCore.remove = [
27128 Roo.HtmlEditorCore.ablack = [
27132 Roo.HtmlEditorCore.aclean = [
27133 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
27137 Roo.HtmlEditorCore.pwhite= [
27138 'http', 'https', 'mailto'
27141 // white listed style attributes.
27142 Roo.HtmlEditorCore.cwhite= [
27143 // 'text-align', /// default is to allow most things..
27149 // black listed style attributes.
27150 Roo.HtmlEditorCore.cblack= [
27151 // 'font-size' -- this can be set by the project
27155 Roo.HtmlEditorCore.swapCodes =[
27156 [ 8211, "–" ],
27157 [ 8212, "—" ],
27174 * @class Roo.bootstrap.HtmlEditor
27175 * @extends Roo.bootstrap.TextArea
27176 * Bootstrap HtmlEditor class
27179 * Create a new HtmlEditor
27180 * @param {Object} config The config object
27183 Roo.bootstrap.HtmlEditor = function(config){
27184 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
27185 if (!this.toolbars) {
27186 this.toolbars = [];
27189 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27192 * @event initialize
27193 * Fires when the editor is fully initialized (including the iframe)
27194 * @param {HtmlEditor} this
27199 * Fires when the editor is first receives the focus. Any insertion must wait
27200 * until after this event.
27201 * @param {HtmlEditor} this
27205 * @event beforesync
27206 * Fires before the textarea is updated with content from the editor iframe. Return false
27207 * to cancel the sync.
27208 * @param {HtmlEditor} this
27209 * @param {String} html
27213 * @event beforepush
27214 * Fires before the iframe editor is updated with content from the textarea. Return false
27215 * to cancel the push.
27216 * @param {HtmlEditor} this
27217 * @param {String} html
27222 * Fires when the textarea is updated with content from the editor iframe.
27223 * @param {HtmlEditor} this
27224 * @param {String} html
27229 * Fires when the iframe editor is updated with content from the textarea.
27230 * @param {HtmlEditor} this
27231 * @param {String} html
27235 * @event editmodechange
27236 * Fires when the editor switches edit modes
27237 * @param {HtmlEditor} this
27238 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27240 editmodechange: true,
27242 * @event editorevent
27243 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27244 * @param {HtmlEditor} this
27248 * @event firstfocus
27249 * Fires when on first focus - needed by toolbars..
27250 * @param {HtmlEditor} this
27255 * Auto save the htmlEditor value as a file into Events
27256 * @param {HtmlEditor} this
27260 * @event savedpreview
27261 * preview the saved version of htmlEditor
27262 * @param {HtmlEditor} this
27269 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
27273 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27278 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27283 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
27288 * @cfg {Number} height (in pixels)
27292 * @cfg {Number} width (in pixels)
27297 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27300 stylesheets: false,
27305 // private properties
27306 validationEvent : false,
27308 initialized : false,
27311 onFocus : Roo.emptyFn,
27313 hideMode:'offsets',
27315 tbContainer : false,
27319 toolbarContainer :function() {
27320 return this.wrap.select('.x-html-editor-tb',true).first();
27324 * Protected method that will not generally be called directly. It
27325 * is called when the editor creates its toolbar. Override this method if you need to
27326 * add custom toolbar buttons.
27327 * @param {HtmlEditor} editor
27329 createToolbar : function(){
27330 Roo.log('renewing');
27331 Roo.log("create toolbars");
27333 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
27334 this.toolbars[0].render(this.toolbarContainer());
27338 // if (!editor.toolbars || !editor.toolbars.length) {
27339 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
27342 // for (var i =0 ; i < editor.toolbars.length;i++) {
27343 // editor.toolbars[i] = Roo.factory(
27344 // typeof(editor.toolbars[i]) == 'string' ?
27345 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
27346 // Roo.bootstrap.HtmlEditor);
27347 // editor.toolbars[i].init(editor);
27353 onRender : function(ct, position)
27355 // Roo.log("Call onRender: " + this.xtype);
27357 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
27359 this.wrap = this.inputEl().wrap({
27360 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27363 this.editorcore.onRender(ct, position);
27365 if (this.resizable) {
27366 this.resizeEl = new Roo.Resizable(this.wrap, {
27370 minHeight : this.height,
27371 height: this.height,
27372 handles : this.resizable,
27375 resize : function(r, w, h) {
27376 _t.onResize(w,h); // -something
27382 this.createToolbar(this);
27385 if(!this.width && this.resizable){
27386 this.setSize(this.wrap.getSize());
27388 if (this.resizeEl) {
27389 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27390 // should trigger onReize..
27396 onResize : function(w, h)
27398 Roo.log('resize: ' +w + ',' + h );
27399 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
27403 if(this.inputEl() ){
27404 if(typeof w == 'number'){
27405 var aw = w - this.wrap.getFrameWidth('lr');
27406 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27409 if(typeof h == 'number'){
27410 var tbh = -11; // fixme it needs to tool bar size!
27411 for (var i =0; i < this.toolbars.length;i++) {
27412 // fixme - ask toolbars for heights?
27413 tbh += this.toolbars[i].el.getHeight();
27414 //if (this.toolbars[i].footer) {
27415 // tbh += this.toolbars[i].footer.el.getHeight();
27423 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27424 ah -= 5; // knock a few pixes off for look..
27425 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27429 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27430 this.editorcore.onResize(ew,eh);
27435 * Toggles the editor between standard and source edit mode.
27436 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27438 toggleSourceEdit : function(sourceEditMode)
27440 this.editorcore.toggleSourceEdit(sourceEditMode);
27442 if(this.editorcore.sourceEditMode){
27443 Roo.log('editor - showing textarea');
27446 // Roo.log(this.syncValue());
27448 this.inputEl().removeClass(['hide', 'x-hidden']);
27449 this.inputEl().dom.removeAttribute('tabIndex');
27450 this.inputEl().focus();
27452 Roo.log('editor - hiding textarea');
27454 // Roo.log(this.pushValue());
27457 this.inputEl().addClass(['hide', 'x-hidden']);
27458 this.inputEl().dom.setAttribute('tabIndex', -1);
27459 //this.deferFocus();
27462 if(this.resizable){
27463 this.setSize(this.wrap.getSize());
27466 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27469 // private (for BoxComponent)
27470 adjustSize : Roo.BoxComponent.prototype.adjustSize,
27472 // private (for BoxComponent)
27473 getResizeEl : function(){
27477 // private (for BoxComponent)
27478 getPositionEl : function(){
27483 initEvents : function(){
27484 this.originalValue = this.getValue();
27488 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27491 // markInvalid : Roo.emptyFn,
27493 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27496 // clearInvalid : Roo.emptyFn,
27498 setValue : function(v){
27499 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
27500 this.editorcore.pushValue();
27505 deferFocus : function(){
27506 this.focus.defer(10, this);
27510 focus : function(){
27511 this.editorcore.focus();
27517 onDestroy : function(){
27523 for (var i =0; i < this.toolbars.length;i++) {
27524 // fixme - ask toolbars for heights?
27525 this.toolbars[i].onDestroy();
27528 this.wrap.dom.innerHTML = '';
27529 this.wrap.remove();
27534 onFirstFocus : function(){
27535 //Roo.log("onFirstFocus");
27536 this.editorcore.onFirstFocus();
27537 for (var i =0; i < this.toolbars.length;i++) {
27538 this.toolbars[i].onFirstFocus();
27544 syncValue : function()
27546 this.editorcore.syncValue();
27549 pushValue : function()
27551 this.editorcore.pushValue();
27555 // hide stuff that is not compatible
27569 * @event specialkey
27573 * @cfg {String} fieldClass @hide
27576 * @cfg {String} focusClass @hide
27579 * @cfg {String} autoCreate @hide
27582 * @cfg {String} inputType @hide
27586 * @cfg {String} invalidText @hide
27589 * @cfg {String} msgFx @hide
27592 * @cfg {String} validateOnBlur @hide
27601 Roo.namespace('Roo.bootstrap.htmleditor');
27603 * @class Roo.bootstrap.HtmlEditorToolbar1
27609 new Roo.bootstrap.HtmlEditor({
27612 new Roo.bootstrap.HtmlEditorToolbar1({
27613 disable : { fonts: 1 , format: 1, ..., ... , ...],
27619 * @cfg {Object} disable List of elements to disable..
27620 * @cfg {Array} btns List of additional buttons.
27624 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27627 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
27630 Roo.apply(this, config);
27632 // default disabled, based on 'good practice'..
27633 this.disable = this.disable || {};
27634 Roo.applyIf(this.disable, {
27637 specialElements : true
27639 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
27641 this.editor = config.editor;
27642 this.editorcore = config.editor.editorcore;
27644 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
27646 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27647 // dont call parent... till later.
27649 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
27654 editorcore : false,
27659 "h1","h2","h3","h4","h5","h6",
27661 "abbr", "acronym", "address", "cite", "samp", "var",
27665 onRender : function(ct, position)
27667 // Roo.log("Call onRender: " + this.xtype);
27669 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
27671 this.el.dom.style.marginBottom = '0';
27673 var editorcore = this.editorcore;
27674 var editor= this.editor;
27677 var btn = function(id,cmd , toggle, handler, html){
27679 var event = toggle ? 'toggle' : 'click';
27684 xns: Roo.bootstrap,
27688 enableToggle:toggle !== false,
27690 pressed : toggle ? false : null,
27693 a.listeners[toggle ? 'toggle' : 'click'] = function() {
27694 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
27700 // var cb_box = function...
27705 xns: Roo.bootstrap,
27710 xns: Roo.bootstrap,
27714 Roo.each(this.formats, function(f) {
27715 style.menu.items.push({
27717 xns: Roo.bootstrap,
27718 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
27723 editorcore.insertTag(this.tagname);
27730 children.push(style);
27732 btn('bold',false,true);
27733 btn('italic',false,true);
27734 btn('align-left', 'justifyleft',true);
27735 btn('align-center', 'justifycenter',true);
27736 btn('align-right' , 'justifyright',true);
27737 btn('link', false, false, function(btn) {
27738 //Roo.log("create link?");
27739 var url = prompt(this.createLinkText, this.defaultLinkValue);
27740 if(url && url != 'http:/'+'/'){
27741 this.editorcore.relayCmd('createlink', url);
27744 btn('list','insertunorderedlist',true);
27745 btn('pencil', false,true, function(btn){
27747 this.toggleSourceEdit(btn.pressed);
27750 if (this.editor.btns.length > 0) {
27751 for (var i = 0; i<this.editor.btns.length; i++) {
27752 children.push(this.editor.btns[i]);
27760 xns: Roo.bootstrap,
27765 xns: Roo.bootstrap,
27770 cog.menu.items.push({
27772 xns: Roo.bootstrap,
27773 html : Clean styles,
27778 editorcore.insertTag(this.tagname);
27787 this.xtype = 'NavSimplebar';
27789 for(var i=0;i< children.length;i++) {
27791 this.buttons.add(this.addxtypeChild(children[i]));
27795 editor.on('editorevent', this.updateToolbar, this);
27797 onBtnClick : function(id)
27799 this.editorcore.relayCmd(id);
27800 this.editorcore.focus();
27804 * Protected method that will not generally be called directly. It triggers
27805 * a toolbar update by reading the markup state of the current selection in the editor.
27807 updateToolbar: function(){
27809 if(!this.editorcore.activated){
27810 this.editor.onFirstFocus(); // is this neeed?
27814 var btns = this.buttons;
27815 var doc = this.editorcore.doc;
27816 btns.get('bold').setActive(doc.queryCommandState('bold'));
27817 btns.get('italic').setActive(doc.queryCommandState('italic'));
27818 //btns.get('underline').setActive(doc.queryCommandState('underline'));
27820 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
27821 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
27822 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
27824 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
27825 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
27828 var ans = this.editorcore.getAllAncestors();
27829 if (this.formatCombo) {
27832 var store = this.formatCombo.store;
27833 this.formatCombo.setValue("");
27834 for (var i =0; i < ans.length;i++) {
27835 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27837 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27845 // hides menus... - so this cant be on a menu...
27846 Roo.bootstrap.MenuMgr.hideAll();
27848 Roo.bootstrap.MenuMgr.hideAll();
27849 //this.editorsyncValue();
27851 onFirstFocus: function() {
27852 this.buttons.each(function(item){
27856 toggleSourceEdit : function(sourceEditMode){
27859 if(sourceEditMode){
27860 Roo.log("disabling buttons");
27861 this.buttons.each( function(item){
27862 if(item.cmd != 'pencil'){
27868 Roo.log("enabling buttons");
27869 if(this.editorcore.initialized){
27870 this.buttons.each( function(item){
27876 Roo.log("calling toggole on editor");
27877 // tell the editor that it's been pressed..
27878 this.editor.toggleSourceEdit(sourceEditMode);
27892 * @class Roo.bootstrap.Markdown
27893 * @extends Roo.bootstrap.TextArea
27894 * Bootstrap Showdown editable area
27895 * @cfg {string} content
27898 * Create a new Showdown
27901 Roo.bootstrap.Markdown = function(config){
27902 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
27906 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
27910 initEvents : function()
27913 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
27914 this.markdownEl = this.el.createChild({
27915 cls : 'roo-markdown-area'
27917 this.inputEl().addClass('d-none');
27918 if (this.getValue() == '') {
27919 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
27922 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
27924 this.markdownEl.on('click', this.toggleTextEdit, this);
27925 this.on('blur', this.toggleTextEdit, this);
27926 this.on('specialkey', this.resizeTextArea, this);
27929 toggleTextEdit : function()
27931 var sh = this.markdownEl.getHeight();
27932 this.inputEl().addClass('d-none');
27933 this.markdownEl.addClass('d-none');
27934 if (!this.editing) {
27936 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
27937 this.inputEl().removeClass('d-none');
27938 this.inputEl().focus();
27939 this.editing = true;
27942 // show showdown...
27943 this.updateMarkdown();
27944 this.markdownEl.removeClass('d-none');
27945 this.editing = false;
27948 updateMarkdown : function()
27950 if (this.getValue() == '') {
27951 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
27955 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
27958 resizeTextArea: function () {
27961 Roo.log([sh, this.getValue().split("\n").length * 30]);
27962 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
27964 setValue : function(val)
27966 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
27967 if (!this.editing) {
27968 this.updateMarkdown();
27974 if (!this.editing) {
27975 this.toggleTextEdit();
27983 * Ext JS Library 1.1.1
27984 * Copyright(c) 2006-2007, Ext JS, LLC.
27986 * Originally Released Under LGPL - original licence link has changed is not relivant.
27989 * <script type="text/javascript">
27993 * @class Roo.bootstrap.PagingToolbar
27994 * @extends Roo.bootstrap.NavSimplebar
27995 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27997 * Create a new PagingToolbar
27998 * @param {Object} config The config object
27999 * @param {Roo.data.Store} store
28001 Roo.bootstrap.PagingToolbar = function(config)
28003 // old args format still supported... - xtype is prefered..
28004 // created from xtype...
28006 this.ds = config.dataSource;
28008 if (config.store && !this.ds) {
28009 this.store= Roo.factory(config.store, Roo.data);
28010 this.ds = this.store;
28011 this.ds.xmodule = this.xmodule || false;
28014 this.toolbarItems = [];
28015 if (config.items) {
28016 this.toolbarItems = config.items;
28019 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28024 this.bind(this.ds);
28027 if (Roo.bootstrap.version == 4) {
28028 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28030 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
28035 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
28037 * @cfg {Roo.data.Store} dataSource
28038 * The underlying data store providing the paged data
28041 * @cfg {String/HTMLElement/Element} container
28042 * container The id or element that will contain the toolbar
28045 * @cfg {Boolean} displayInfo
28046 * True to display the displayMsg (defaults to false)
28049 * @cfg {Number} pageSize
28050 * The number of records to display per page (defaults to 20)
28054 * @cfg {String} displayMsg
28055 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28057 displayMsg : 'Displaying {0} - {1} of {2}',
28059 * @cfg {String} emptyMsg
28060 * The message to display when no records are found (defaults to "No data to display")
28062 emptyMsg : 'No data to display',
28064 * Customizable piece of the default paging text (defaults to "Page")
28067 beforePageText : "Page",
28069 * Customizable piece of the default paging text (defaults to "of %0")
28072 afterPageText : "of {0}",
28074 * Customizable piece of the default paging text (defaults to "First Page")
28077 firstText : "First Page",
28079 * Customizable piece of the default paging text (defaults to "Previous Page")
28082 prevText : "Previous Page",
28084 * Customizable piece of the default paging text (defaults to "Next Page")
28087 nextText : "Next Page",
28089 * Customizable piece of the default paging text (defaults to "Last Page")
28092 lastText : "Last Page",
28094 * Customizable piece of the default paging text (defaults to "Refresh")
28097 refreshText : "Refresh",
28101 onRender : function(ct, position)
28103 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28104 this.navgroup.parentId = this.id;
28105 this.navgroup.onRender(this.el, null);
28106 // add the buttons to the navgroup
28108 if(this.displayInfo){
28109 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28110 this.displayEl = this.el.select('.x-paging-info', true).first();
28111 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28112 // this.displayEl = navel.el.select('span',true).first();
28118 Roo.each(_this.buttons, function(e){ // this might need to use render????
28119 Roo.factory(e).render(_this.el);
28123 Roo.each(_this.toolbarItems, function(e) {
28124 _this.navgroup.addItem(e);
28128 this.first = this.navgroup.addItem({
28129 tooltip: this.firstText,
28130 cls: "prev btn-outline-secondary",
28131 html : ' <i class="fa fa-step-backward"></i>',
28133 preventDefault: true,
28134 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28137 this.prev = this.navgroup.addItem({
28138 tooltip: this.prevText,
28139 cls: "prev btn-outline-secondary",
28140 html : ' <i class="fa fa-backward"></i>',
28142 preventDefault: true,
28143 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
28145 //this.addSeparator();
28148 var field = this.navgroup.addItem( {
28150 cls : 'x-paging-position btn-outline-secondary',
28152 html : this.beforePageText +
28153 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28154 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
28157 this.field = field.el.select('input', true).first();
28158 this.field.on("keydown", this.onPagingKeydown, this);
28159 this.field.on("focus", function(){this.dom.select();});
28162 this.afterTextEl = field.el.select('.x-paging-after',true).first();
28163 //this.field.setHeight(18);
28164 //this.addSeparator();
28165 this.next = this.navgroup.addItem({
28166 tooltip: this.nextText,
28167 cls: "next btn-outline-secondary",
28168 html : ' <i class="fa fa-forward"></i>',
28170 preventDefault: true,
28171 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
28173 this.last = this.navgroup.addItem({
28174 tooltip: this.lastText,
28175 html : ' <i class="fa fa-step-forward"></i>',
28176 cls: "next btn-outline-secondary",
28178 preventDefault: true,
28179 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
28181 //this.addSeparator();
28182 this.loading = this.navgroup.addItem({
28183 tooltip: this.refreshText,
28184 cls: "btn-outline-secondary",
28185 html : ' <i class="fa fa-refresh"></i>',
28186 preventDefault: true,
28187 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28193 updateInfo : function(){
28194 if(this.displayEl){
28195 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28196 var msg = count == 0 ?
28200 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
28202 this.displayEl.update(msg);
28207 onLoad : function(ds, r, o)
28209 this.cursor = o.params && o.params.start ? o.params.start : 0;
28211 var d = this.getPageData(),
28216 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28217 this.field.dom.value = ap;
28218 this.first.setDisabled(ap == 1);
28219 this.prev.setDisabled(ap == 1);
28220 this.next.setDisabled(ap == ps);
28221 this.last.setDisabled(ap == ps);
28222 this.loading.enable();
28227 getPageData : function(){
28228 var total = this.ds.getTotalCount();
28231 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28232 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28237 onLoadError : function(){
28238 this.loading.enable();
28242 onPagingKeydown : function(e){
28243 var k = e.getKey();
28244 var d = this.getPageData();
28246 var v = this.field.dom.value, pageNum;
28247 if(!v || isNaN(pageNum = parseInt(v, 10))){
28248 this.field.dom.value = d.activePage;
28251 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28252 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28255 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))
28257 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28258 this.field.dom.value = pageNum;
28259 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28262 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28264 var v = this.field.dom.value, pageNum;
28265 var increment = (e.shiftKey) ? 10 : 1;
28266 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28269 if(!v || isNaN(pageNum = parseInt(v, 10))) {
28270 this.field.dom.value = d.activePage;
28273 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28275 this.field.dom.value = parseInt(v, 10) + increment;
28276 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28277 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28284 beforeLoad : function(){
28286 this.loading.disable();
28291 onClick : function(which){
28300 ds.load({params:{start: 0, limit: this.pageSize}});
28303 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28306 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28309 var total = ds.getTotalCount();
28310 var extra = total % this.pageSize;
28311 var lastStart = extra ? (total - extra) : total-this.pageSize;
28312 ds.load({params:{start: lastStart, limit: this.pageSize}});
28315 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28321 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28322 * @param {Roo.data.Store} store The data store to unbind
28324 unbind : function(ds){
28325 ds.un("beforeload", this.beforeLoad, this);
28326 ds.un("load", this.onLoad, this);
28327 ds.un("loadexception", this.onLoadError, this);
28328 ds.un("remove", this.updateInfo, this);
28329 ds.un("add", this.updateInfo, this);
28330 this.ds = undefined;
28334 * Binds the paging toolbar to the specified {@link Roo.data.Store}
28335 * @param {Roo.data.Store} store The data store to bind
28337 bind : function(ds){
28338 ds.on("beforeload", this.beforeLoad, this);
28339 ds.on("load", this.onLoad, this);
28340 ds.on("loadexception", this.onLoadError, this);
28341 ds.on("remove", this.updateInfo, this);
28342 ds.on("add", this.updateInfo, this);
28353 * @class Roo.bootstrap.MessageBar
28354 * @extends Roo.bootstrap.Component
28355 * Bootstrap MessageBar class
28356 * @cfg {String} html contents of the MessageBar
28357 * @cfg {String} weight (info | success | warning | danger) default info
28358 * @cfg {String} beforeClass insert the bar before the given class
28359 * @cfg {Boolean} closable (true | false) default false
28360 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28363 * Create a new Element
28364 * @param {Object} config The config object
28367 Roo.bootstrap.MessageBar = function(config){
28368 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28371 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
28377 beforeClass: 'bootstrap-sticky-wrap',
28379 getAutoCreate : function(){
28383 cls: 'alert alert-dismissable alert-' + this.weight,
28388 html: this.html || ''
28394 cfg.cls += ' alert-messages-fixed';
28408 onRender : function(ct, position)
28410 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28413 var cfg = Roo.apply({}, this.getAutoCreate());
28417 cfg.cls += ' ' + this.cls;
28420 cfg.style = this.style;
28422 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28424 this.el.setVisibilityMode(Roo.Element.DISPLAY);
28427 this.el.select('>button.close').on('click', this.hide, this);
28433 if (!this.rendered) {
28439 this.fireEvent('show', this);
28445 if (!this.rendered) {
28451 this.fireEvent('hide', this);
28454 update : function()
28456 // var e = this.el.dom.firstChild;
28458 // if(this.closable){
28459 // e = e.nextSibling;
28462 // e.data = this.html || '';
28464 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28480 * @class Roo.bootstrap.Graph
28481 * @extends Roo.bootstrap.Component
28482 * Bootstrap Graph class
28486 @cfg {String} graphtype bar | vbar | pie
28487 @cfg {number} g_x coodinator | centre x (pie)
28488 @cfg {number} g_y coodinator | centre y (pie)
28489 @cfg {number} g_r radius (pie)
28490 @cfg {number} g_height height of the chart (respected by all elements in the set)
28491 @cfg {number} g_width width of the chart (respected by all elements in the set)
28492 @cfg {Object} title The title of the chart
28495 -opts (object) options for the chart
28497 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28498 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28500 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.
28501 o stacked (boolean) whether or not to tread values as in a stacked bar chart
28503 o stretch (boolean)
28505 -opts (object) options for the pie
28508 o startAngle (number)
28509 o endAngle (number)
28513 * Create a new Input
28514 * @param {Object} config The config object
28517 Roo.bootstrap.Graph = function(config){
28518 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28524 * The img click event for the img.
28525 * @param {Roo.EventObject} e
28531 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
28542 //g_colors: this.colors,
28549 getAutoCreate : function(){
28560 onRender : function(ct,position){
28563 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28565 if (typeof(Raphael) == 'undefined') {
28566 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28570 this.raphael = Raphael(this.el.dom);
28572 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28573 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28574 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28575 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28577 r.text(160, 10, "Single Series Chart").attr(txtattr);
28578 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28579 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28580 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28582 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28583 r.barchart(330, 10, 300, 220, data1);
28584 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28585 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28588 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28589 // r.barchart(30, 30, 560, 250, xdata, {
28590 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28591 // axis : "0 0 1 1",
28592 // axisxlabels : xdata
28593 // //yvalues : cols,
28596 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28598 // this.load(null,xdata,{
28599 // axis : "0 0 1 1",
28600 // axisxlabels : xdata
28605 load : function(graphtype,xdata,opts)
28607 this.raphael.clear();
28609 graphtype = this.graphtype;
28614 var r = this.raphael,
28615 fin = function () {
28616 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28618 fout = function () {
28619 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28621 pfin = function() {
28622 this.sector.stop();
28623 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28626 this.label[0].stop();
28627 this.label[0].attr({ r: 7.5 });
28628 this.label[1].attr({ "font-weight": 800 });
28631 pfout = function() {
28632 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28635 this.label[0].animate({ r: 5 }, 500, "bounce");
28636 this.label[1].attr({ "font-weight": 400 });
28642 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28645 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28648 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
28649 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28651 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28658 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28663 setTitle: function(o)
28668 initEvents: function() {
28671 this.el.on('click', this.onClick, this);
28675 onClick : function(e)
28677 Roo.log('img onclick');
28678 this.fireEvent('click', this, e);
28690 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28693 * @class Roo.bootstrap.dash.NumberBox
28694 * @extends Roo.bootstrap.Component
28695 * Bootstrap NumberBox class
28696 * @cfg {String} headline Box headline
28697 * @cfg {String} content Box content
28698 * @cfg {String} icon Box icon
28699 * @cfg {String} footer Footer text
28700 * @cfg {String} fhref Footer href
28703 * Create a new NumberBox
28704 * @param {Object} config The config object
28708 Roo.bootstrap.dash.NumberBox = function(config){
28709 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28713 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
28722 getAutoCreate : function(){
28726 cls : 'small-box ',
28734 cls : 'roo-headline',
28735 html : this.headline
28739 cls : 'roo-content',
28740 html : this.content
28754 cls : 'ion ' + this.icon
28763 cls : 'small-box-footer',
28764 href : this.fhref || '#',
28768 cfg.cn.push(footer);
28775 onRender : function(ct,position){
28776 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28783 setHeadline: function (value)
28785 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28788 setFooter: function (value, href)
28790 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28793 this.el.select('a.small-box-footer',true).first().attr('href', href);
28798 setContent: function (value)
28800 this.el.select('.roo-content',true).first().dom.innerHTML = value;
28803 initEvents: function()
28817 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28820 * @class Roo.bootstrap.dash.TabBox
28821 * @extends Roo.bootstrap.Component
28822 * Bootstrap TabBox class
28823 * @cfg {String} title Title of the TabBox
28824 * @cfg {String} icon Icon of the TabBox
28825 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28826 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28829 * Create a new TabBox
28830 * @param {Object} config The config object
28834 Roo.bootstrap.dash.TabBox = function(config){
28835 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28840 * When a pane is added
28841 * @param {Roo.bootstrap.dash.TabPane} pane
28845 * @event activatepane
28846 * When a pane is activated
28847 * @param {Roo.bootstrap.dash.TabPane} pane
28849 "activatepane" : true
28857 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28862 tabScrollable : false,
28864 getChildContainer : function()
28866 return this.el.select('.tab-content', true).first();
28869 getAutoCreate : function(){
28873 cls: 'pull-left header',
28881 cls: 'fa ' + this.icon
28887 cls: 'nav nav-tabs pull-right',
28893 if(this.tabScrollable){
28900 cls: 'nav nav-tabs pull-right',
28911 cls: 'nav-tabs-custom',
28916 cls: 'tab-content no-padding',
28924 initEvents : function()
28926 //Roo.log('add add pane handler');
28927 this.on('addpane', this.onAddPane, this);
28930 * Updates the box title
28931 * @param {String} html to set the title to.
28933 setTitle : function(value)
28935 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28937 onAddPane : function(pane)
28939 this.panes.push(pane);
28940 //Roo.log('addpane');
28942 // tabs are rendere left to right..
28943 if(!this.showtabs){
28947 var ctr = this.el.select('.nav-tabs', true).first();
28950 var existing = ctr.select('.nav-tab',true);
28951 var qty = existing.getCount();;
28954 var tab = ctr.createChild({
28956 cls : 'nav-tab' + (qty ? '' : ' active'),
28964 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28967 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28969 pane.el.addClass('active');
28974 onTabClick : function(ev,un,ob,pane)
28976 //Roo.log('tab - prev default');
28977 ev.preventDefault();
28980 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28981 pane.tab.addClass('active');
28982 //Roo.log(pane.title);
28983 this.getChildContainer().select('.tab-pane',true).removeClass('active');
28984 // technically we should have a deactivate event.. but maybe add later.
28985 // and it should not de-activate the selected tab...
28986 this.fireEvent('activatepane', pane);
28987 pane.el.addClass('active');
28988 pane.fireEvent('activate');
28993 getActivePane : function()
28996 Roo.each(this.panes, function(p) {
28997 if(p.el.hasClass('active')){
29018 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29020 * @class Roo.bootstrap.TabPane
29021 * @extends Roo.bootstrap.Component
29022 * Bootstrap TabPane class
29023 * @cfg {Boolean} active (false | true) Default false
29024 * @cfg {String} title title of panel
29028 * Create a new TabPane
29029 * @param {Object} config The config object
29032 Roo.bootstrap.dash.TabPane = function(config){
29033 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29039 * When a pane is activated
29040 * @param {Roo.bootstrap.dash.TabPane} pane
29047 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
29052 // the tabBox that this is attached to.
29055 getAutoCreate : function()
29063 cfg.cls += ' active';
29068 initEvents : function()
29070 //Roo.log('trigger add pane handler');
29071 this.parent().fireEvent('addpane', this)
29075 * Updates the tab title
29076 * @param {String} html to set the title to.
29078 setTitle: function(str)
29084 this.tab.select('a', true).first().dom.innerHTML = str;
29101 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29104 * @class Roo.bootstrap.menu.Menu
29105 * @extends Roo.bootstrap.Component
29106 * Bootstrap Menu class - container for Menu
29107 * @cfg {String} html Text of the menu
29108 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
29109 * @cfg {String} icon Font awesome icon
29110 * @cfg {String} pos Menu align to (top | bottom) default bottom
29114 * Create a new Menu
29115 * @param {Object} config The config object
29119 Roo.bootstrap.menu.Menu = function(config){
29120 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
29124 * @event beforeshow
29125 * Fires before this menu is displayed
29126 * @param {Roo.bootstrap.menu.Menu} this
29130 * @event beforehide
29131 * Fires before this menu is hidden
29132 * @param {Roo.bootstrap.menu.Menu} this
29137 * Fires after this menu is displayed
29138 * @param {Roo.bootstrap.menu.Menu} this
29143 * Fires after this menu is hidden
29144 * @param {Roo.bootstrap.menu.Menu} this
29149 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
29150 * @param {Roo.bootstrap.menu.Menu} this
29151 * @param {Roo.EventObject} e
29158 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
29162 weight : 'default',
29167 getChildContainer : function() {
29168 if(this.isSubMenu){
29172 return this.el.select('ul.dropdown-menu', true).first();
29175 getAutoCreate : function()
29180 cls : 'roo-menu-text',
29188 cls : 'fa ' + this.icon
29199 cls : 'dropdown-button btn btn-' + this.weight,
29204 cls : 'dropdown-toggle btn btn-' + this.weight,
29214 cls : 'dropdown-menu'
29220 if(this.pos == 'top'){
29221 cfg.cls += ' dropup';
29224 if(this.isSubMenu){
29227 cls : 'dropdown-menu'
29234 onRender : function(ct, position)
29236 this.isSubMenu = ct.hasClass('dropdown-submenu');
29238 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
29241 initEvents : function()
29243 if(this.isSubMenu){
29247 this.hidden = true;
29249 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
29250 this.triggerEl.on('click', this.onTriggerPress, this);
29252 this.buttonEl = this.el.select('button.dropdown-button', true).first();
29253 this.buttonEl.on('click', this.onClick, this);
29259 if(this.isSubMenu){
29263 return this.el.select('ul.dropdown-menu', true).first();
29266 onClick : function(e)
29268 this.fireEvent("click", this, e);
29271 onTriggerPress : function(e)
29273 if (this.isVisible()) {
29280 isVisible : function(){
29281 return !this.hidden;
29286 this.fireEvent("beforeshow", this);
29288 this.hidden = false;
29289 this.el.addClass('open');
29291 Roo.get(document).on("mouseup", this.onMouseUp, this);
29293 this.fireEvent("show", this);
29300 this.fireEvent("beforehide", this);
29302 this.hidden = true;
29303 this.el.removeClass('open');
29305 Roo.get(document).un("mouseup", this.onMouseUp);
29307 this.fireEvent("hide", this);
29310 onMouseUp : function()
29324 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29327 * @class Roo.bootstrap.menu.Item
29328 * @extends Roo.bootstrap.Component
29329 * Bootstrap MenuItem class
29330 * @cfg {Boolean} submenu (true | false) default false
29331 * @cfg {String} html text of the item
29332 * @cfg {String} href the link
29333 * @cfg {Boolean} disable (true | false) default false
29334 * @cfg {Boolean} preventDefault (true | false) default true
29335 * @cfg {String} icon Font awesome icon
29336 * @cfg {String} pos Submenu align to (left | right) default right
29340 * Create a new Item
29341 * @param {Object} config The config object
29345 Roo.bootstrap.menu.Item = function(config){
29346 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
29350 * Fires when the mouse is hovering over this menu
29351 * @param {Roo.bootstrap.menu.Item} this
29352 * @param {Roo.EventObject} e
29357 * Fires when the mouse exits this menu
29358 * @param {Roo.bootstrap.menu.Item} this
29359 * @param {Roo.EventObject} e
29365 * The raw click event for the entire grid.
29366 * @param {Roo.EventObject} e
29372 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
29377 preventDefault: true,
29382 getAutoCreate : function()
29387 cls : 'roo-menu-item-text',
29395 cls : 'fa ' + this.icon
29404 href : this.href || '#',
29411 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
29415 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
29417 if(this.pos == 'left'){
29418 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
29425 initEvents : function()
29427 this.el.on('mouseover', this.onMouseOver, this);
29428 this.el.on('mouseout', this.onMouseOut, this);
29430 this.el.select('a', true).first().on('click', this.onClick, this);
29434 onClick : function(e)
29436 if(this.preventDefault){
29437 e.preventDefault();
29440 this.fireEvent("click", this, e);
29443 onMouseOver : function(e)
29445 if(this.submenu && this.pos == 'left'){
29446 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29449 this.fireEvent("mouseover", this, e);
29452 onMouseOut : function(e)
29454 this.fireEvent("mouseout", this, e);
29466 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29469 * @class Roo.bootstrap.menu.Separator
29470 * @extends Roo.bootstrap.Component
29471 * Bootstrap Separator class
29474 * Create a new Separator
29475 * @param {Object} config The config object
29479 Roo.bootstrap.menu.Separator = function(config){
29480 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29483 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
29485 getAutoCreate : function(){
29488 cls: 'dropdown-divider divider'
29506 * @class Roo.bootstrap.Tooltip
29507 * Bootstrap Tooltip class
29508 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29509 * to determine which dom element triggers the tooltip.
29511 * It needs to add support for additional attributes like tooltip-position
29514 * Create a new Toolti
29515 * @param {Object} config The config object
29518 Roo.bootstrap.Tooltip = function(config){
29519 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29521 this.alignment = Roo.bootstrap.Tooltip.alignment;
29523 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29524 this.alignment = config.alignment;
29529 Roo.apply(Roo.bootstrap.Tooltip, {
29531 * @function init initialize tooltip monitoring.
29535 currentTip : false,
29536 currentRegion : false,
29542 Roo.get(document).on('mouseover', this.enter ,this);
29543 Roo.get(document).on('mouseout', this.leave, this);
29546 this.currentTip = new Roo.bootstrap.Tooltip();
29549 enter : function(ev)
29551 var dom = ev.getTarget();
29553 //Roo.log(['enter',dom]);
29554 var el = Roo.fly(dom);
29555 if (this.currentEl) {
29557 //Roo.log(this.currentEl);
29558 //Roo.log(this.currentEl.contains(dom));
29559 if (this.currentEl == el) {
29562 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29568 if (this.currentTip.el) {
29569 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29573 if(!el || el.dom == document){
29579 if (!el.attr('tooltip')) {
29580 pel = el.findParent("[tooltip]");
29582 bindEl = Roo.get(pel);
29588 // you can not look for children, as if el is the body.. then everythign is the child..
29589 if (!pel && !el.attr('tooltip')) { //
29590 if (!el.select("[tooltip]").elements.length) {
29593 // is the mouse over this child...?
29594 bindEl = el.select("[tooltip]").first();
29595 var xy = ev.getXY();
29596 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29597 //Roo.log("not in region.");
29600 //Roo.log("child element over..");
29603 this.currentEl = el;
29604 this.currentTip.bind(bindEl);
29605 this.currentRegion = Roo.lib.Region.getRegion(dom);
29606 this.currentTip.enter();
29609 leave : function(ev)
29611 var dom = ev.getTarget();
29612 //Roo.log(['leave',dom]);
29613 if (!this.currentEl) {
29618 if (dom != this.currentEl.dom) {
29621 var xy = ev.getXY();
29622 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
29625 // only activate leave if mouse cursor is outside... bounding box..
29630 if (this.currentTip) {
29631 this.currentTip.leave();
29633 //Roo.log('clear currentEl');
29634 this.currentEl = false;
29639 'left' : ['r-l', [-2,0], 'right'],
29640 'right' : ['l-r', [2,0], 'left'],
29641 'bottom' : ['t-b', [0,2], 'top'],
29642 'top' : [ 'b-t', [0,-2], 'bottom']
29648 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
29653 delay : null, // can be { show : 300 , hide: 500}
29657 hoverState : null, //???
29659 placement : 'bottom',
29663 getAutoCreate : function(){
29670 cls : 'tooltip-arrow arrow'
29673 cls : 'tooltip-inner'
29680 bind : function(el)
29685 initEvents : function()
29687 this.arrowEl = this.el.select('.arrow', true).first();
29688 this.innerEl = this.el.select('.tooltip-inner', true).first();
29691 enter : function () {
29693 if (this.timeout != null) {
29694 clearTimeout(this.timeout);
29697 this.hoverState = 'in';
29698 //Roo.log("enter - show");
29699 if (!this.delay || !this.delay.show) {
29704 this.timeout = setTimeout(function () {
29705 if (_t.hoverState == 'in') {
29708 }, this.delay.show);
29712 clearTimeout(this.timeout);
29714 this.hoverState = 'out';
29715 if (!this.delay || !this.delay.hide) {
29721 this.timeout = setTimeout(function () {
29722 //Roo.log("leave - timeout");
29724 if (_t.hoverState == 'out') {
29726 Roo.bootstrap.Tooltip.currentEl = false;
29731 show : function (msg)
29734 this.render(document.body);
29737 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29739 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29741 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29743 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29744 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29746 var placement = typeof this.placement == 'function' ?
29747 this.placement.call(this, this.el, on_el) :
29750 var autoToken = /\s?auto?\s?/i;
29751 var autoPlace = autoToken.test(placement);
29753 placement = placement.replace(autoToken, '') || 'top';
29757 //this.el.setXY([0,0]);
29759 //this.el.dom.style.display='block';
29761 //this.el.appendTo(on_el);
29763 var p = this.getPosition();
29764 var box = this.el.getBox();
29770 var align = this.alignment[placement];
29772 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29774 if(placement == 'top' || placement == 'bottom'){
29776 placement = 'right';
29779 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29780 placement = 'left';
29783 var scroll = Roo.select('body', true).first().getScroll();
29785 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29789 align = this.alignment[placement];
29791 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29795 var elems = document.getElementsByTagName('div');
29796 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29797 for (var i = 0; i < elems.length; i++) {
29798 var zindex = Number.parseInt(
29799 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29802 if (zindex > highest) {
29809 this.el.dom.style.zIndex = highest;
29811 this.el.alignTo(this.bindEl, align[0],align[1]);
29812 //var arrow = this.el.select('.arrow',true).first();
29813 //arrow.set(align[2],
29815 this.el.addClass(placement);
29816 this.el.addClass("bs-tooltip-"+ placement);
29818 this.el.addClass('in fade show');
29820 this.hoverState = null;
29822 if (this.el.hasClass('fade')) {
29837 //this.el.setXY([0,0]);
29838 this.el.removeClass(['show', 'in']);
29854 * @class Roo.bootstrap.LocationPicker
29855 * @extends Roo.bootstrap.Component
29856 * Bootstrap LocationPicker class
29857 * @cfg {Number} latitude Position when init default 0
29858 * @cfg {Number} longitude Position when init default 0
29859 * @cfg {Number} zoom default 15
29860 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29861 * @cfg {Boolean} mapTypeControl default false
29862 * @cfg {Boolean} disableDoubleClickZoom default false
29863 * @cfg {Boolean} scrollwheel default true
29864 * @cfg {Boolean} streetViewControl default false
29865 * @cfg {Number} radius default 0
29866 * @cfg {String} locationName
29867 * @cfg {Boolean} draggable default true
29868 * @cfg {Boolean} enableAutocomplete default false
29869 * @cfg {Boolean} enableReverseGeocode default true
29870 * @cfg {String} markerTitle
29873 * Create a new LocationPicker
29874 * @param {Object} config The config object
29878 Roo.bootstrap.LocationPicker = function(config){
29880 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29885 * Fires when the picker initialized.
29886 * @param {Roo.bootstrap.LocationPicker} this
29887 * @param {Google Location} location
29891 * @event positionchanged
29892 * Fires when the picker position changed.
29893 * @param {Roo.bootstrap.LocationPicker} this
29894 * @param {Google Location} location
29896 positionchanged : true,
29899 * Fires when the map resize.
29900 * @param {Roo.bootstrap.LocationPicker} this
29905 * Fires when the map show.
29906 * @param {Roo.bootstrap.LocationPicker} this
29911 * Fires when the map hide.
29912 * @param {Roo.bootstrap.LocationPicker} this
29917 * Fires when click the map.
29918 * @param {Roo.bootstrap.LocationPicker} this
29919 * @param {Map event} e
29923 * @event mapRightClick
29924 * Fires when right click the map.
29925 * @param {Roo.bootstrap.LocationPicker} this
29926 * @param {Map event} e
29928 mapRightClick : true,
29930 * @event markerClick
29931 * Fires when click the marker.
29932 * @param {Roo.bootstrap.LocationPicker} this
29933 * @param {Map event} e
29935 markerClick : true,
29937 * @event markerRightClick
29938 * Fires when right click the marker.
29939 * @param {Roo.bootstrap.LocationPicker} this
29940 * @param {Map event} e
29942 markerRightClick : true,
29944 * @event OverlayViewDraw
29945 * Fires when OverlayView Draw
29946 * @param {Roo.bootstrap.LocationPicker} this
29948 OverlayViewDraw : true,
29950 * @event OverlayViewOnAdd
29951 * Fires when OverlayView Draw
29952 * @param {Roo.bootstrap.LocationPicker} this
29954 OverlayViewOnAdd : true,
29956 * @event OverlayViewOnRemove
29957 * Fires when OverlayView Draw
29958 * @param {Roo.bootstrap.LocationPicker} this
29960 OverlayViewOnRemove : true,
29962 * @event OverlayViewShow
29963 * Fires when OverlayView Draw
29964 * @param {Roo.bootstrap.LocationPicker} this
29965 * @param {Pixel} cpx
29967 OverlayViewShow : true,
29969 * @event OverlayViewHide
29970 * Fires when OverlayView Draw
29971 * @param {Roo.bootstrap.LocationPicker} this
29973 OverlayViewHide : true,
29975 * @event loadexception
29976 * Fires when load google lib failed.
29977 * @param {Roo.bootstrap.LocationPicker} this
29979 loadexception : true
29984 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
29986 gMapContext: false,
29992 mapTypeControl: false,
29993 disableDoubleClickZoom: false,
29995 streetViewControl: false,
29999 enableAutocomplete: false,
30000 enableReverseGeocode: true,
30003 getAutoCreate: function()
30008 cls: 'roo-location-picker'
30014 initEvents: function(ct, position)
30016 if(!this.el.getWidth() || this.isApplied()){
30020 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30025 initial: function()
30027 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30028 this.fireEvent('loadexception', this);
30032 if(!this.mapTypeId){
30033 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30036 this.gMapContext = this.GMapContext();
30038 this.initOverlayView();
30040 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30044 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30045 _this.setPosition(_this.gMapContext.marker.position);
30048 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30049 _this.fireEvent('mapClick', this, event);
30053 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30054 _this.fireEvent('mapRightClick', this, event);
30058 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30059 _this.fireEvent('markerClick', this, event);
30063 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30064 _this.fireEvent('markerRightClick', this, event);
30068 this.setPosition(this.gMapContext.location);
30070 this.fireEvent('initial', this, this.gMapContext.location);
30073 initOverlayView: function()
30077 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30081 _this.fireEvent('OverlayViewDraw', _this);
30086 _this.fireEvent('OverlayViewOnAdd', _this);
30089 onRemove: function()
30091 _this.fireEvent('OverlayViewOnRemove', _this);
30094 show: function(cpx)
30096 _this.fireEvent('OverlayViewShow', _this, cpx);
30101 _this.fireEvent('OverlayViewHide', _this);
30107 fromLatLngToContainerPixel: function(event)
30109 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30112 isApplied: function()
30114 return this.getGmapContext() == false ? false : true;
30117 getGmapContext: function()
30119 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30122 GMapContext: function()
30124 var position = new google.maps.LatLng(this.latitude, this.longitude);
30126 var _map = new google.maps.Map(this.el.dom, {
30129 mapTypeId: this.mapTypeId,
30130 mapTypeControl: this.mapTypeControl,
30131 disableDoubleClickZoom: this.disableDoubleClickZoom,
30132 scrollwheel: this.scrollwheel,
30133 streetViewControl: this.streetViewControl,
30134 locationName: this.locationName,
30135 draggable: this.draggable,
30136 enableAutocomplete: this.enableAutocomplete,
30137 enableReverseGeocode: this.enableReverseGeocode
30140 var _marker = new google.maps.Marker({
30141 position: position,
30143 title: this.markerTitle,
30144 draggable: this.draggable
30151 location: position,
30152 radius: this.radius,
30153 locationName: this.locationName,
30154 addressComponents: {
30155 formatted_address: null,
30156 addressLine1: null,
30157 addressLine2: null,
30159 streetNumber: null,
30163 stateOrProvince: null
30166 domContainer: this.el.dom,
30167 geodecoder: new google.maps.Geocoder()
30171 drawCircle: function(center, radius, options)
30173 if (this.gMapContext.circle != null) {
30174 this.gMapContext.circle.setMap(null);
30178 options = Roo.apply({}, options, {
30179 strokeColor: "#0000FF",
30180 strokeOpacity: .35,
30182 fillColor: "#0000FF",
30186 options.map = this.gMapContext.map;
30187 options.radius = radius;
30188 options.center = center;
30189 this.gMapContext.circle = new google.maps.Circle(options);
30190 return this.gMapContext.circle;
30196 setPosition: function(location)
30198 this.gMapContext.location = location;
30199 this.gMapContext.marker.setPosition(location);
30200 this.gMapContext.map.panTo(location);
30201 this.drawCircle(location, this.gMapContext.radius, {});
30205 if (this.gMapContext.settings.enableReverseGeocode) {
30206 this.gMapContext.geodecoder.geocode({
30207 latLng: this.gMapContext.location
30208 }, function(results, status) {
30210 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30211 _this.gMapContext.locationName = results[0].formatted_address;
30212 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30214 _this.fireEvent('positionchanged', this, location);
30221 this.fireEvent('positionchanged', this, location);
30226 google.maps.event.trigger(this.gMapContext.map, "resize");
30228 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30230 this.fireEvent('resize', this);
30233 setPositionByLatLng: function(latitude, longitude)
30235 this.setPosition(new google.maps.LatLng(latitude, longitude));
30238 getCurrentPosition: function()
30241 latitude: this.gMapContext.location.lat(),
30242 longitude: this.gMapContext.location.lng()
30246 getAddressName: function()
30248 return this.gMapContext.locationName;
30251 getAddressComponents: function()
30253 return this.gMapContext.addressComponents;
30256 address_component_from_google_geocode: function(address_components)
30260 for (var i = 0; i < address_components.length; i++) {
30261 var component = address_components[i];
30262 if (component.types.indexOf("postal_code") >= 0) {
30263 result.postalCode = component.short_name;
30264 } else if (component.types.indexOf("street_number") >= 0) {
30265 result.streetNumber = component.short_name;
30266 } else if (component.types.indexOf("route") >= 0) {
30267 result.streetName = component.short_name;
30268 } else if (component.types.indexOf("neighborhood") >= 0) {
30269 result.city = component.short_name;
30270 } else if (component.types.indexOf("locality") >= 0) {
30271 result.city = component.short_name;
30272 } else if (component.types.indexOf("sublocality") >= 0) {
30273 result.district = component.short_name;
30274 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30275 result.stateOrProvince = component.short_name;
30276 } else if (component.types.indexOf("country") >= 0) {
30277 result.country = component.short_name;
30281 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30282 result.addressLine2 = "";
30286 setZoomLevel: function(zoom)
30288 this.gMapContext.map.setZoom(zoom);
30301 this.fireEvent('show', this);
30312 this.fireEvent('hide', this);
30317 Roo.apply(Roo.bootstrap.LocationPicker, {
30319 OverlayView : function(map, options)
30321 options = options || {};
30328 * @class Roo.bootstrap.Alert
30329 * @extends Roo.bootstrap.Component
30330 * Bootstrap Alert class - shows an alert area box
30332 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30333 Enter a valid email address
30336 * @cfg {String} title The title of alert
30337 * @cfg {String} html The content of alert
30338 * @cfg {String} weight (success|info|warning|danger) Weight of the message
30339 * @cfg {String} fa font-awesomeicon
30340 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30341 * @cfg {Boolean} close true to show a x closer
30345 * Create a new alert
30346 * @param {Object} config The config object
30350 Roo.bootstrap.Alert = function(config){
30351 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30355 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
30361 faicon: false, // BC
30365 getAutoCreate : function()
30377 style : this.close ? '' : 'display:none'
30381 cls : 'roo-alert-icon'
30386 cls : 'roo-alert-title',
30391 cls : 'roo-alert-text',
30398 cfg.cn[0].cls += ' fa ' + this.faicon;
30401 cfg.cn[0].cls += ' fa ' + this.fa;
30405 cfg.cls += ' alert-' + this.weight;
30411 initEvents: function()
30413 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30414 this.titleEl = this.el.select('.roo-alert-title',true).first();
30415 this.iconEl = this.el.select('.roo-alert-icon',true).first();
30416 this.htmlEl = this.el.select('.roo-alert-text',true).first();
30417 if (this.seconds > 0) {
30418 this.hide.defer(this.seconds, this);
30422 * Set the Title Message HTML
30423 * @param {String} html
30425 setTitle : function(str)
30427 this.titleEl.dom.innerHTML = str;
30431 * Set the Body Message HTML
30432 * @param {String} html
30434 setHtml : function(str)
30436 this.htmlEl.dom.innerHTML = str;
30439 * Set the Weight of the alert
30440 * @param {String} (success|info|warning|danger) weight
30443 setWeight : function(weight)
30446 this.el.removeClass('alert-' + this.weight);
30449 this.weight = weight;
30451 this.el.addClass('alert-' + this.weight);
30454 * Set the Icon of the alert
30455 * @param {String} see fontawsome names (name without the 'fa-' bit)
30457 setIcon : function(icon)
30460 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30463 this.faicon = icon;
30465 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30490 * @class Roo.bootstrap.UploadCropbox
30491 * @extends Roo.bootstrap.Component
30492 * Bootstrap UploadCropbox class
30493 * @cfg {String} emptyText show when image has been loaded
30494 * @cfg {String} rotateNotify show when image too small to rotate
30495 * @cfg {Number} errorTimeout default 3000
30496 * @cfg {Number} minWidth default 300
30497 * @cfg {Number} minHeight default 300
30498 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30499 * @cfg {Boolean} isDocument (true|false) default false
30500 * @cfg {String} url action url
30501 * @cfg {String} paramName default 'imageUpload'
30502 * @cfg {String} method default POST
30503 * @cfg {Boolean} loadMask (true|false) default true
30504 * @cfg {Boolean} loadingText default 'Loading...'
30507 * Create a new UploadCropbox
30508 * @param {Object} config The config object
30511 Roo.bootstrap.UploadCropbox = function(config){
30512 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30516 * @event beforeselectfile
30517 * Fire before select file
30518 * @param {Roo.bootstrap.UploadCropbox} this
30520 "beforeselectfile" : true,
30523 * Fire after initEvent
30524 * @param {Roo.bootstrap.UploadCropbox} this
30529 * Fire after initEvent
30530 * @param {Roo.bootstrap.UploadCropbox} this
30531 * @param {String} data
30536 * Fire when preparing the file data
30537 * @param {Roo.bootstrap.UploadCropbox} this
30538 * @param {Object} file
30543 * Fire when get exception
30544 * @param {Roo.bootstrap.UploadCropbox} this
30545 * @param {XMLHttpRequest} xhr
30547 "exception" : true,
30549 * @event beforeloadcanvas
30550 * Fire before load the canvas
30551 * @param {Roo.bootstrap.UploadCropbox} this
30552 * @param {String} src
30554 "beforeloadcanvas" : true,
30557 * Fire when trash image
30558 * @param {Roo.bootstrap.UploadCropbox} this
30563 * Fire when download the image
30564 * @param {Roo.bootstrap.UploadCropbox} this
30568 * @event footerbuttonclick
30569 * Fire when footerbuttonclick
30570 * @param {Roo.bootstrap.UploadCropbox} this
30571 * @param {String} type
30573 "footerbuttonclick" : true,
30577 * @param {Roo.bootstrap.UploadCropbox} this
30582 * Fire when rotate the image
30583 * @param {Roo.bootstrap.UploadCropbox} this
30584 * @param {String} pos
30589 * Fire when inspect the file
30590 * @param {Roo.bootstrap.UploadCropbox} this
30591 * @param {Object} file
30596 * Fire when xhr upload the file
30597 * @param {Roo.bootstrap.UploadCropbox} this
30598 * @param {Object} data
30603 * Fire when arrange the file data
30604 * @param {Roo.bootstrap.UploadCropbox} this
30605 * @param {Object} formData
30610 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30613 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
30615 emptyText : 'Click to upload image',
30616 rotateNotify : 'Image is too small to rotate',
30617 errorTimeout : 3000,
30631 cropType : 'image/jpeg',
30633 canvasLoaded : false,
30634 isDocument : false,
30636 paramName : 'imageUpload',
30638 loadingText : 'Loading...',
30641 getAutoCreate : function()
30645 cls : 'roo-upload-cropbox',
30649 cls : 'roo-upload-cropbox-selector',
30654 cls : 'roo-upload-cropbox-body',
30655 style : 'cursor:pointer',
30659 cls : 'roo-upload-cropbox-preview'
30663 cls : 'roo-upload-cropbox-thumb'
30667 cls : 'roo-upload-cropbox-empty-notify',
30668 html : this.emptyText
30672 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30673 html : this.rotateNotify
30679 cls : 'roo-upload-cropbox-footer',
30682 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30692 onRender : function(ct, position)
30694 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30696 if (this.buttons.length) {
30698 Roo.each(this.buttons, function(bb) {
30700 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30702 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30708 this.maskEl = this.el;
30712 initEvents : function()
30714 this.urlAPI = (window.createObjectURL && window) ||
30715 (window.URL && URL.revokeObjectURL && URL) ||
30716 (window.webkitURL && webkitURL);
30718 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30719 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30721 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30722 this.selectorEl.hide();
30724 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30725 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30727 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30728 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30729 this.thumbEl.hide();
30731 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30732 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30734 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30735 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30736 this.errorEl.hide();
30738 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30739 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30740 this.footerEl.hide();
30742 this.setThumbBoxSize();
30748 this.fireEvent('initial', this);
30755 window.addEventListener("resize", function() { _this.resize(); } );
30757 this.bodyEl.on('click', this.beforeSelectFile, this);
30760 this.bodyEl.on('touchstart', this.onTouchStart, this);
30761 this.bodyEl.on('touchmove', this.onTouchMove, this);
30762 this.bodyEl.on('touchend', this.onTouchEnd, this);
30766 this.bodyEl.on('mousedown', this.onMouseDown, this);
30767 this.bodyEl.on('mousemove', this.onMouseMove, this);
30768 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30769 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30770 Roo.get(document).on('mouseup', this.onMouseUp, this);
30773 this.selectorEl.on('change', this.onFileSelected, this);
30779 this.baseScale = 1;
30781 this.baseRotate = 1;
30782 this.dragable = false;
30783 this.pinching = false;
30786 this.cropData = false;
30787 this.notifyEl.dom.innerHTML = this.emptyText;
30789 this.selectorEl.dom.value = '';
30793 resize : function()
30795 if(this.fireEvent('resize', this) != false){
30796 this.setThumbBoxPosition();
30797 this.setCanvasPosition();
30801 onFooterButtonClick : function(e, el, o, type)
30804 case 'rotate-left' :
30805 this.onRotateLeft(e);
30807 case 'rotate-right' :
30808 this.onRotateRight(e);
30811 this.beforeSelectFile(e);
30826 this.fireEvent('footerbuttonclick', this, type);
30829 beforeSelectFile : function(e)
30831 e.preventDefault();
30833 if(this.fireEvent('beforeselectfile', this) != false){
30834 this.selectorEl.dom.click();
30838 onFileSelected : function(e)
30840 e.preventDefault();
30842 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30846 var file = this.selectorEl.dom.files[0];
30848 if(this.fireEvent('inspect', this, file) != false){
30849 this.prepare(file);
30854 trash : function(e)
30856 this.fireEvent('trash', this);
30859 download : function(e)
30861 this.fireEvent('download', this);
30864 loadCanvas : function(src)
30866 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30870 this.imageEl = document.createElement('img');
30874 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30876 this.imageEl.src = src;
30880 onLoadCanvas : function()
30882 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30883 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30885 this.bodyEl.un('click', this.beforeSelectFile, this);
30887 this.notifyEl.hide();
30888 this.thumbEl.show();
30889 this.footerEl.show();
30891 this.baseRotateLevel();
30893 if(this.isDocument){
30894 this.setThumbBoxSize();
30897 this.setThumbBoxPosition();
30899 this.baseScaleLevel();
30905 this.canvasLoaded = true;
30908 this.maskEl.unmask();
30913 setCanvasPosition : function()
30915 if(!this.canvasEl){
30919 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30920 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30922 this.previewEl.setLeft(pw);
30923 this.previewEl.setTop(ph);
30927 onMouseDown : function(e)
30931 this.dragable = true;
30932 this.pinching = false;
30934 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30935 this.dragable = false;
30939 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30940 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30944 onMouseMove : function(e)
30948 if(!this.canvasLoaded){
30952 if (!this.dragable){
30956 var minX = Math.ceil(this.thumbEl.getLeft(true));
30957 var minY = Math.ceil(this.thumbEl.getTop(true));
30959 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30960 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30962 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30963 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30965 x = x - this.mouseX;
30966 y = y - this.mouseY;
30968 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30969 var bgY = Math.ceil(y + this.previewEl.getTop(true));
30971 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30972 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30974 this.previewEl.setLeft(bgX);
30975 this.previewEl.setTop(bgY);
30977 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30978 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30981 onMouseUp : function(e)
30985 this.dragable = false;
30988 onMouseWheel : function(e)
30992 this.startScale = this.scale;
30994 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30996 if(!this.zoomable()){
30997 this.scale = this.startScale;
31006 zoomable : function()
31008 var minScale = this.thumbEl.getWidth() / this.minWidth;
31010 if(this.minWidth < this.minHeight){
31011 minScale = this.thumbEl.getHeight() / this.minHeight;
31014 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31015 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31019 (this.rotate == 0 || this.rotate == 180) &&
31021 width > this.imageEl.OriginWidth ||
31022 height > this.imageEl.OriginHeight ||
31023 (width < this.minWidth && height < this.minHeight)
31031 (this.rotate == 90 || this.rotate == 270) &&
31033 width > this.imageEl.OriginWidth ||
31034 height > this.imageEl.OriginHeight ||
31035 (width < this.minHeight && height < this.minWidth)
31042 !this.isDocument &&
31043 (this.rotate == 0 || this.rotate == 180) &&
31045 width < this.minWidth ||
31046 width > this.imageEl.OriginWidth ||
31047 height < this.minHeight ||
31048 height > this.imageEl.OriginHeight
31055 !this.isDocument &&
31056 (this.rotate == 90 || this.rotate == 270) &&
31058 width < this.minHeight ||
31059 width > this.imageEl.OriginWidth ||
31060 height < this.minWidth ||
31061 height > this.imageEl.OriginHeight
31071 onRotateLeft : function(e)
31073 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31075 var minScale = this.thumbEl.getWidth() / this.minWidth;
31077 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31078 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31080 this.startScale = this.scale;
31082 while (this.getScaleLevel() < minScale){
31084 this.scale = this.scale + 1;
31086 if(!this.zoomable()){
31091 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31092 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31097 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31104 this.scale = this.startScale;
31106 this.onRotateFail();
31111 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31113 if(this.isDocument){
31114 this.setThumbBoxSize();
31115 this.setThumbBoxPosition();
31116 this.setCanvasPosition();
31121 this.fireEvent('rotate', this, 'left');
31125 onRotateRight : function(e)
31127 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31129 var minScale = this.thumbEl.getWidth() / this.minWidth;
31131 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31132 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31134 this.startScale = this.scale;
31136 while (this.getScaleLevel() < minScale){
31138 this.scale = this.scale + 1;
31140 if(!this.zoomable()){
31145 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31146 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31151 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31158 this.scale = this.startScale;
31160 this.onRotateFail();
31165 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31167 if(this.isDocument){
31168 this.setThumbBoxSize();
31169 this.setThumbBoxPosition();
31170 this.setCanvasPosition();
31175 this.fireEvent('rotate', this, 'right');
31178 onRotateFail : function()
31180 this.errorEl.show(true);
31184 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31189 this.previewEl.dom.innerHTML = '';
31191 var canvasEl = document.createElement("canvas");
31193 var contextEl = canvasEl.getContext("2d");
31195 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31196 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31197 var center = this.imageEl.OriginWidth / 2;
31199 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31200 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31201 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31202 center = this.imageEl.OriginHeight / 2;
31205 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31207 contextEl.translate(center, center);
31208 contextEl.rotate(this.rotate * Math.PI / 180);
31210 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31212 this.canvasEl = document.createElement("canvas");
31214 this.contextEl = this.canvasEl.getContext("2d");
31216 switch (this.rotate) {
31219 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31220 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31222 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31227 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31228 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31230 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31231 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);
31235 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31240 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31241 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31243 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31244 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);
31248 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);
31253 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31254 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31256 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31257 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31261 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);
31268 this.previewEl.appendChild(this.canvasEl);
31270 this.setCanvasPosition();
31275 if(!this.canvasLoaded){
31279 var imageCanvas = document.createElement("canvas");
31281 var imageContext = imageCanvas.getContext("2d");
31283 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31284 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31286 var center = imageCanvas.width / 2;
31288 imageContext.translate(center, center);
31290 imageContext.rotate(this.rotate * Math.PI / 180);
31292 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31294 var canvas = document.createElement("canvas");
31296 var context = canvas.getContext("2d");
31298 canvas.width = this.minWidth;
31299 canvas.height = this.minHeight;
31301 switch (this.rotate) {
31304 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31305 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31307 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31308 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31310 var targetWidth = this.minWidth - 2 * x;
31311 var targetHeight = this.minHeight - 2 * y;
31315 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31316 scale = targetWidth / width;
31319 if(x > 0 && y == 0){
31320 scale = targetHeight / height;
31323 if(x > 0 && y > 0){
31324 scale = targetWidth / width;
31326 if(width < height){
31327 scale = targetHeight / height;
31331 context.scale(scale, scale);
31333 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31334 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31336 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31337 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31339 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31344 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31345 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31347 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31348 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31350 var targetWidth = this.minWidth - 2 * x;
31351 var targetHeight = this.minHeight - 2 * y;
31355 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31356 scale = targetWidth / width;
31359 if(x > 0 && y == 0){
31360 scale = targetHeight / height;
31363 if(x > 0 && y > 0){
31364 scale = targetWidth / width;
31366 if(width < height){
31367 scale = targetHeight / height;
31371 context.scale(scale, scale);
31373 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31374 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31376 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31377 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31379 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31381 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31386 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31387 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31389 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31390 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31392 var targetWidth = this.minWidth - 2 * x;
31393 var targetHeight = this.minHeight - 2 * y;
31397 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31398 scale = targetWidth / width;
31401 if(x > 0 && y == 0){
31402 scale = targetHeight / height;
31405 if(x > 0 && y > 0){
31406 scale = targetWidth / width;
31408 if(width < height){
31409 scale = targetHeight / height;
31413 context.scale(scale, scale);
31415 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31416 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31418 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31419 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31421 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31422 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31424 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31429 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31430 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31432 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31433 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31435 var targetWidth = this.minWidth - 2 * x;
31436 var targetHeight = this.minHeight - 2 * y;
31440 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31441 scale = targetWidth / width;
31444 if(x > 0 && y == 0){
31445 scale = targetHeight / height;
31448 if(x > 0 && y > 0){
31449 scale = targetWidth / width;
31451 if(width < height){
31452 scale = targetHeight / height;
31456 context.scale(scale, scale);
31458 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31459 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31461 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31462 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31464 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31466 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31473 this.cropData = canvas.toDataURL(this.cropType);
31475 if(this.fireEvent('crop', this, this.cropData) !== false){
31476 this.process(this.file, this.cropData);
31483 setThumbBoxSize : function()
31487 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31488 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31489 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31491 this.minWidth = width;
31492 this.minHeight = height;
31494 if(this.rotate == 90 || this.rotate == 270){
31495 this.minWidth = height;
31496 this.minHeight = width;
31501 width = Math.ceil(this.minWidth * height / this.minHeight);
31503 if(this.minWidth > this.minHeight){
31505 height = Math.ceil(this.minHeight * width / this.minWidth);
31508 this.thumbEl.setStyle({
31509 width : width + 'px',
31510 height : height + 'px'
31517 setThumbBoxPosition : function()
31519 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31520 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31522 this.thumbEl.setLeft(x);
31523 this.thumbEl.setTop(y);
31527 baseRotateLevel : function()
31529 this.baseRotate = 1;
31532 typeof(this.exif) != 'undefined' &&
31533 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31534 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31536 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31539 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31543 baseScaleLevel : function()
31547 if(this.isDocument){
31549 if(this.baseRotate == 6 || this.baseRotate == 8){
31551 height = this.thumbEl.getHeight();
31552 this.baseScale = height / this.imageEl.OriginWidth;
31554 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31555 width = this.thumbEl.getWidth();
31556 this.baseScale = width / this.imageEl.OriginHeight;
31562 height = this.thumbEl.getHeight();
31563 this.baseScale = height / this.imageEl.OriginHeight;
31565 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31566 width = this.thumbEl.getWidth();
31567 this.baseScale = width / this.imageEl.OriginWidth;
31573 if(this.baseRotate == 6 || this.baseRotate == 8){
31575 width = this.thumbEl.getHeight();
31576 this.baseScale = width / this.imageEl.OriginHeight;
31578 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31579 height = this.thumbEl.getWidth();
31580 this.baseScale = height / this.imageEl.OriginHeight;
31583 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31584 height = this.thumbEl.getWidth();
31585 this.baseScale = height / this.imageEl.OriginHeight;
31587 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31588 width = this.thumbEl.getHeight();
31589 this.baseScale = width / this.imageEl.OriginWidth;
31596 width = this.thumbEl.getWidth();
31597 this.baseScale = width / this.imageEl.OriginWidth;
31599 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31600 height = this.thumbEl.getHeight();
31601 this.baseScale = height / this.imageEl.OriginHeight;
31604 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31606 height = this.thumbEl.getHeight();
31607 this.baseScale = height / this.imageEl.OriginHeight;
31609 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31610 width = this.thumbEl.getWidth();
31611 this.baseScale = width / this.imageEl.OriginWidth;
31619 getScaleLevel : function()
31621 return this.baseScale * Math.pow(1.1, this.scale);
31624 onTouchStart : function(e)
31626 if(!this.canvasLoaded){
31627 this.beforeSelectFile(e);
31631 var touches = e.browserEvent.touches;
31637 if(touches.length == 1){
31638 this.onMouseDown(e);
31642 if(touches.length != 2){
31648 for(var i = 0, finger; finger = touches[i]; i++){
31649 coords.push(finger.pageX, finger.pageY);
31652 var x = Math.pow(coords[0] - coords[2], 2);
31653 var y = Math.pow(coords[1] - coords[3], 2);
31655 this.startDistance = Math.sqrt(x + y);
31657 this.startScale = this.scale;
31659 this.pinching = true;
31660 this.dragable = false;
31664 onTouchMove : function(e)
31666 if(!this.pinching && !this.dragable){
31670 var touches = e.browserEvent.touches;
31677 this.onMouseMove(e);
31683 for(var i = 0, finger; finger = touches[i]; i++){
31684 coords.push(finger.pageX, finger.pageY);
31687 var x = Math.pow(coords[0] - coords[2], 2);
31688 var y = Math.pow(coords[1] - coords[3], 2);
31690 this.endDistance = Math.sqrt(x + y);
31692 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31694 if(!this.zoomable()){
31695 this.scale = this.startScale;
31703 onTouchEnd : function(e)
31705 this.pinching = false;
31706 this.dragable = false;
31710 process : function(file, crop)
31713 this.maskEl.mask(this.loadingText);
31716 this.xhr = new XMLHttpRequest();
31718 file.xhr = this.xhr;
31720 this.xhr.open(this.method, this.url, true);
31723 "Accept": "application/json",
31724 "Cache-Control": "no-cache",
31725 "X-Requested-With": "XMLHttpRequest"
31728 for (var headerName in headers) {
31729 var headerValue = headers[headerName];
31731 this.xhr.setRequestHeader(headerName, headerValue);
31737 this.xhr.onload = function()
31739 _this.xhrOnLoad(_this.xhr);
31742 this.xhr.onerror = function()
31744 _this.xhrOnError(_this.xhr);
31747 var formData = new FormData();
31749 formData.append('returnHTML', 'NO');
31752 formData.append('crop', crop);
31755 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31756 formData.append(this.paramName, file, file.name);
31759 if(typeof(file.filename) != 'undefined'){
31760 formData.append('filename', file.filename);
31763 if(typeof(file.mimetype) != 'undefined'){
31764 formData.append('mimetype', file.mimetype);
31767 if(this.fireEvent('arrange', this, formData) != false){
31768 this.xhr.send(formData);
31772 xhrOnLoad : function(xhr)
31775 this.maskEl.unmask();
31778 if (xhr.readyState !== 4) {
31779 this.fireEvent('exception', this, xhr);
31783 var response = Roo.decode(xhr.responseText);
31785 if(!response.success){
31786 this.fireEvent('exception', this, xhr);
31790 var response = Roo.decode(xhr.responseText);
31792 this.fireEvent('upload', this, response);
31796 xhrOnError : function()
31799 this.maskEl.unmask();
31802 Roo.log('xhr on error');
31804 var response = Roo.decode(xhr.responseText);
31810 prepare : function(file)
31813 this.maskEl.mask(this.loadingText);
31819 if(typeof(file) === 'string'){
31820 this.loadCanvas(file);
31824 if(!file || !this.urlAPI){
31829 this.cropType = file.type;
31833 if(this.fireEvent('prepare', this, this.file) != false){
31835 var reader = new FileReader();
31837 reader.onload = function (e) {
31838 if (e.target.error) {
31839 Roo.log(e.target.error);
31843 var buffer = e.target.result,
31844 dataView = new DataView(buffer),
31846 maxOffset = dataView.byteLength - 4,
31850 if (dataView.getUint16(0) === 0xffd8) {
31851 while (offset < maxOffset) {
31852 markerBytes = dataView.getUint16(offset);
31854 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31855 markerLength = dataView.getUint16(offset + 2) + 2;
31856 if (offset + markerLength > dataView.byteLength) {
31857 Roo.log('Invalid meta data: Invalid segment size.');
31861 if(markerBytes == 0xffe1){
31862 _this.parseExifData(
31869 offset += markerLength;
31879 var url = _this.urlAPI.createObjectURL(_this.file);
31881 _this.loadCanvas(url);
31886 reader.readAsArrayBuffer(this.file);
31892 parseExifData : function(dataView, offset, length)
31894 var tiffOffset = offset + 10,
31898 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31899 // No Exif data, might be XMP data instead
31903 // Check for the ASCII code for "Exif" (0x45786966):
31904 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31905 // No Exif data, might be XMP data instead
31908 if (tiffOffset + 8 > dataView.byteLength) {
31909 Roo.log('Invalid Exif data: Invalid segment size.');
31912 // Check for the two null bytes:
31913 if (dataView.getUint16(offset + 8) !== 0x0000) {
31914 Roo.log('Invalid Exif data: Missing byte alignment offset.');
31917 // Check the byte alignment:
31918 switch (dataView.getUint16(tiffOffset)) {
31920 littleEndian = true;
31923 littleEndian = false;
31926 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31929 // Check for the TIFF tag marker (0x002A):
31930 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31931 Roo.log('Invalid Exif data: Missing TIFF marker.');
31934 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31935 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31937 this.parseExifTags(
31940 tiffOffset + dirOffset,
31945 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31950 if (dirOffset + 6 > dataView.byteLength) {
31951 Roo.log('Invalid Exif data: Invalid directory offset.');
31954 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31955 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31956 if (dirEndOffset + 4 > dataView.byteLength) {
31957 Roo.log('Invalid Exif data: Invalid directory size.');
31960 for (i = 0; i < tagsNumber; i += 1) {
31964 dirOffset + 2 + 12 * i, // tag offset
31968 // Return the offset to the next directory:
31969 return dataView.getUint32(dirEndOffset, littleEndian);
31972 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
31974 var tag = dataView.getUint16(offset, littleEndian);
31976 this.exif[tag] = this.getExifValue(
31980 dataView.getUint16(offset + 2, littleEndian), // tag type
31981 dataView.getUint32(offset + 4, littleEndian), // tag length
31986 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31988 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31997 Roo.log('Invalid Exif data: Invalid tag type.');
32001 tagSize = tagType.size * length;
32002 // Determine if the value is contained in the dataOffset bytes,
32003 // or if the value at the dataOffset is a pointer to the actual data:
32004 dataOffset = tagSize > 4 ?
32005 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32006 if (dataOffset + tagSize > dataView.byteLength) {
32007 Roo.log('Invalid Exif data: Invalid data offset.');
32010 if (length === 1) {
32011 return tagType.getValue(dataView, dataOffset, littleEndian);
32014 for (i = 0; i < length; i += 1) {
32015 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32018 if (tagType.ascii) {
32020 // Concatenate the chars:
32021 for (i = 0; i < values.length; i += 1) {
32023 // Ignore the terminating NULL byte(s):
32024 if (c === '\u0000') {
32036 Roo.apply(Roo.bootstrap.UploadCropbox, {
32038 'Orientation': 0x0112
32042 1: 0, //'top-left',
32044 3: 180, //'bottom-right',
32045 // 4: 'bottom-left',
32047 6: 90, //'right-top',
32048 // 7: 'right-bottom',
32049 8: 270 //'left-bottom'
32053 // byte, 8-bit unsigned int:
32055 getValue: function (dataView, dataOffset) {
32056 return dataView.getUint8(dataOffset);
32060 // ascii, 8-bit byte:
32062 getValue: function (dataView, dataOffset) {
32063 return String.fromCharCode(dataView.getUint8(dataOffset));
32068 // short, 16 bit int:
32070 getValue: function (dataView, dataOffset, littleEndian) {
32071 return dataView.getUint16(dataOffset, littleEndian);
32075 // long, 32 bit int:
32077 getValue: function (dataView, dataOffset, littleEndian) {
32078 return dataView.getUint32(dataOffset, littleEndian);
32082 // rational = two long values, first is numerator, second is denominator:
32084 getValue: function (dataView, dataOffset, littleEndian) {
32085 return dataView.getUint32(dataOffset, littleEndian) /
32086 dataView.getUint32(dataOffset + 4, littleEndian);
32090 // slong, 32 bit signed int:
32092 getValue: function (dataView, dataOffset, littleEndian) {
32093 return dataView.getInt32(dataOffset, littleEndian);
32097 // srational, two slongs, first is numerator, second is denominator:
32099 getValue: function (dataView, dataOffset, littleEndian) {
32100 return dataView.getInt32(dataOffset, littleEndian) /
32101 dataView.getInt32(dataOffset + 4, littleEndian);
32111 cls : 'btn-group roo-upload-cropbox-rotate-left',
32112 action : 'rotate-left',
32116 cls : 'btn btn-default',
32117 html : '<i class="fa fa-undo"></i>'
32123 cls : 'btn-group roo-upload-cropbox-picture',
32124 action : 'picture',
32128 cls : 'btn btn-default',
32129 html : '<i class="fa fa-picture-o"></i>'
32135 cls : 'btn-group roo-upload-cropbox-rotate-right',
32136 action : 'rotate-right',
32140 cls : 'btn btn-default',
32141 html : '<i class="fa fa-repeat"></i>'
32149 cls : 'btn-group roo-upload-cropbox-rotate-left',
32150 action : 'rotate-left',
32154 cls : 'btn btn-default',
32155 html : '<i class="fa fa-undo"></i>'
32161 cls : 'btn-group roo-upload-cropbox-download',
32162 action : 'download',
32166 cls : 'btn btn-default',
32167 html : '<i class="fa fa-download"></i>'
32173 cls : 'btn-group roo-upload-cropbox-crop',
32178 cls : 'btn btn-default',
32179 html : '<i class="fa fa-crop"></i>'
32185 cls : 'btn-group roo-upload-cropbox-trash',
32190 cls : 'btn btn-default',
32191 html : '<i class="fa fa-trash"></i>'
32197 cls : 'btn-group roo-upload-cropbox-rotate-right',
32198 action : 'rotate-right',
32202 cls : 'btn btn-default',
32203 html : '<i class="fa fa-repeat"></i>'
32211 cls : 'btn-group roo-upload-cropbox-rotate-left',
32212 action : 'rotate-left',
32216 cls : 'btn btn-default',
32217 html : '<i class="fa fa-undo"></i>'
32223 cls : 'btn-group roo-upload-cropbox-rotate-right',
32224 action : 'rotate-right',
32228 cls : 'btn btn-default',
32229 html : '<i class="fa fa-repeat"></i>'
32242 * @class Roo.bootstrap.DocumentManager
32243 * @extends Roo.bootstrap.Component
32244 * Bootstrap DocumentManager class
32245 * @cfg {String} paramName default 'imageUpload'
32246 * @cfg {String} toolTipName default 'filename'
32247 * @cfg {String} method default POST
32248 * @cfg {String} url action url
32249 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32250 * @cfg {Boolean} multiple multiple upload default true
32251 * @cfg {Number} thumbSize default 300
32252 * @cfg {String} fieldLabel
32253 * @cfg {Number} labelWidth default 4
32254 * @cfg {String} labelAlign (left|top) default left
32255 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32256 * @cfg {Number} labellg set the width of label (1-12)
32257 * @cfg {Number} labelmd set the width of label (1-12)
32258 * @cfg {Number} labelsm set the width of label (1-12)
32259 * @cfg {Number} labelxs set the width of label (1-12)
32262 * Create a new DocumentManager
32263 * @param {Object} config The config object
32266 Roo.bootstrap.DocumentManager = function(config){
32267 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32270 this.delegates = [];
32275 * Fire when initial the DocumentManager
32276 * @param {Roo.bootstrap.DocumentManager} this
32281 * inspect selected file
32282 * @param {Roo.bootstrap.DocumentManager} this
32283 * @param {File} file
32288 * Fire when xhr load exception
32289 * @param {Roo.bootstrap.DocumentManager} this
32290 * @param {XMLHttpRequest} xhr
32292 "exception" : true,
32294 * @event afterupload
32295 * Fire when xhr load exception
32296 * @param {Roo.bootstrap.DocumentManager} this
32297 * @param {XMLHttpRequest} xhr
32299 "afterupload" : true,
32302 * prepare the form data
32303 * @param {Roo.bootstrap.DocumentManager} this
32304 * @param {Object} formData
32309 * Fire when remove the file
32310 * @param {Roo.bootstrap.DocumentManager} this
32311 * @param {Object} file
32316 * Fire after refresh the file
32317 * @param {Roo.bootstrap.DocumentManager} this
32322 * Fire after click the image
32323 * @param {Roo.bootstrap.DocumentManager} this
32324 * @param {Object} file
32329 * Fire when upload a image and editable set to true
32330 * @param {Roo.bootstrap.DocumentManager} this
32331 * @param {Object} file
32335 * @event beforeselectfile
32336 * Fire before select file
32337 * @param {Roo.bootstrap.DocumentManager} this
32339 "beforeselectfile" : true,
32342 * Fire before process file
32343 * @param {Roo.bootstrap.DocumentManager} this
32344 * @param {Object} file
32348 * @event previewrendered
32349 * Fire when preview rendered
32350 * @param {Roo.bootstrap.DocumentManager} this
32351 * @param {Object} file
32353 "previewrendered" : true,
32356 "previewResize" : true
32361 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
32370 paramName : 'imageUpload',
32371 toolTipName : 'filename',
32374 labelAlign : 'left',
32384 getAutoCreate : function()
32386 var managerWidget = {
32388 cls : 'roo-document-manager',
32392 cls : 'roo-document-manager-selector',
32397 cls : 'roo-document-manager-uploader',
32401 cls : 'roo-document-manager-upload-btn',
32402 html : '<i class="fa fa-plus"></i>'
32413 cls : 'column col-md-12',
32418 if(this.fieldLabel.length){
32423 cls : 'column col-md-12',
32424 html : this.fieldLabel
32428 cls : 'column col-md-12',
32433 if(this.labelAlign == 'left'){
32438 html : this.fieldLabel
32447 if(this.labelWidth > 12){
32448 content[0].style = "width: " + this.labelWidth + 'px';
32451 if(this.labelWidth < 13 && this.labelmd == 0){
32452 this.labelmd = this.labelWidth;
32455 if(this.labellg > 0){
32456 content[0].cls += ' col-lg-' + this.labellg;
32457 content[1].cls += ' col-lg-' + (12 - this.labellg);
32460 if(this.labelmd > 0){
32461 content[0].cls += ' col-md-' + this.labelmd;
32462 content[1].cls += ' col-md-' + (12 - this.labelmd);
32465 if(this.labelsm > 0){
32466 content[0].cls += ' col-sm-' + this.labelsm;
32467 content[1].cls += ' col-sm-' + (12 - this.labelsm);
32470 if(this.labelxs > 0){
32471 content[0].cls += ' col-xs-' + this.labelxs;
32472 content[1].cls += ' col-xs-' + (12 - this.labelxs);
32480 cls : 'row clearfix',
32488 initEvents : function()
32490 this.managerEl = this.el.select('.roo-document-manager', true).first();
32491 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32493 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32494 this.selectorEl.hide();
32497 this.selectorEl.attr('multiple', 'multiple');
32500 this.selectorEl.on('change', this.onFileSelected, this);
32502 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32503 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32505 this.uploader.on('click', this.onUploaderClick, this);
32507 this.renderProgressDialog();
32511 window.addEventListener("resize", function() { _this.refresh(); } );
32513 this.fireEvent('initial', this);
32516 renderProgressDialog : function()
32520 this.progressDialog = new Roo.bootstrap.Modal({
32521 cls : 'roo-document-manager-progress-dialog',
32522 allow_close : false,
32533 btnclick : function() {
32534 _this.uploadCancel();
32540 this.progressDialog.render(Roo.get(document.body));
32542 this.progress = new Roo.bootstrap.Progress({
32543 cls : 'roo-document-manager-progress',
32548 this.progress.render(this.progressDialog.getChildContainer());
32550 this.progressBar = new Roo.bootstrap.ProgressBar({
32551 cls : 'roo-document-manager-progress-bar',
32554 aria_valuemax : 12,
32558 this.progressBar.render(this.progress.getChildContainer());
32561 onUploaderClick : function(e)
32563 e.preventDefault();
32565 if(this.fireEvent('beforeselectfile', this) != false){
32566 this.selectorEl.dom.click();
32571 onFileSelected : function(e)
32573 e.preventDefault();
32575 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32579 Roo.each(this.selectorEl.dom.files, function(file){
32580 if(this.fireEvent('inspect', this, file) != false){
32581 this.files.push(file);
32591 this.selectorEl.dom.value = '';
32593 if(!this.files || !this.files.length){
32597 if(this.boxes > 0 && this.files.length > this.boxes){
32598 this.files = this.files.slice(0, this.boxes);
32601 this.uploader.show();
32603 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32604 this.uploader.hide();
32613 Roo.each(this.files, function(file){
32615 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32616 var f = this.renderPreview(file);
32621 if(file.type.indexOf('image') != -1){
32622 this.delegates.push(
32624 _this.process(file);
32625 }).createDelegate(this)
32633 _this.process(file);
32634 }).createDelegate(this)
32639 this.files = files;
32641 this.delegates = this.delegates.concat(docs);
32643 if(!this.delegates.length){
32648 this.progressBar.aria_valuemax = this.delegates.length;
32655 arrange : function()
32657 if(!this.delegates.length){
32658 this.progressDialog.hide();
32663 var delegate = this.delegates.shift();
32665 this.progressDialog.show();
32667 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32669 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32674 refresh : function()
32676 this.uploader.show();
32678 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32679 this.uploader.hide();
32682 Roo.isTouch ? this.closable(false) : this.closable(true);
32684 this.fireEvent('refresh', this);
32687 onRemove : function(e, el, o)
32689 e.preventDefault();
32691 this.fireEvent('remove', this, o);
32695 remove : function(o)
32699 Roo.each(this.files, function(file){
32700 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32709 this.files = files;
32716 Roo.each(this.files, function(file){
32721 file.target.remove();
32730 onClick : function(e, el, o)
32732 e.preventDefault();
32734 this.fireEvent('click', this, o);
32738 closable : function(closable)
32740 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32742 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32754 xhrOnLoad : function(xhr)
32756 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32760 if (xhr.readyState !== 4) {
32762 this.fireEvent('exception', this, xhr);
32766 var response = Roo.decode(xhr.responseText);
32768 if(!response.success){
32770 this.fireEvent('exception', this, xhr);
32774 var file = this.renderPreview(response.data);
32776 this.files.push(file);
32780 this.fireEvent('afterupload', this, xhr);
32784 xhrOnError : function(xhr)
32786 Roo.log('xhr on error');
32788 var response = Roo.decode(xhr.responseText);
32795 process : function(file)
32797 if(this.fireEvent('process', this, file) !== false){
32798 if(this.editable && file.type.indexOf('image') != -1){
32799 this.fireEvent('edit', this, file);
32803 this.uploadStart(file, false);
32810 uploadStart : function(file, crop)
32812 this.xhr = new XMLHttpRequest();
32814 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32819 file.xhr = this.xhr;
32821 this.managerEl.createChild({
32823 cls : 'roo-document-manager-loading',
32827 tooltip : file.name,
32828 cls : 'roo-document-manager-thumb',
32829 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32835 this.xhr.open(this.method, this.url, true);
32838 "Accept": "application/json",
32839 "Cache-Control": "no-cache",
32840 "X-Requested-With": "XMLHttpRequest"
32843 for (var headerName in headers) {
32844 var headerValue = headers[headerName];
32846 this.xhr.setRequestHeader(headerName, headerValue);
32852 this.xhr.onload = function()
32854 _this.xhrOnLoad(_this.xhr);
32857 this.xhr.onerror = function()
32859 _this.xhrOnError(_this.xhr);
32862 var formData = new FormData();
32864 formData.append('returnHTML', 'NO');
32867 formData.append('crop', crop);
32870 formData.append(this.paramName, file, file.name);
32877 if(this.fireEvent('prepare', this, formData, options) != false){
32879 if(options.manually){
32883 this.xhr.send(formData);
32887 this.uploadCancel();
32890 uploadCancel : function()
32896 this.delegates = [];
32898 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32905 renderPreview : function(file)
32907 if(typeof(file.target) != 'undefined' && file.target){
32911 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32913 var previewEl = this.managerEl.createChild({
32915 cls : 'roo-document-manager-preview',
32919 tooltip : file[this.toolTipName],
32920 cls : 'roo-document-manager-thumb',
32921 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32926 html : '<i class="fa fa-times-circle"></i>'
32931 var close = previewEl.select('button.close', true).first();
32933 close.on('click', this.onRemove, this, file);
32935 file.target = previewEl;
32937 var image = previewEl.select('img', true).first();
32941 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32943 image.on('click', this.onClick, this, file);
32945 this.fireEvent('previewrendered', this, file);
32951 onPreviewLoad : function(file, image)
32953 if(typeof(file.target) == 'undefined' || !file.target){
32957 var width = image.dom.naturalWidth || image.dom.width;
32958 var height = image.dom.naturalHeight || image.dom.height;
32960 if(!this.previewResize) {
32964 if(width > height){
32965 file.target.addClass('wide');
32969 file.target.addClass('tall');
32974 uploadFromSource : function(file, crop)
32976 this.xhr = new XMLHttpRequest();
32978 this.managerEl.createChild({
32980 cls : 'roo-document-manager-loading',
32984 tooltip : file.name,
32985 cls : 'roo-document-manager-thumb',
32986 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32992 this.xhr.open(this.method, this.url, true);
32995 "Accept": "application/json",
32996 "Cache-Control": "no-cache",
32997 "X-Requested-With": "XMLHttpRequest"
33000 for (var headerName in headers) {
33001 var headerValue = headers[headerName];
33003 this.xhr.setRequestHeader(headerName, headerValue);
33009 this.xhr.onload = function()
33011 _this.xhrOnLoad(_this.xhr);
33014 this.xhr.onerror = function()
33016 _this.xhrOnError(_this.xhr);
33019 var formData = new FormData();
33021 formData.append('returnHTML', 'NO');
33023 formData.append('crop', crop);
33025 if(typeof(file.filename) != 'undefined'){
33026 formData.append('filename', file.filename);
33029 if(typeof(file.mimetype) != 'undefined'){
33030 formData.append('mimetype', file.mimetype);
33035 if(this.fireEvent('prepare', this, formData) != false){
33036 this.xhr.send(formData);
33046 * @class Roo.bootstrap.DocumentViewer
33047 * @extends Roo.bootstrap.Component
33048 * Bootstrap DocumentViewer class
33049 * @cfg {Boolean} showDownload (true|false) show download button (default true)
33050 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33053 * Create a new DocumentViewer
33054 * @param {Object} config The config object
33057 Roo.bootstrap.DocumentViewer = function(config){
33058 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33063 * Fire after initEvent
33064 * @param {Roo.bootstrap.DocumentViewer} this
33070 * @param {Roo.bootstrap.DocumentViewer} this
33075 * Fire after download button
33076 * @param {Roo.bootstrap.DocumentViewer} this
33081 * Fire after trash button
33082 * @param {Roo.bootstrap.DocumentViewer} this
33089 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
33091 showDownload : true,
33095 getAutoCreate : function()
33099 cls : 'roo-document-viewer',
33103 cls : 'roo-document-viewer-body',
33107 cls : 'roo-document-viewer-thumb',
33111 cls : 'roo-document-viewer-image'
33119 cls : 'roo-document-viewer-footer',
33122 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33126 cls : 'btn-group roo-document-viewer-download',
33130 cls : 'btn btn-default',
33131 html : '<i class="fa fa-download"></i>'
33137 cls : 'btn-group roo-document-viewer-trash',
33141 cls : 'btn btn-default',
33142 html : '<i class="fa fa-trash"></i>'
33155 initEvents : function()
33157 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33158 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33160 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33161 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33163 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33164 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33166 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33167 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33169 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33170 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33172 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33173 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33175 this.bodyEl.on('click', this.onClick, this);
33176 this.downloadBtn.on('click', this.onDownload, this);
33177 this.trashBtn.on('click', this.onTrash, this);
33179 this.downloadBtn.hide();
33180 this.trashBtn.hide();
33182 if(this.showDownload){
33183 this.downloadBtn.show();
33186 if(this.showTrash){
33187 this.trashBtn.show();
33190 if(!this.showDownload && !this.showTrash) {
33191 this.footerEl.hide();
33196 initial : function()
33198 this.fireEvent('initial', this);
33202 onClick : function(e)
33204 e.preventDefault();
33206 this.fireEvent('click', this);
33209 onDownload : function(e)
33211 e.preventDefault();
33213 this.fireEvent('download', this);
33216 onTrash : function(e)
33218 e.preventDefault();
33220 this.fireEvent('trash', this);
33232 * @class Roo.bootstrap.NavProgressBar
33233 * @extends Roo.bootstrap.Component
33234 * Bootstrap NavProgressBar class
33237 * Create a new nav progress bar
33238 * @param {Object} config The config object
33241 Roo.bootstrap.NavProgressBar = function(config){
33242 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
33244 this.bullets = this.bullets || [];
33246 // Roo.bootstrap.NavProgressBar.register(this);
33250 * Fires when the active item changes
33251 * @param {Roo.bootstrap.NavProgressBar} this
33252 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
33253 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
33260 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
33265 getAutoCreate : function()
33267 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
33271 cls : 'roo-navigation-bar-group',
33275 cls : 'roo-navigation-top-bar'
33279 cls : 'roo-navigation-bullets-bar',
33283 cls : 'roo-navigation-bar'
33290 cls : 'roo-navigation-bottom-bar'
33300 initEvents: function()
33305 onRender : function(ct, position)
33307 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33309 if(this.bullets.length){
33310 Roo.each(this.bullets, function(b){
33319 addItem : function(cfg)
33321 var item = new Roo.bootstrap.NavProgressItem(cfg);
33323 item.parentId = this.id;
33324 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
33327 var top = new Roo.bootstrap.Element({
33329 cls : 'roo-navigation-bar-text'
33332 var bottom = new Roo.bootstrap.Element({
33334 cls : 'roo-navigation-bar-text'
33337 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
33338 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
33340 var topText = new Roo.bootstrap.Element({
33342 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
33345 var bottomText = new Roo.bootstrap.Element({
33347 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
33350 topText.onRender(top.el, null);
33351 bottomText.onRender(bottom.el, null);
33354 item.bottomEl = bottom;
33357 this.barItems.push(item);
33362 getActive : function()
33364 var active = false;
33366 Roo.each(this.barItems, function(v){
33368 if (!v.isActive()) {
33380 setActiveItem : function(item)
33384 Roo.each(this.barItems, function(v){
33385 if (v.rid == item.rid) {
33389 if (v.isActive()) {
33390 v.setActive(false);
33395 item.setActive(true);
33397 this.fireEvent('changed', this, item, prev);
33400 getBarItem: function(rid)
33404 Roo.each(this.barItems, function(e) {
33405 if (e.rid != rid) {
33416 indexOfItem : function(item)
33420 Roo.each(this.barItems, function(v, i){
33422 if (v.rid != item.rid) {
33433 setActiveNext : function()
33435 var i = this.indexOfItem(this.getActive());
33437 if (i > this.barItems.length) {
33441 this.setActiveItem(this.barItems[i+1]);
33444 setActivePrev : function()
33446 var i = this.indexOfItem(this.getActive());
33452 this.setActiveItem(this.barItems[i-1]);
33455 format : function()
33457 if(!this.barItems.length){
33461 var width = 100 / this.barItems.length;
33463 Roo.each(this.barItems, function(i){
33464 i.el.setStyle('width', width + '%');
33465 i.topEl.el.setStyle('width', width + '%');
33466 i.bottomEl.el.setStyle('width', width + '%');
33475 * Nav Progress Item
33480 * @class Roo.bootstrap.NavProgressItem
33481 * @extends Roo.bootstrap.Component
33482 * Bootstrap NavProgressItem class
33483 * @cfg {String} rid the reference id
33484 * @cfg {Boolean} active (true|false) Is item active default false
33485 * @cfg {Boolean} disabled (true|false) Is item active default false
33486 * @cfg {String} html
33487 * @cfg {String} position (top|bottom) text position default bottom
33488 * @cfg {String} icon show icon instead of number
33491 * Create a new NavProgressItem
33492 * @param {Object} config The config object
33494 Roo.bootstrap.NavProgressItem = function(config){
33495 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33500 * The raw click event for the entire grid.
33501 * @param {Roo.bootstrap.NavProgressItem} this
33502 * @param {Roo.EventObject} e
33509 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
33515 position : 'bottom',
33518 getAutoCreate : function()
33520 var iconCls = 'roo-navigation-bar-item-icon';
33522 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33526 cls: 'roo-navigation-bar-item',
33536 cfg.cls += ' active';
33539 cfg.cls += ' disabled';
33545 disable : function()
33547 this.setDisabled(true);
33550 enable : function()
33552 this.setDisabled(false);
33555 initEvents: function()
33557 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33559 this.iconEl.on('click', this.onClick, this);
33562 onClick : function(e)
33564 e.preventDefault();
33570 if(this.fireEvent('click', this, e) === false){
33574 this.parent().setActiveItem(this);
33577 isActive: function ()
33579 return this.active;
33582 setActive : function(state)
33584 if(this.active == state){
33588 this.active = state;
33591 this.el.addClass('active');
33595 this.el.removeClass('active');
33600 setDisabled : function(state)
33602 if(this.disabled == state){
33606 this.disabled = state;
33609 this.el.addClass('disabled');
33613 this.el.removeClass('disabled');
33616 tooltipEl : function()
33618 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33631 * @class Roo.bootstrap.FieldLabel
33632 * @extends Roo.bootstrap.Component
33633 * Bootstrap FieldLabel class
33634 * @cfg {String} html contents of the element
33635 * @cfg {String} tag tag of the element default label
33636 * @cfg {String} cls class of the element
33637 * @cfg {String} target label target
33638 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33639 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33640 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33641 * @cfg {String} iconTooltip default "This field is required"
33642 * @cfg {String} indicatorpos (left|right) default left
33645 * Create a new FieldLabel
33646 * @param {Object} config The config object
33649 Roo.bootstrap.FieldLabel = function(config){
33650 Roo.bootstrap.Element.superclass.constructor.call(this, config);
33655 * Fires after the field has been marked as invalid.
33656 * @param {Roo.form.FieldLabel} this
33657 * @param {String} msg The validation message
33662 * Fires after the field has been validated with no errors.
33663 * @param {Roo.form.FieldLabel} this
33669 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
33676 invalidClass : 'has-warning',
33677 validClass : 'has-success',
33678 iconTooltip : 'This field is required',
33679 indicatorpos : 'left',
33681 getAutoCreate : function(){
33684 if (!this.allowBlank) {
33690 cls : 'roo-bootstrap-field-label ' + this.cls,
33695 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33696 tooltip : this.iconTooltip
33705 if(this.indicatorpos == 'right'){
33708 cls : 'roo-bootstrap-field-label ' + this.cls,
33717 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33718 tooltip : this.iconTooltip
33727 initEvents: function()
33729 Roo.bootstrap.Element.superclass.initEvents.call(this);
33731 this.indicator = this.indicatorEl();
33733 if(this.indicator){
33734 this.indicator.removeClass('visible');
33735 this.indicator.addClass('invisible');
33738 Roo.bootstrap.FieldLabel.register(this);
33741 indicatorEl : function()
33743 var indicator = this.el.select('i.roo-required-indicator',true).first();
33754 * Mark this field as valid
33756 markValid : function()
33758 if(this.indicator){
33759 this.indicator.removeClass('visible');
33760 this.indicator.addClass('invisible');
33762 if (Roo.bootstrap.version == 3) {
33763 this.el.removeClass(this.invalidClass);
33764 this.el.addClass(this.validClass);
33766 this.el.removeClass('is-invalid');
33767 this.el.addClass('is-valid');
33771 this.fireEvent('valid', this);
33775 * Mark this field as invalid
33776 * @param {String} msg The validation message
33778 markInvalid : function(msg)
33780 if(this.indicator){
33781 this.indicator.removeClass('invisible');
33782 this.indicator.addClass('visible');
33784 if (Roo.bootstrap.version == 3) {
33785 this.el.removeClass(this.validClass);
33786 this.el.addClass(this.invalidClass);
33788 this.el.removeClass('is-valid');
33789 this.el.addClass('is-invalid');
33793 this.fireEvent('invalid', this, msg);
33799 Roo.apply(Roo.bootstrap.FieldLabel, {
33804 * register a FieldLabel Group
33805 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33807 register : function(label)
33809 if(this.groups.hasOwnProperty(label.target)){
33813 this.groups[label.target] = label;
33817 * fetch a FieldLabel Group based on the target
33818 * @param {string} target
33819 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33821 get: function(target) {
33822 if (typeof(this.groups[target]) == 'undefined') {
33826 return this.groups[target] ;
33835 * page DateSplitField.
33841 * @class Roo.bootstrap.DateSplitField
33842 * @extends Roo.bootstrap.Component
33843 * Bootstrap DateSplitField class
33844 * @cfg {string} fieldLabel - the label associated
33845 * @cfg {Number} labelWidth set the width of label (0-12)
33846 * @cfg {String} labelAlign (top|left)
33847 * @cfg {Boolean} dayAllowBlank (true|false) default false
33848 * @cfg {Boolean} monthAllowBlank (true|false) default false
33849 * @cfg {Boolean} yearAllowBlank (true|false) default false
33850 * @cfg {string} dayPlaceholder
33851 * @cfg {string} monthPlaceholder
33852 * @cfg {string} yearPlaceholder
33853 * @cfg {string} dayFormat default 'd'
33854 * @cfg {string} monthFormat default 'm'
33855 * @cfg {string} yearFormat default 'Y'
33856 * @cfg {Number} labellg set the width of label (1-12)
33857 * @cfg {Number} labelmd set the width of label (1-12)
33858 * @cfg {Number} labelsm set the width of label (1-12)
33859 * @cfg {Number} labelxs set the width of label (1-12)
33863 * Create a new DateSplitField
33864 * @param {Object} config The config object
33867 Roo.bootstrap.DateSplitField = function(config){
33868 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33874 * getting the data of years
33875 * @param {Roo.bootstrap.DateSplitField} this
33876 * @param {Object} years
33881 * getting the data of days
33882 * @param {Roo.bootstrap.DateSplitField} this
33883 * @param {Object} days
33888 * Fires after the field has been marked as invalid.
33889 * @param {Roo.form.Field} this
33890 * @param {String} msg The validation message
33895 * Fires after the field has been validated with no errors.
33896 * @param {Roo.form.Field} this
33902 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33905 labelAlign : 'top',
33907 dayAllowBlank : false,
33908 monthAllowBlank : false,
33909 yearAllowBlank : false,
33910 dayPlaceholder : '',
33911 monthPlaceholder : '',
33912 yearPlaceholder : '',
33916 isFormField : true,
33922 getAutoCreate : function()
33926 cls : 'row roo-date-split-field-group',
33931 cls : 'form-hidden-field roo-date-split-field-group-value',
33937 var labelCls = 'col-md-12';
33938 var contentCls = 'col-md-4';
33940 if(this.fieldLabel){
33944 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33948 html : this.fieldLabel
33953 if(this.labelAlign == 'left'){
33955 if(this.labelWidth > 12){
33956 label.style = "width: " + this.labelWidth + 'px';
33959 if(this.labelWidth < 13 && this.labelmd == 0){
33960 this.labelmd = this.labelWidth;
33963 if(this.labellg > 0){
33964 labelCls = ' col-lg-' + this.labellg;
33965 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33968 if(this.labelmd > 0){
33969 labelCls = ' col-md-' + this.labelmd;
33970 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33973 if(this.labelsm > 0){
33974 labelCls = ' col-sm-' + this.labelsm;
33975 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33978 if(this.labelxs > 0){
33979 labelCls = ' col-xs-' + this.labelxs;
33980 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33984 label.cls += ' ' + labelCls;
33986 cfg.cn.push(label);
33989 Roo.each(['day', 'month', 'year'], function(t){
33992 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33999 inputEl: function ()
34001 return this.el.select('.roo-date-split-field-group-value', true).first();
34004 onRender : function(ct, position)
34008 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
34010 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
34012 this.dayField = new Roo.bootstrap.ComboBox({
34013 allowBlank : this.dayAllowBlank,
34014 alwaysQuery : true,
34015 displayField : 'value',
34018 forceSelection : true,
34020 placeholder : this.dayPlaceholder,
34021 selectOnFocus : true,
34022 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34023 triggerAction : 'all',
34025 valueField : 'value',
34026 store : new Roo.data.SimpleStore({
34027 data : (function() {
34029 _this.fireEvent('days', _this, days);
34032 fields : [ 'value' ]
34035 select : function (_self, record, index)
34037 _this.setValue(_this.getValue());
34042 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
34044 this.monthField = new Roo.bootstrap.MonthField({
34045 after : '<i class=\"fa fa-calendar\"></i>',
34046 allowBlank : this.monthAllowBlank,
34047 placeholder : this.monthPlaceholder,
34050 render : function (_self)
34052 this.el.select('span.input-group-addon', true).first().on('click', function(e){
34053 e.preventDefault();
34057 select : function (_self, oldvalue, newvalue)
34059 _this.setValue(_this.getValue());
34064 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
34066 this.yearField = new Roo.bootstrap.ComboBox({
34067 allowBlank : this.yearAllowBlank,
34068 alwaysQuery : true,
34069 displayField : 'value',
34072 forceSelection : true,
34074 placeholder : this.yearPlaceholder,
34075 selectOnFocus : true,
34076 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34077 triggerAction : 'all',
34079 valueField : 'value',
34080 store : new Roo.data.SimpleStore({
34081 data : (function() {
34083 _this.fireEvent('years', _this, years);
34086 fields : [ 'value' ]
34089 select : function (_self, record, index)
34091 _this.setValue(_this.getValue());
34096 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
34099 setValue : function(v, format)
34101 this.inputEl.dom.value = v;
34103 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
34105 var d = Date.parseDate(v, f);
34112 this.setDay(d.format(this.dayFormat));
34113 this.setMonth(d.format(this.monthFormat));
34114 this.setYear(d.format(this.yearFormat));
34121 setDay : function(v)
34123 this.dayField.setValue(v);
34124 this.inputEl.dom.value = this.getValue();
34129 setMonth : function(v)
34131 this.monthField.setValue(v, true);
34132 this.inputEl.dom.value = this.getValue();
34137 setYear : function(v)
34139 this.yearField.setValue(v);
34140 this.inputEl.dom.value = this.getValue();
34145 getDay : function()
34147 return this.dayField.getValue();
34150 getMonth : function()
34152 return this.monthField.getValue();
34155 getYear : function()
34157 return this.yearField.getValue();
34160 getValue : function()
34162 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
34164 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
34174 this.inputEl.dom.value = '';
34179 validate : function()
34181 var d = this.dayField.validate();
34182 var m = this.monthField.validate();
34183 var y = this.yearField.validate();
34188 (!this.dayAllowBlank && !d) ||
34189 (!this.monthAllowBlank && !m) ||
34190 (!this.yearAllowBlank && !y)
34195 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
34204 this.markInvalid();
34209 markValid : function()
34212 var label = this.el.select('label', true).first();
34213 var icon = this.el.select('i.fa-star', true).first();
34219 this.fireEvent('valid', this);
34223 * Mark this field as invalid
34224 * @param {String} msg The validation message
34226 markInvalid : function(msg)
34229 var label = this.el.select('label', true).first();
34230 var icon = this.el.select('i.fa-star', true).first();
34232 if(label && !icon){
34233 this.el.select('.roo-date-split-field-label', true).createChild({
34235 cls : 'text-danger fa fa-lg fa-star',
34236 tooltip : 'This field is required',
34237 style : 'margin-right:5px;'
34241 this.fireEvent('invalid', this, msg);
34244 clearInvalid : function()
34246 var label = this.el.select('label', true).first();
34247 var icon = this.el.select('i.fa-star', true).first();
34253 this.fireEvent('valid', this);
34256 getName: function()
34266 * http://masonry.desandro.com
34268 * The idea is to render all the bricks based on vertical width...
34270 * The original code extends 'outlayer' - we might need to use that....
34276 * @class Roo.bootstrap.LayoutMasonry
34277 * @extends Roo.bootstrap.Component
34278 * Bootstrap Layout Masonry class
34281 * Create a new Element
34282 * @param {Object} config The config object
34285 Roo.bootstrap.LayoutMasonry = function(config){
34287 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34291 Roo.bootstrap.LayoutMasonry.register(this);
34297 * Fire after layout the items
34298 * @param {Roo.bootstrap.LayoutMasonry} this
34299 * @param {Roo.EventObject} e
34306 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
34309 * @cfg {Boolean} isLayoutInstant = no animation?
34311 isLayoutInstant : false, // needed?
34314 * @cfg {Number} boxWidth width of the columns
34319 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
34324 * @cfg {Number} padWidth padding below box..
34329 * @cfg {Number} gutter gutter width..
34334 * @cfg {Number} maxCols maximum number of columns
34340 * @cfg {Boolean} isAutoInitial defalut true
34342 isAutoInitial : true,
34347 * @cfg {Boolean} isHorizontal defalut false
34349 isHorizontal : false,
34351 currentSize : null,
34357 bricks: null, //CompositeElement
34361 _isLayoutInited : false,
34363 // isAlternative : false, // only use for vertical layout...
34366 * @cfg {Number} alternativePadWidth padding below box..
34368 alternativePadWidth : 50,
34370 selectedBrick : [],
34372 getAutoCreate : function(){
34374 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34378 cls: 'blog-masonary-wrapper ' + this.cls,
34380 cls : 'mas-boxes masonary'
34387 getChildContainer: function( )
34389 if (this.boxesEl) {
34390 return this.boxesEl;
34393 this.boxesEl = this.el.select('.mas-boxes').first();
34395 return this.boxesEl;
34399 initEvents : function()
34403 if(this.isAutoInitial){
34404 Roo.log('hook children rendered');
34405 this.on('childrenrendered', function() {
34406 Roo.log('children rendered');
34412 initial : function()
34414 this.selectedBrick = [];
34416 this.currentSize = this.el.getBox(true);
34418 Roo.EventManager.onWindowResize(this.resize, this);
34420 if(!this.isAutoInitial){
34428 //this.layout.defer(500,this);
34432 resize : function()
34434 var cs = this.el.getBox(true);
34437 this.currentSize.width == cs.width &&
34438 this.currentSize.x == cs.x &&
34439 this.currentSize.height == cs.height &&
34440 this.currentSize.y == cs.y
34442 Roo.log("no change in with or X or Y");
34446 this.currentSize = cs;
34452 layout : function()
34454 this._resetLayout();
34456 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34458 this.layoutItems( isInstant );
34460 this._isLayoutInited = true;
34462 this.fireEvent('layout', this);
34466 _resetLayout : function()
34468 if(this.isHorizontal){
34469 this.horizontalMeasureColumns();
34473 this.verticalMeasureColumns();
34477 verticalMeasureColumns : function()
34479 this.getContainerWidth();
34481 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34482 // this.colWidth = Math.floor(this.containerWidth * 0.8);
34486 var boxWidth = this.boxWidth + this.padWidth;
34488 if(this.containerWidth < this.boxWidth){
34489 boxWidth = this.containerWidth
34492 var containerWidth = this.containerWidth;
34494 var cols = Math.floor(containerWidth / boxWidth);
34496 this.cols = Math.max( cols, 1 );
34498 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34500 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34502 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34504 this.colWidth = boxWidth + avail - this.padWidth;
34506 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34507 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
34510 horizontalMeasureColumns : function()
34512 this.getContainerWidth();
34514 var boxWidth = this.boxWidth;
34516 if(this.containerWidth < boxWidth){
34517 boxWidth = this.containerWidth;
34520 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34522 this.el.setHeight(boxWidth);
34526 getContainerWidth : function()
34528 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34531 layoutItems : function( isInstant )
34533 Roo.log(this.bricks);
34535 var items = Roo.apply([], this.bricks);
34537 if(this.isHorizontal){
34538 this._horizontalLayoutItems( items , isInstant );
34542 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34543 // this._verticalAlternativeLayoutItems( items , isInstant );
34547 this._verticalLayoutItems( items , isInstant );
34551 _verticalLayoutItems : function ( items , isInstant)
34553 if ( !items || !items.length ) {
34558 ['xs', 'xs', 'xs', 'tall'],
34559 ['xs', 'xs', 'tall'],
34560 ['xs', 'xs', 'sm'],
34561 ['xs', 'xs', 'xs'],
34567 ['sm', 'xs', 'xs'],
34571 ['tall', 'xs', 'xs', 'xs'],
34572 ['tall', 'xs', 'xs'],
34584 Roo.each(items, function(item, k){
34586 switch (item.size) {
34587 // these layouts take up a full box,
34598 boxes.push([item]);
34621 var filterPattern = function(box, length)
34629 var pattern = box.slice(0, length);
34633 Roo.each(pattern, function(i){
34634 format.push(i.size);
34637 Roo.each(standard, function(s){
34639 if(String(s) != String(format)){
34648 if(!match && length == 1){
34653 filterPattern(box, length - 1);
34657 queue.push(pattern);
34659 box = box.slice(length, box.length);
34661 filterPattern(box, 4);
34667 Roo.each(boxes, function(box, k){
34673 if(box.length == 1){
34678 filterPattern(box, 4);
34682 this._processVerticalLayoutQueue( queue, isInstant );
34686 // _verticalAlternativeLayoutItems : function( items , isInstant )
34688 // if ( !items || !items.length ) {
34692 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
34696 _horizontalLayoutItems : function ( items , isInstant)
34698 if ( !items || !items.length || items.length < 3) {
34704 var eItems = items.slice(0, 3);
34706 items = items.slice(3, items.length);
34709 ['xs', 'xs', 'xs', 'wide'],
34710 ['xs', 'xs', 'wide'],
34711 ['xs', 'xs', 'sm'],
34712 ['xs', 'xs', 'xs'],
34718 ['sm', 'xs', 'xs'],
34722 ['wide', 'xs', 'xs', 'xs'],
34723 ['wide', 'xs', 'xs'],
34736 Roo.each(items, function(item, k){
34738 switch (item.size) {
34749 boxes.push([item]);
34773 var filterPattern = function(box, length)
34781 var pattern = box.slice(0, length);
34785 Roo.each(pattern, function(i){
34786 format.push(i.size);
34789 Roo.each(standard, function(s){
34791 if(String(s) != String(format)){
34800 if(!match && length == 1){
34805 filterPattern(box, length - 1);
34809 queue.push(pattern);
34811 box = box.slice(length, box.length);
34813 filterPattern(box, 4);
34819 Roo.each(boxes, function(box, k){
34825 if(box.length == 1){
34830 filterPattern(box, 4);
34837 var pos = this.el.getBox(true);
34841 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34843 var hit_end = false;
34845 Roo.each(queue, function(box){
34849 Roo.each(box, function(b){
34851 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34861 Roo.each(box, function(b){
34863 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34866 mx = Math.max(mx, b.x);
34870 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34874 Roo.each(box, function(b){
34876 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34890 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34893 /** Sets position of item in DOM
34894 * @param {Element} item
34895 * @param {Number} x - horizontal position
34896 * @param {Number} y - vertical position
34897 * @param {Boolean} isInstant - disables transitions
34899 _processVerticalLayoutQueue : function( queue, isInstant )
34901 var pos = this.el.getBox(true);
34906 for (var i = 0; i < this.cols; i++){
34910 Roo.each(queue, function(box, k){
34912 var col = k % this.cols;
34914 Roo.each(box, function(b,kk){
34916 b.el.position('absolute');
34918 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34919 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34921 if(b.size == 'md-left' || b.size == 'md-right'){
34922 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34923 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34926 b.el.setWidth(width);
34927 b.el.setHeight(height);
34929 b.el.select('iframe',true).setSize(width,height);
34933 for (var i = 0; i < this.cols; i++){
34935 if(maxY[i] < maxY[col]){
34940 col = Math.min(col, i);
34944 x = pos.x + col * (this.colWidth + this.padWidth);
34948 var positions = [];
34950 switch (box.length){
34952 positions = this.getVerticalOneBoxColPositions(x, y, box);
34955 positions = this.getVerticalTwoBoxColPositions(x, y, box);
34958 positions = this.getVerticalThreeBoxColPositions(x, y, box);
34961 positions = this.getVerticalFourBoxColPositions(x, y, box);
34967 Roo.each(box, function(b,kk){
34969 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34971 var sz = b.el.getSize();
34973 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34981 for (var i = 0; i < this.cols; i++){
34982 mY = Math.max(mY, maxY[i]);
34985 this.el.setHeight(mY - pos.y);
34989 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34991 // var pos = this.el.getBox(true);
34994 // var maxX = pos.right;
34996 // var maxHeight = 0;
34998 // Roo.each(items, function(item, k){
35002 // item.el.position('absolute');
35004 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
35006 // item.el.setWidth(width);
35008 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
35010 // item.el.setHeight(height);
35013 // item.el.setXY([x, y], isInstant ? false : true);
35015 // item.el.setXY([maxX - width, y], isInstant ? false : true);
35018 // y = y + height + this.alternativePadWidth;
35020 // maxHeight = maxHeight + height + this.alternativePadWidth;
35024 // this.el.setHeight(maxHeight);
35028 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
35030 var pos = this.el.getBox(true);
35035 var maxX = pos.right;
35037 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
35039 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
35041 Roo.each(queue, function(box, k){
35043 Roo.each(box, function(b, kk){
35045 b.el.position('absolute');
35047 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35048 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35050 if(b.size == 'md-left' || b.size == 'md-right'){
35051 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35052 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35055 b.el.setWidth(width);
35056 b.el.setHeight(height);
35064 var positions = [];
35066 switch (box.length){
35068 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
35071 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
35074 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
35077 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
35083 Roo.each(box, function(b,kk){
35085 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35087 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
35095 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
35097 Roo.each(eItems, function(b,k){
35099 b.size = (k == 0) ? 'sm' : 'xs';
35100 b.x = (k == 0) ? 2 : 1;
35101 b.y = (k == 0) ? 2 : 1;
35103 b.el.position('absolute');
35105 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35107 b.el.setWidth(width);
35109 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35111 b.el.setHeight(height);
35115 var positions = [];
35118 x : maxX - this.unitWidth * 2 - this.gutter,
35123 x : maxX - this.unitWidth,
35124 y : minY + (this.unitWidth + this.gutter) * 2
35128 x : maxX - this.unitWidth * 3 - this.gutter * 2,
35132 Roo.each(eItems, function(b,k){
35134 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
35140 getVerticalOneBoxColPositions : function(x, y, box)
35144 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
35146 if(box[0].size == 'md-left'){
35150 if(box[0].size == 'md-right'){
35155 x : x + (this.unitWidth + this.gutter) * rand,
35162 getVerticalTwoBoxColPositions : function(x, y, box)
35166 if(box[0].size == 'xs'){
35170 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
35174 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
35188 x : x + (this.unitWidth + this.gutter) * 2,
35189 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
35196 getVerticalThreeBoxColPositions : function(x, y, box)
35200 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35208 x : x + (this.unitWidth + this.gutter) * 1,
35213 x : x + (this.unitWidth + this.gutter) * 2,
35221 if(box[0].size == 'xs' && box[1].size == 'xs'){
35230 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
35234 x : x + (this.unitWidth + this.gutter) * 1,
35248 x : x + (this.unitWidth + this.gutter) * 2,
35253 x : x + (this.unitWidth + this.gutter) * 2,
35254 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
35261 getVerticalFourBoxColPositions : function(x, y, box)
35265 if(box[0].size == 'xs'){
35274 y : y + (this.unitHeight + this.gutter) * 1
35279 y : y + (this.unitHeight + this.gutter) * 2
35283 x : x + (this.unitWidth + this.gutter) * 1,
35297 x : x + (this.unitWidth + this.gutter) * 2,
35302 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35303 y : y + (this.unitHeight + this.gutter) * 1
35307 x : x + (this.unitWidth + this.gutter) * 2,
35308 y : y + (this.unitWidth + this.gutter) * 2
35315 getHorizontalOneBoxColPositions : function(maxX, minY, box)
35319 if(box[0].size == 'md-left'){
35321 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35328 if(box[0].size == 'md-right'){
35330 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35331 y : minY + (this.unitWidth + this.gutter) * 1
35337 var rand = Math.floor(Math.random() * (4 - box[0].y));
35340 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35341 y : minY + (this.unitWidth + this.gutter) * rand
35348 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35352 if(box[0].size == 'xs'){
35355 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35360 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35361 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35369 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35374 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35375 y : minY + (this.unitWidth + this.gutter) * 2
35382 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35386 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35389 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35394 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35395 y : minY + (this.unitWidth + this.gutter) * 1
35399 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35400 y : minY + (this.unitWidth + this.gutter) * 2
35407 if(box[0].size == 'xs' && box[1].size == 'xs'){
35410 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35415 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35420 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35421 y : minY + (this.unitWidth + this.gutter) * 1
35429 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35434 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35435 y : minY + (this.unitWidth + this.gutter) * 2
35439 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35440 y : minY + (this.unitWidth + this.gutter) * 2
35447 getHorizontalFourBoxColPositions : function(maxX, minY, box)
35451 if(box[0].size == 'xs'){
35454 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35459 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35464 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),
35469 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35470 y : minY + (this.unitWidth + this.gutter) * 1
35478 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35483 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35484 y : minY + (this.unitWidth + this.gutter) * 2
35488 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35489 y : minY + (this.unitWidth + this.gutter) * 2
35493 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),
35494 y : minY + (this.unitWidth + this.gutter) * 2
35502 * remove a Masonry Brick
35503 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35505 removeBrick : function(brick_id)
35511 for (var i = 0; i<this.bricks.length; i++) {
35512 if (this.bricks[i].id == brick_id) {
35513 this.bricks.splice(i,1);
35514 this.el.dom.removeChild(Roo.get(brick_id).dom);
35521 * adds a Masonry Brick
35522 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35524 addBrick : function(cfg)
35526 var cn = new Roo.bootstrap.MasonryBrick(cfg);
35527 //this.register(cn);
35528 cn.parentId = this.id;
35529 cn.render(this.el);
35534 * register a Masonry Brick
35535 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35538 register : function(brick)
35540 this.bricks.push(brick);
35541 brick.masonryId = this.id;
35545 * clear all the Masonry Brick
35547 clearAll : function()
35550 //this.getChildContainer().dom.innerHTML = "";
35551 this.el.dom.innerHTML = '';
35554 getSelected : function()
35556 if (!this.selectedBrick) {
35560 return this.selectedBrick;
35564 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35568 * register a Masonry Layout
35569 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35572 register : function(layout)
35574 this.groups[layout.id] = layout;
35577 * fetch a Masonry Layout based on the masonry layout ID
35578 * @param {string} the masonry layout to add
35579 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35582 get: function(layout_id) {
35583 if (typeof(this.groups[layout_id]) == 'undefined') {
35586 return this.groups[layout_id] ;
35598 * http://masonry.desandro.com
35600 * The idea is to render all the bricks based on vertical width...
35602 * The original code extends 'outlayer' - we might need to use that....
35608 * @class Roo.bootstrap.LayoutMasonryAuto
35609 * @extends Roo.bootstrap.Component
35610 * Bootstrap Layout Masonry class
35613 * Create a new Element
35614 * @param {Object} config The config object
35617 Roo.bootstrap.LayoutMasonryAuto = function(config){
35618 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35621 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
35624 * @cfg {Boolean} isFitWidth - resize the width..
35626 isFitWidth : false, // options..
35628 * @cfg {Boolean} isOriginLeft = left align?
35630 isOriginLeft : true,
35632 * @cfg {Boolean} isOriginTop = top align?
35634 isOriginTop : false,
35636 * @cfg {Boolean} isLayoutInstant = no animation?
35638 isLayoutInstant : false, // needed?
35640 * @cfg {Boolean} isResizingContainer = not sure if this is used..
35642 isResizingContainer : true,
35644 * @cfg {Number} columnWidth width of the columns
35650 * @cfg {Number} maxCols maximum number of columns
35655 * @cfg {Number} padHeight padding below box..
35661 * @cfg {Boolean} isAutoInitial defalut true
35664 isAutoInitial : true,
35670 initialColumnWidth : 0,
35671 currentSize : null,
35673 colYs : null, // array.
35680 bricks: null, //CompositeElement
35681 cols : 0, // array?
35682 // element : null, // wrapped now this.el
35683 _isLayoutInited : null,
35686 getAutoCreate : function(){
35690 cls: 'blog-masonary-wrapper ' + this.cls,
35692 cls : 'mas-boxes masonary'
35699 getChildContainer: function( )
35701 if (this.boxesEl) {
35702 return this.boxesEl;
35705 this.boxesEl = this.el.select('.mas-boxes').first();
35707 return this.boxesEl;
35711 initEvents : function()
35715 if(this.isAutoInitial){
35716 Roo.log('hook children rendered');
35717 this.on('childrenrendered', function() {
35718 Roo.log('children rendered');
35725 initial : function()
35727 this.reloadItems();
35729 this.currentSize = this.el.getBox(true);
35731 /// was window resize... - let's see if this works..
35732 Roo.EventManager.onWindowResize(this.resize, this);
35734 if(!this.isAutoInitial){
35739 this.layout.defer(500,this);
35742 reloadItems: function()
35744 this.bricks = this.el.select('.masonry-brick', true);
35746 this.bricks.each(function(b) {
35747 //Roo.log(b.getSize());
35748 if (!b.attr('originalwidth')) {
35749 b.attr('originalwidth', b.getSize().width);
35754 Roo.log(this.bricks.elements.length);
35757 resize : function()
35760 var cs = this.el.getBox(true);
35762 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35763 Roo.log("no change in with or X");
35766 this.currentSize = cs;
35770 layout : function()
35773 this._resetLayout();
35774 //this._manageStamps();
35776 // don't animate first layout
35777 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35778 this.layoutItems( isInstant );
35780 // flag for initalized
35781 this._isLayoutInited = true;
35784 layoutItems : function( isInstant )
35786 //var items = this._getItemsForLayout( this.items );
35787 // original code supports filtering layout items.. we just ignore it..
35789 this._layoutItems( this.bricks , isInstant );
35791 this._postLayout();
35793 _layoutItems : function ( items , isInstant)
35795 //this.fireEvent( 'layout', this, items );
35798 if ( !items || !items.elements.length ) {
35799 // no items, emit event with empty array
35804 items.each(function(item) {
35805 Roo.log("layout item");
35807 // get x/y object from method
35808 var position = this._getItemLayoutPosition( item );
35810 position.item = item;
35811 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35812 queue.push( position );
35815 this._processLayoutQueue( queue );
35817 /** Sets position of item in DOM
35818 * @param {Element} item
35819 * @param {Number} x - horizontal position
35820 * @param {Number} y - vertical position
35821 * @param {Boolean} isInstant - disables transitions
35823 _processLayoutQueue : function( queue )
35825 for ( var i=0, len = queue.length; i < len; i++ ) {
35826 var obj = queue[i];
35827 obj.item.position('absolute');
35828 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35834 * Any logic you want to do after each layout,
35835 * i.e. size the container
35837 _postLayout : function()
35839 this.resizeContainer();
35842 resizeContainer : function()
35844 if ( !this.isResizingContainer ) {
35847 var size = this._getContainerSize();
35849 this.el.setSize(size.width,size.height);
35850 this.boxesEl.setSize(size.width,size.height);
35856 _resetLayout : function()
35858 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35859 this.colWidth = this.el.getWidth();
35860 //this.gutter = this.el.getWidth();
35862 this.measureColumns();
35868 this.colYs.push( 0 );
35874 measureColumns : function()
35876 this.getContainerWidth();
35877 // if columnWidth is 0, default to outerWidth of first item
35878 if ( !this.columnWidth ) {
35879 var firstItem = this.bricks.first();
35880 Roo.log(firstItem);
35881 this.columnWidth = this.containerWidth;
35882 if (firstItem && firstItem.attr('originalwidth') ) {
35883 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35885 // columnWidth fall back to item of first element
35886 Roo.log("set column width?");
35887 this.initialColumnWidth = this.columnWidth ;
35889 // if first elem has no width, default to size of container
35894 if (this.initialColumnWidth) {
35895 this.columnWidth = this.initialColumnWidth;
35900 // column width is fixed at the top - however if container width get's smaller we should
35903 // this bit calcs how man columns..
35905 var columnWidth = this.columnWidth += this.gutter;
35907 // calculate columns
35908 var containerWidth = this.containerWidth + this.gutter;
35910 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35911 // fix rounding errors, typically with gutters
35912 var excess = columnWidth - containerWidth % columnWidth;
35915 // if overshoot is less than a pixel, round up, otherwise floor it
35916 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35917 cols = Math[ mathMethod ]( cols );
35918 this.cols = Math.max( cols, 1 );
35919 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35921 // padding positioning..
35922 var totalColWidth = this.cols * this.columnWidth;
35923 var padavail = this.containerWidth - totalColWidth;
35924 // so for 2 columns - we need 3 'pads'
35926 var padNeeded = (1+this.cols) * this.padWidth;
35928 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35930 this.columnWidth += padExtra
35931 //this.padWidth = Math.floor(padavail / ( this.cols));
35933 // adjust colum width so that padding is fixed??
35935 // we have 3 columns ... total = width * 3
35936 // we have X left over... that should be used by
35938 //if (this.expandC) {
35946 getContainerWidth : function()
35948 /* // container is parent if fit width
35949 var container = this.isFitWidth ? this.element.parentNode : this.element;
35950 // check that this.size and size are there
35951 // IE8 triggers resize on body size change, so they might not be
35953 var size = getSize( container ); //FIXME
35954 this.containerWidth = size && size.innerWidth; //FIXME
35957 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
35961 _getItemLayoutPosition : function( item ) // what is item?
35963 // we resize the item to our columnWidth..
35965 item.setWidth(this.columnWidth);
35966 item.autoBoxAdjust = false;
35968 var sz = item.getSize();
35970 // how many columns does this brick span
35971 var remainder = this.containerWidth % this.columnWidth;
35973 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35974 // round if off by 1 pixel, otherwise use ceil
35975 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
35976 colSpan = Math.min( colSpan, this.cols );
35978 // normally this should be '1' as we dont' currently allow multi width columns..
35980 var colGroup = this._getColGroup( colSpan );
35981 // get the minimum Y value from the columns
35982 var minimumY = Math.min.apply( Math, colGroup );
35983 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35985 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35987 // position the brick
35989 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35990 y: this.currentSize.y + minimumY + this.padHeight
35994 // apply setHeight to necessary columns
35995 var setHeight = minimumY + sz.height + this.padHeight;
35996 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35998 var setSpan = this.cols + 1 - colGroup.length;
35999 for ( var i = 0; i < setSpan; i++ ) {
36000 this.colYs[ shortColIndex + i ] = setHeight ;
36007 * @param {Number} colSpan - number of columns the element spans
36008 * @returns {Array} colGroup
36010 _getColGroup : function( colSpan )
36012 if ( colSpan < 2 ) {
36013 // if brick spans only one column, use all the column Ys
36018 // how many different places could this brick fit horizontally
36019 var groupCount = this.cols + 1 - colSpan;
36020 // for each group potential horizontal position
36021 for ( var i = 0; i < groupCount; i++ ) {
36022 // make an array of colY values for that one group
36023 var groupColYs = this.colYs.slice( i, i + colSpan );
36024 // and get the max value of the array
36025 colGroup[i] = Math.max.apply( Math, groupColYs );
36030 _manageStamp : function( stamp )
36032 var stampSize = stamp.getSize();
36033 var offset = stamp.getBox();
36034 // get the columns that this stamp affects
36035 var firstX = this.isOriginLeft ? offset.x : offset.right;
36036 var lastX = firstX + stampSize.width;
36037 var firstCol = Math.floor( firstX / this.columnWidth );
36038 firstCol = Math.max( 0, firstCol );
36040 var lastCol = Math.floor( lastX / this.columnWidth );
36041 // lastCol should not go over if multiple of columnWidth #425
36042 lastCol -= lastX % this.columnWidth ? 0 : 1;
36043 lastCol = Math.min( this.cols - 1, lastCol );
36045 // set colYs to bottom of the stamp
36046 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
36049 for ( var i = firstCol; i <= lastCol; i++ ) {
36050 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
36055 _getContainerSize : function()
36057 this.maxY = Math.max.apply( Math, this.colYs );
36062 if ( this.isFitWidth ) {
36063 size.width = this._getContainerFitWidth();
36069 _getContainerFitWidth : function()
36071 var unusedCols = 0;
36072 // count unused columns
36075 if ( this.colYs[i] !== 0 ) {
36080 // fit container to columns that have been used
36081 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
36084 needsResizeLayout : function()
36086 var previousWidth = this.containerWidth;
36087 this.getContainerWidth();
36088 return previousWidth !== this.containerWidth;
36103 * @class Roo.bootstrap.MasonryBrick
36104 * @extends Roo.bootstrap.Component
36105 * Bootstrap MasonryBrick class
36108 * Create a new MasonryBrick
36109 * @param {Object} config The config object
36112 Roo.bootstrap.MasonryBrick = function(config){
36114 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
36116 Roo.bootstrap.MasonryBrick.register(this);
36122 * When a MasonryBrick is clcik
36123 * @param {Roo.bootstrap.MasonryBrick} this
36124 * @param {Roo.EventObject} e
36130 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
36133 * @cfg {String} title
36137 * @cfg {String} html
36141 * @cfg {String} bgimage
36145 * @cfg {String} videourl
36149 * @cfg {String} cls
36153 * @cfg {String} href
36157 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
36162 * @cfg {String} placetitle (center|bottom)
36167 * @cfg {Boolean} isFitContainer defalut true
36169 isFitContainer : true,
36172 * @cfg {Boolean} preventDefault defalut false
36174 preventDefault : false,
36177 * @cfg {Boolean} inverse defalut false
36179 maskInverse : false,
36181 getAutoCreate : function()
36183 if(!this.isFitContainer){
36184 return this.getSplitAutoCreate();
36187 var cls = 'masonry-brick masonry-brick-full';
36189 if(this.href.length){
36190 cls += ' masonry-brick-link';
36193 if(this.bgimage.length){
36194 cls += ' masonry-brick-image';
36197 if(this.maskInverse){
36198 cls += ' mask-inverse';
36201 if(!this.html.length && !this.maskInverse && !this.videourl.length){
36202 cls += ' enable-mask';
36206 cls += ' masonry-' + this.size + '-brick';
36209 if(this.placetitle.length){
36211 switch (this.placetitle) {
36213 cls += ' masonry-center-title';
36216 cls += ' masonry-bottom-title';
36223 if(!this.html.length && !this.bgimage.length){
36224 cls += ' masonry-center-title';
36227 if(!this.html.length && this.bgimage.length){
36228 cls += ' masonry-bottom-title';
36233 cls += ' ' + this.cls;
36237 tag: (this.href.length) ? 'a' : 'div',
36242 cls: 'masonry-brick-mask'
36246 cls: 'masonry-brick-paragraph',
36252 if(this.href.length){
36253 cfg.href = this.href;
36256 var cn = cfg.cn[1].cn;
36258 if(this.title.length){
36261 cls: 'masonry-brick-title',
36266 if(this.html.length){
36269 cls: 'masonry-brick-text',
36274 if (!this.title.length && !this.html.length) {
36275 cfg.cn[1].cls += ' hide';
36278 if(this.bgimage.length){
36281 cls: 'masonry-brick-image-view',
36286 if(this.videourl.length){
36287 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36288 // youtube support only?
36291 cls: 'masonry-brick-image-view',
36294 allowfullscreen : true
36302 getSplitAutoCreate : function()
36304 var cls = 'masonry-brick masonry-brick-split';
36306 if(this.href.length){
36307 cls += ' masonry-brick-link';
36310 if(this.bgimage.length){
36311 cls += ' masonry-brick-image';
36315 cls += ' masonry-' + this.size + '-brick';
36318 switch (this.placetitle) {
36320 cls += ' masonry-center-title';
36323 cls += ' masonry-bottom-title';
36326 if(!this.bgimage.length){
36327 cls += ' masonry-center-title';
36330 if(this.bgimage.length){
36331 cls += ' masonry-bottom-title';
36337 cls += ' ' + this.cls;
36341 tag: (this.href.length) ? 'a' : 'div',
36346 cls: 'masonry-brick-split-head',
36350 cls: 'masonry-brick-paragraph',
36357 cls: 'masonry-brick-split-body',
36363 if(this.href.length){
36364 cfg.href = this.href;
36367 if(this.title.length){
36368 cfg.cn[0].cn[0].cn.push({
36370 cls: 'masonry-brick-title',
36375 if(this.html.length){
36376 cfg.cn[1].cn.push({
36378 cls: 'masonry-brick-text',
36383 if(this.bgimage.length){
36384 cfg.cn[0].cn.push({
36386 cls: 'masonry-brick-image-view',
36391 if(this.videourl.length){
36392 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36393 // youtube support only?
36394 cfg.cn[0].cn.cn.push({
36396 cls: 'masonry-brick-image-view',
36399 allowfullscreen : true
36406 initEvents: function()
36408 switch (this.size) {
36441 this.el.on('touchstart', this.onTouchStart, this);
36442 this.el.on('touchmove', this.onTouchMove, this);
36443 this.el.on('touchend', this.onTouchEnd, this);
36444 this.el.on('contextmenu', this.onContextMenu, this);
36446 this.el.on('mouseenter' ,this.enter, this);
36447 this.el.on('mouseleave', this.leave, this);
36448 this.el.on('click', this.onClick, this);
36451 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36452 this.parent().bricks.push(this);
36457 onClick: function(e, el)
36459 var time = this.endTimer - this.startTimer;
36460 // Roo.log(e.preventDefault());
36463 e.preventDefault();
36468 if(!this.preventDefault){
36472 e.preventDefault();
36474 if (this.activeClass != '') {
36475 this.selectBrick();
36478 this.fireEvent('click', this, e);
36481 enter: function(e, el)
36483 e.preventDefault();
36485 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36489 if(this.bgimage.length && this.html.length){
36490 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36494 leave: function(e, el)
36496 e.preventDefault();
36498 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36502 if(this.bgimage.length && this.html.length){
36503 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36507 onTouchStart: function(e, el)
36509 // e.preventDefault();
36511 this.touchmoved = false;
36513 if(!this.isFitContainer){
36517 if(!this.bgimage.length || !this.html.length){
36521 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36523 this.timer = new Date().getTime();
36527 onTouchMove: function(e, el)
36529 this.touchmoved = true;
36532 onContextMenu : function(e,el)
36534 e.preventDefault();
36535 e.stopPropagation();
36539 onTouchEnd: function(e, el)
36541 // e.preventDefault();
36543 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36550 if(!this.bgimage.length || !this.html.length){
36552 if(this.href.length){
36553 window.location.href = this.href;
36559 if(!this.isFitContainer){
36563 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36565 window.location.href = this.href;
36568 //selection on single brick only
36569 selectBrick : function() {
36571 if (!this.parentId) {
36575 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36576 var index = m.selectedBrick.indexOf(this.id);
36579 m.selectedBrick.splice(index,1);
36580 this.el.removeClass(this.activeClass);
36584 for(var i = 0; i < m.selectedBrick.length; i++) {
36585 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36586 b.el.removeClass(b.activeClass);
36589 m.selectedBrick = [];
36591 m.selectedBrick.push(this.id);
36592 this.el.addClass(this.activeClass);
36596 isSelected : function(){
36597 return this.el.hasClass(this.activeClass);
36602 Roo.apply(Roo.bootstrap.MasonryBrick, {
36605 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36607 * register a Masonry Brick
36608 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36611 register : function(brick)
36613 //this.groups[brick.id] = brick;
36614 this.groups.add(brick.id, brick);
36617 * fetch a masonry brick based on the masonry brick ID
36618 * @param {string} the masonry brick to add
36619 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36622 get: function(brick_id)
36624 // if (typeof(this.groups[brick_id]) == 'undefined') {
36627 // return this.groups[brick_id] ;
36629 if(this.groups.key(brick_id)) {
36630 return this.groups.key(brick_id);
36648 * @class Roo.bootstrap.Brick
36649 * @extends Roo.bootstrap.Component
36650 * Bootstrap Brick class
36653 * Create a new Brick
36654 * @param {Object} config The config object
36657 Roo.bootstrap.Brick = function(config){
36658 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36664 * When a Brick is click
36665 * @param {Roo.bootstrap.Brick} this
36666 * @param {Roo.EventObject} e
36672 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
36675 * @cfg {String} title
36679 * @cfg {String} html
36683 * @cfg {String} bgimage
36687 * @cfg {String} cls
36691 * @cfg {String} href
36695 * @cfg {String} video
36699 * @cfg {Boolean} square
36703 getAutoCreate : function()
36705 var cls = 'roo-brick';
36707 if(this.href.length){
36708 cls += ' roo-brick-link';
36711 if(this.bgimage.length){
36712 cls += ' roo-brick-image';
36715 if(!this.html.length && !this.bgimage.length){
36716 cls += ' roo-brick-center-title';
36719 if(!this.html.length && this.bgimage.length){
36720 cls += ' roo-brick-bottom-title';
36724 cls += ' ' + this.cls;
36728 tag: (this.href.length) ? 'a' : 'div',
36733 cls: 'roo-brick-paragraph',
36739 if(this.href.length){
36740 cfg.href = this.href;
36743 var cn = cfg.cn[0].cn;
36745 if(this.title.length){
36748 cls: 'roo-brick-title',
36753 if(this.html.length){
36756 cls: 'roo-brick-text',
36763 if(this.bgimage.length){
36766 cls: 'roo-brick-image-view',
36774 initEvents: function()
36776 if(this.title.length || this.html.length){
36777 this.el.on('mouseenter' ,this.enter, this);
36778 this.el.on('mouseleave', this.leave, this);
36781 Roo.EventManager.onWindowResize(this.resize, this);
36783 if(this.bgimage.length){
36784 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36785 this.imageEl.on('load', this.onImageLoad, this);
36792 onImageLoad : function()
36797 resize : function()
36799 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36801 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36803 if(this.bgimage.length){
36804 var image = this.el.select('.roo-brick-image-view', true).first();
36806 image.setWidth(paragraph.getWidth());
36809 image.setHeight(paragraph.getWidth());
36812 this.el.setHeight(image.getHeight());
36813 paragraph.setHeight(image.getHeight());
36819 enter: function(e, el)
36821 e.preventDefault();
36823 if(this.bgimage.length){
36824 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36825 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36829 leave: function(e, el)
36831 e.preventDefault();
36833 if(this.bgimage.length){
36834 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36835 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36850 * @class Roo.bootstrap.NumberField
36851 * @extends Roo.bootstrap.Input
36852 * Bootstrap NumberField class
36858 * Create a new NumberField
36859 * @param {Object} config The config object
36862 Roo.bootstrap.NumberField = function(config){
36863 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36866 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36869 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36871 allowDecimals : true,
36873 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36875 decimalSeparator : ".",
36877 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36879 decimalPrecision : 2,
36881 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36883 allowNegative : true,
36886 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36890 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36892 minValue : Number.NEGATIVE_INFINITY,
36894 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36896 maxValue : Number.MAX_VALUE,
36898 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36900 minText : "The minimum value for this field is {0}",
36902 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36904 maxText : "The maximum value for this field is {0}",
36906 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36907 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36909 nanText : "{0} is not a valid number",
36911 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36913 thousandsDelimiter : false,
36915 * @cfg {String} valueAlign alignment of value
36917 valueAlign : "left",
36919 getAutoCreate : function()
36921 var hiddenInput = {
36925 cls: 'hidden-number-input'
36929 hiddenInput.name = this.name;
36934 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36936 this.name = hiddenInput.name;
36938 if(cfg.cn.length > 0) {
36939 cfg.cn.push(hiddenInput);
36946 initEvents : function()
36948 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36950 var allowed = "0123456789";
36952 if(this.allowDecimals){
36953 allowed += this.decimalSeparator;
36956 if(this.allowNegative){
36960 if(this.thousandsDelimiter) {
36964 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36966 var keyPress = function(e){
36968 var k = e.getKey();
36970 var c = e.getCharCode();
36973 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36974 allowed.indexOf(String.fromCharCode(c)) === -1
36980 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36984 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36989 this.el.on("keypress", keyPress, this);
36992 validateValue : function(value)
36995 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36999 var num = this.parseValue(value);
37002 this.markInvalid(String.format(this.nanText, value));
37006 if(num < this.minValue){
37007 this.markInvalid(String.format(this.minText, this.minValue));
37011 if(num > this.maxValue){
37012 this.markInvalid(String.format(this.maxText, this.maxValue));
37019 getValue : function()
37021 var v = this.hiddenEl().getValue();
37023 return this.fixPrecision(this.parseValue(v));
37026 parseValue : function(value)
37028 if(this.thousandsDelimiter) {
37030 r = new RegExp(",", "g");
37031 value = value.replace(r, "");
37034 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
37035 return isNaN(value) ? '' : value;
37038 fixPrecision : function(value)
37040 if(this.thousandsDelimiter) {
37042 r = new RegExp(",", "g");
37043 value = value.replace(r, "");
37046 var nan = isNaN(value);
37048 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
37049 return nan ? '' : value;
37051 return parseFloat(value).toFixed(this.decimalPrecision);
37054 setValue : function(v)
37056 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
37062 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
37064 this.inputEl().dom.value = (v == '') ? '' :
37065 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
37067 if(!this.allowZero && v === '0') {
37068 this.hiddenEl().dom.value = '';
37069 this.inputEl().dom.value = '';
37076 decimalPrecisionFcn : function(v)
37078 return Math.floor(v);
37081 beforeBlur : function()
37083 var v = this.parseValue(this.getRawValue());
37085 if(v || v === 0 || v === ''){
37090 hiddenEl : function()
37092 return this.el.select('input.hidden-number-input',true).first();
37104 * @class Roo.bootstrap.DocumentSlider
37105 * @extends Roo.bootstrap.Component
37106 * Bootstrap DocumentSlider class
37109 * Create a new DocumentViewer
37110 * @param {Object} config The config object
37113 Roo.bootstrap.DocumentSlider = function(config){
37114 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
37121 * Fire after initEvent
37122 * @param {Roo.bootstrap.DocumentSlider} this
37127 * Fire after update
37128 * @param {Roo.bootstrap.DocumentSlider} this
37134 * @param {Roo.bootstrap.DocumentSlider} this
37140 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
37146 getAutoCreate : function()
37150 cls : 'roo-document-slider',
37154 cls : 'roo-document-slider-header',
37158 cls : 'roo-document-slider-header-title'
37164 cls : 'roo-document-slider-body',
37168 cls : 'roo-document-slider-prev',
37172 cls : 'fa fa-chevron-left'
37178 cls : 'roo-document-slider-thumb',
37182 cls : 'roo-document-slider-image'
37188 cls : 'roo-document-slider-next',
37192 cls : 'fa fa-chevron-right'
37204 initEvents : function()
37206 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
37207 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
37209 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
37210 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
37212 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
37213 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
37215 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
37216 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
37218 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
37219 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
37221 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
37222 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37224 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
37225 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37227 this.thumbEl.on('click', this.onClick, this);
37229 this.prevIndicator.on('click', this.prev, this);
37231 this.nextIndicator.on('click', this.next, this);
37235 initial : function()
37237 if(this.files.length){
37238 this.indicator = 1;
37242 this.fireEvent('initial', this);
37245 update : function()
37247 this.imageEl.attr('src', this.files[this.indicator - 1]);
37249 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
37251 this.prevIndicator.show();
37253 if(this.indicator == 1){
37254 this.prevIndicator.hide();
37257 this.nextIndicator.show();
37259 if(this.indicator == this.files.length){
37260 this.nextIndicator.hide();
37263 this.thumbEl.scrollTo('top');
37265 this.fireEvent('update', this);
37268 onClick : function(e)
37270 e.preventDefault();
37272 this.fireEvent('click', this);
37277 e.preventDefault();
37279 this.indicator = Math.max(1, this.indicator - 1);
37286 e.preventDefault();
37288 this.indicator = Math.min(this.files.length, this.indicator + 1);
37302 * @class Roo.bootstrap.RadioSet
37303 * @extends Roo.bootstrap.Input
37304 * Bootstrap RadioSet class
37305 * @cfg {String} indicatorpos (left|right) default left
37306 * @cfg {Boolean} inline (true|false) inline the element (default true)
37307 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37309 * Create a new RadioSet
37310 * @param {Object} config The config object
37313 Roo.bootstrap.RadioSet = function(config){
37315 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
37319 Roo.bootstrap.RadioSet.register(this);
37324 * Fires when the element is checked or unchecked.
37325 * @param {Roo.bootstrap.RadioSet} this This radio
37326 * @param {Roo.bootstrap.Radio} item The checked item
37331 * Fires when the element is click.
37332 * @param {Roo.bootstrap.RadioSet} this This radio set
37333 * @param {Roo.bootstrap.Radio} item The checked item
37334 * @param {Roo.EventObject} e The event object
37341 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
37349 indicatorpos : 'left',
37351 getAutoCreate : function()
37355 cls : 'roo-radio-set-label',
37359 html : this.fieldLabel
37363 if (Roo.bootstrap.version == 3) {
37366 if(this.indicatorpos == 'left'){
37369 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37370 tooltip : 'This field is required'
37375 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37376 tooltip : 'This field is required'
37382 cls : 'roo-radio-set-items'
37385 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37387 if (align === 'left' && this.fieldLabel.length) {
37390 cls : "roo-radio-set-right",
37396 if(this.labelWidth > 12){
37397 label.style = "width: " + this.labelWidth + 'px';
37400 if(this.labelWidth < 13 && this.labelmd == 0){
37401 this.labelmd = this.labelWidth;
37404 if(this.labellg > 0){
37405 label.cls += ' col-lg-' + this.labellg;
37406 items.cls += ' col-lg-' + (12 - this.labellg);
37409 if(this.labelmd > 0){
37410 label.cls += ' col-md-' + this.labelmd;
37411 items.cls += ' col-md-' + (12 - this.labelmd);
37414 if(this.labelsm > 0){
37415 label.cls += ' col-sm-' + this.labelsm;
37416 items.cls += ' col-sm-' + (12 - this.labelsm);
37419 if(this.labelxs > 0){
37420 label.cls += ' col-xs-' + this.labelxs;
37421 items.cls += ' col-xs-' + (12 - this.labelxs);
37427 cls : 'roo-radio-set',
37431 cls : 'roo-radio-set-input',
37434 value : this.value ? this.value : ''
37441 if(this.weight.length){
37442 cfg.cls += ' roo-radio-' + this.weight;
37446 cfg.cls += ' roo-radio-set-inline';
37450 ['xs','sm','md','lg'].map(function(size){
37451 if (settings[size]) {
37452 cfg.cls += ' col-' + size + '-' + settings[size];
37460 initEvents : function()
37462 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37463 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37465 if(!this.fieldLabel.length){
37466 this.labelEl.hide();
37469 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37470 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37472 this.indicator = this.indicatorEl();
37474 if(this.indicator){
37475 this.indicator.addClass('invisible');
37478 this.originalValue = this.getValue();
37482 inputEl: function ()
37484 return this.el.select('.roo-radio-set-input', true).first();
37487 getChildContainer : function()
37489 return this.itemsEl;
37492 register : function(item)
37494 this.radioes.push(item);
37498 validate : function()
37500 if(this.getVisibilityEl().hasClass('hidden')){
37506 Roo.each(this.radioes, function(i){
37515 if(this.allowBlank) {
37519 if(this.disabled || valid){
37524 this.markInvalid();
37529 markValid : function()
37531 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37532 this.indicatorEl().removeClass('visible');
37533 this.indicatorEl().addClass('invisible');
37537 if (Roo.bootstrap.version == 3) {
37538 this.el.removeClass([this.invalidClass, this.validClass]);
37539 this.el.addClass(this.validClass);
37541 this.el.removeClass(['is-invalid','is-valid']);
37542 this.el.addClass(['is-valid']);
37544 this.fireEvent('valid', this);
37547 markInvalid : function(msg)
37549 if(this.allowBlank || this.disabled){
37553 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37554 this.indicatorEl().removeClass('invisible');
37555 this.indicatorEl().addClass('visible');
37557 if (Roo.bootstrap.version == 3) {
37558 this.el.removeClass([this.invalidClass, this.validClass]);
37559 this.el.addClass(this.invalidClass);
37561 this.el.removeClass(['is-invalid','is-valid']);
37562 this.el.addClass(['is-invalid']);
37565 this.fireEvent('invalid', this, msg);
37569 setValue : function(v, suppressEvent)
37571 if(this.value === v){
37578 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37581 Roo.each(this.radioes, function(i){
37583 i.el.removeClass('checked');
37586 Roo.each(this.radioes, function(i){
37588 if(i.value === v || i.value.toString() === v.toString()){
37590 i.el.addClass('checked');
37592 if(suppressEvent !== true){
37593 this.fireEvent('check', this, i);
37604 clearInvalid : function(){
37606 if(!this.el || this.preventMark){
37610 this.el.removeClass([this.invalidClass]);
37612 this.fireEvent('valid', this);
37617 Roo.apply(Roo.bootstrap.RadioSet, {
37621 register : function(set)
37623 this.groups[set.name] = set;
37626 get: function(name)
37628 if (typeof(this.groups[name]) == 'undefined') {
37632 return this.groups[name] ;
37638 * Ext JS Library 1.1.1
37639 * Copyright(c) 2006-2007, Ext JS, LLC.
37641 * Originally Released Under LGPL - original licence link has changed is not relivant.
37644 * <script type="text/javascript">
37649 * @class Roo.bootstrap.SplitBar
37650 * @extends Roo.util.Observable
37651 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37655 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37656 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37657 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37658 split.minSize = 100;
37659 split.maxSize = 600;
37660 split.animate = true;
37661 split.on('moved', splitterMoved);
37664 * Create a new SplitBar
37665 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
37666 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
37667 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37668 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
37669 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37670 position of the SplitBar).
37672 Roo.bootstrap.SplitBar = function(cfg){
37677 // dragElement : elm
37678 // resizingElement: el,
37680 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37681 // placement : Roo.bootstrap.SplitBar.LEFT ,
37682 // existingProxy ???
37685 this.el = Roo.get(cfg.dragElement, true);
37686 this.el.dom.unselectable = "on";
37688 this.resizingEl = Roo.get(cfg.resizingElement, true);
37692 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37693 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37696 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37699 * The minimum size of the resizing element. (Defaults to 0)
37705 * The maximum size of the resizing element. (Defaults to 2000)
37708 this.maxSize = 2000;
37711 * Whether to animate the transition to the new size
37714 this.animate = false;
37717 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37720 this.useShim = false;
37725 if(!cfg.existingProxy){
37727 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37729 this.proxy = Roo.get(cfg.existingProxy).dom;
37732 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37735 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37738 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37741 this.dragSpecs = {};
37744 * @private The adapter to use to positon and resize elements
37746 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37747 this.adapter.init(this);
37749 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37751 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37752 this.el.addClass("roo-splitbar-h");
37755 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37756 this.el.addClass("roo-splitbar-v");
37762 * Fires when the splitter is moved (alias for {@link #event-moved})
37763 * @param {Roo.bootstrap.SplitBar} this
37764 * @param {Number} newSize the new width or height
37769 * Fires when the splitter is moved
37770 * @param {Roo.bootstrap.SplitBar} this
37771 * @param {Number} newSize the new width or height
37775 * @event beforeresize
37776 * Fires before the splitter is dragged
37777 * @param {Roo.bootstrap.SplitBar} this
37779 "beforeresize" : true,
37781 "beforeapply" : true
37784 Roo.util.Observable.call(this);
37787 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37788 onStartProxyDrag : function(x, y){
37789 this.fireEvent("beforeresize", this);
37791 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37793 o.enableDisplayMode("block");
37794 // all splitbars share the same overlay
37795 Roo.bootstrap.SplitBar.prototype.overlay = o;
37797 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37798 this.overlay.show();
37799 Roo.get(this.proxy).setDisplayed("block");
37800 var size = this.adapter.getElementSize(this);
37801 this.activeMinSize = this.getMinimumSize();;
37802 this.activeMaxSize = this.getMaximumSize();;
37803 var c1 = size - this.activeMinSize;
37804 var c2 = Math.max(this.activeMaxSize - size, 0);
37805 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37806 this.dd.resetConstraints();
37807 this.dd.setXConstraint(
37808 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37809 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37811 this.dd.setYConstraint(0, 0);
37813 this.dd.resetConstraints();
37814 this.dd.setXConstraint(0, 0);
37815 this.dd.setYConstraint(
37816 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37817 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37820 this.dragSpecs.startSize = size;
37821 this.dragSpecs.startPoint = [x, y];
37822 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37826 * @private Called after the drag operation by the DDProxy
37828 onEndProxyDrag : function(e){
37829 Roo.get(this.proxy).setDisplayed(false);
37830 var endPoint = Roo.lib.Event.getXY(e);
37832 this.overlay.hide();
37835 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37836 newSize = this.dragSpecs.startSize +
37837 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37838 endPoint[0] - this.dragSpecs.startPoint[0] :
37839 this.dragSpecs.startPoint[0] - endPoint[0]
37842 newSize = this.dragSpecs.startSize +
37843 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37844 endPoint[1] - this.dragSpecs.startPoint[1] :
37845 this.dragSpecs.startPoint[1] - endPoint[1]
37848 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37849 if(newSize != this.dragSpecs.startSize){
37850 if(this.fireEvent('beforeapply', this, newSize) !== false){
37851 this.adapter.setElementSize(this, newSize);
37852 this.fireEvent("moved", this, newSize);
37853 this.fireEvent("resize", this, newSize);
37859 * Get the adapter this SplitBar uses
37860 * @return The adapter object
37862 getAdapter : function(){
37863 return this.adapter;
37867 * Set the adapter this SplitBar uses
37868 * @param {Object} adapter A SplitBar adapter object
37870 setAdapter : function(adapter){
37871 this.adapter = adapter;
37872 this.adapter.init(this);
37876 * Gets the minimum size for the resizing element
37877 * @return {Number} The minimum size
37879 getMinimumSize : function(){
37880 return this.minSize;
37884 * Sets the minimum size for the resizing element
37885 * @param {Number} minSize The minimum size
37887 setMinimumSize : function(minSize){
37888 this.minSize = minSize;
37892 * Gets the maximum size for the resizing element
37893 * @return {Number} The maximum size
37895 getMaximumSize : function(){
37896 return this.maxSize;
37900 * Sets the maximum size for the resizing element
37901 * @param {Number} maxSize The maximum size
37903 setMaximumSize : function(maxSize){
37904 this.maxSize = maxSize;
37908 * Sets the initialize size for the resizing element
37909 * @param {Number} size The initial size
37911 setCurrentSize : function(size){
37912 var oldAnimate = this.animate;
37913 this.animate = false;
37914 this.adapter.setElementSize(this, size);
37915 this.animate = oldAnimate;
37919 * Destroy this splitbar.
37920 * @param {Boolean} removeEl True to remove the element
37922 destroy : function(removeEl){
37924 this.shim.remove();
37927 this.proxy.parentNode.removeChild(this.proxy);
37935 * @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.
37937 Roo.bootstrap.SplitBar.createProxy = function(dir){
37938 var proxy = new Roo.Element(document.createElement("div"));
37939 proxy.unselectable();
37940 var cls = 'roo-splitbar-proxy';
37941 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37942 document.body.appendChild(proxy.dom);
37947 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37948 * Default Adapter. It assumes the splitter and resizing element are not positioned
37949 * elements and only gets/sets the width of the element. Generally used for table based layouts.
37951 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37954 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37955 // do nothing for now
37956 init : function(s){
37960 * Called before drag operations to get the current size of the resizing element.
37961 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37963 getElementSize : function(s){
37964 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37965 return s.resizingEl.getWidth();
37967 return s.resizingEl.getHeight();
37972 * Called after drag operations to set the size of the resizing element.
37973 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37974 * @param {Number} newSize The new size to set
37975 * @param {Function} onComplete A function to be invoked when resizing is complete
37977 setElementSize : function(s, newSize, onComplete){
37978 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37980 s.resizingEl.setWidth(newSize);
37982 onComplete(s, newSize);
37985 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37990 s.resizingEl.setHeight(newSize);
37992 onComplete(s, newSize);
37995 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
38002 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
38003 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
38004 * Adapter that moves the splitter element to align with the resized sizing element.
38005 * Used with an absolute positioned SplitBar.
38006 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
38007 * document.body, make sure you assign an id to the body element.
38009 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
38010 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
38011 this.container = Roo.get(container);
38014 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
38015 init : function(s){
38016 this.basic.init(s);
38019 getElementSize : function(s){
38020 return this.basic.getElementSize(s);
38023 setElementSize : function(s, newSize, onComplete){
38024 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
38027 moveSplitter : function(s){
38028 var yes = Roo.bootstrap.SplitBar;
38029 switch(s.placement){
38031 s.el.setX(s.resizingEl.getRight());
38034 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
38037 s.el.setY(s.resizingEl.getBottom());
38040 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
38047 * Orientation constant - Create a vertical SplitBar
38051 Roo.bootstrap.SplitBar.VERTICAL = 1;
38054 * Orientation constant - Create a horizontal SplitBar
38058 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
38061 * Placement constant - The resizing element is to the left of the splitter element
38065 Roo.bootstrap.SplitBar.LEFT = 1;
38068 * Placement constant - The resizing element is to the right of the splitter element
38072 Roo.bootstrap.SplitBar.RIGHT = 2;
38075 * Placement constant - The resizing element is positioned above the splitter element
38079 Roo.bootstrap.SplitBar.TOP = 3;
38082 * Placement constant - The resizing element is positioned under splitter element
38086 Roo.bootstrap.SplitBar.BOTTOM = 4;
38087 Roo.namespace("Roo.bootstrap.layout");/*
38089 * Ext JS Library 1.1.1
38090 * Copyright(c) 2006-2007, Ext JS, LLC.
38092 * Originally Released Under LGPL - original licence link has changed is not relivant.
38095 * <script type="text/javascript">
38099 * @class Roo.bootstrap.layout.Manager
38100 * @extends Roo.bootstrap.Component
38101 * Base class for layout managers.
38103 Roo.bootstrap.layout.Manager = function(config)
38105 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
38111 /** false to disable window resize monitoring @type Boolean */
38112 this.monitorWindowResize = true;
38117 * Fires when a layout is performed.
38118 * @param {Roo.LayoutManager} this
38122 * @event regionresized
38123 * Fires when the user resizes a region.
38124 * @param {Roo.LayoutRegion} region The resized region
38125 * @param {Number} newSize The new size (width for east/west, height for north/south)
38127 "regionresized" : true,
38129 * @event regioncollapsed
38130 * Fires when a region is collapsed.
38131 * @param {Roo.LayoutRegion} region The collapsed region
38133 "regioncollapsed" : true,
38135 * @event regionexpanded
38136 * Fires when a region is expanded.
38137 * @param {Roo.LayoutRegion} region The expanded region
38139 "regionexpanded" : true
38141 this.updating = false;
38144 this.el = Roo.get(config.el);
38150 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
38155 monitorWindowResize : true,
38161 onRender : function(ct, position)
38164 this.el = Roo.get(ct);
38167 //this.fireEvent('render',this);
38171 initEvents: function()
38175 // ie scrollbar fix
38176 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
38177 document.body.scroll = "no";
38178 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
38179 this.el.position('relative');
38181 this.id = this.el.id;
38182 this.el.addClass("roo-layout-container");
38183 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
38184 if(this.el.dom != document.body ) {
38185 this.el.on('resize', this.layout,this);
38186 this.el.on('show', this.layout,this);
38192 * Returns true if this layout is currently being updated
38193 * @return {Boolean}
38195 isUpdating : function(){
38196 return this.updating;
38200 * Suspend the LayoutManager from doing auto-layouts while
38201 * making multiple add or remove calls
38203 beginUpdate : function(){
38204 this.updating = true;
38208 * Restore auto-layouts and optionally disable the manager from performing a layout
38209 * @param {Boolean} noLayout true to disable a layout update
38211 endUpdate : function(noLayout){
38212 this.updating = false;
38218 layout: function(){
38222 onRegionResized : function(region, newSize){
38223 this.fireEvent("regionresized", region, newSize);
38227 onRegionCollapsed : function(region){
38228 this.fireEvent("regioncollapsed", region);
38231 onRegionExpanded : function(region){
38232 this.fireEvent("regionexpanded", region);
38236 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
38237 * performs box-model adjustments.
38238 * @return {Object} The size as an object {width: (the width), height: (the height)}
38240 getViewSize : function()
38243 if(this.el.dom != document.body){
38244 size = this.el.getSize();
38246 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
38248 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
38249 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38254 * Returns the Element this layout is bound to.
38255 * @return {Roo.Element}
38257 getEl : function(){
38262 * Returns the specified region.
38263 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
38264 * @return {Roo.LayoutRegion}
38266 getRegion : function(target){
38267 return this.regions[target.toLowerCase()];
38270 onWindowResize : function(){
38271 if(this.monitorWindowResize){
38278 * Ext JS Library 1.1.1
38279 * Copyright(c) 2006-2007, Ext JS, LLC.
38281 * Originally Released Under LGPL - original licence link has changed is not relivant.
38284 * <script type="text/javascript">
38287 * @class Roo.bootstrap.layout.Border
38288 * @extends Roo.bootstrap.layout.Manager
38289 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38290 * please see: examples/bootstrap/nested.html<br><br>
38292 <b>The container the layout is rendered into can be either the body element or any other element.
38293 If it is not the body element, the container needs to either be an absolute positioned element,
38294 or you will need to add "position:relative" to the css of the container. You will also need to specify
38295 the container size if it is not the body element.</b>
38298 * Create a new Border
38299 * @param {Object} config Configuration options
38301 Roo.bootstrap.layout.Border = function(config){
38302 config = config || {};
38303 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38307 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38308 if(config[region]){
38309 config[region].region = region;
38310 this.addRegion(config[region]);
38316 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
38318 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38320 parent : false, // this might point to a 'nest' or a ???
38323 * Creates and adds a new region if it doesn't already exist.
38324 * @param {String} target The target region key (north, south, east, west or center).
38325 * @param {Object} config The regions config object
38326 * @return {BorderLayoutRegion} The new region
38328 addRegion : function(config)
38330 if(!this.regions[config.region]){
38331 var r = this.factory(config);
38332 this.bindRegion(r);
38334 return this.regions[config.region];
38338 bindRegion : function(r){
38339 this.regions[r.config.region] = r;
38341 r.on("visibilitychange", this.layout, this);
38342 r.on("paneladded", this.layout, this);
38343 r.on("panelremoved", this.layout, this);
38344 r.on("invalidated", this.layout, this);
38345 r.on("resized", this.onRegionResized, this);
38346 r.on("collapsed", this.onRegionCollapsed, this);
38347 r.on("expanded", this.onRegionExpanded, this);
38351 * Performs a layout update.
38353 layout : function()
38355 if(this.updating) {
38359 // render all the rebions if they have not been done alreayd?
38360 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38361 if(this.regions[region] && !this.regions[region].bodyEl){
38362 this.regions[region].onRender(this.el)
38366 var size = this.getViewSize();
38367 var w = size.width;
38368 var h = size.height;
38373 //var x = 0, y = 0;
38375 var rs = this.regions;
38376 var north = rs["north"];
38377 var south = rs["south"];
38378 var west = rs["west"];
38379 var east = rs["east"];
38380 var center = rs["center"];
38381 //if(this.hideOnLayout){ // not supported anymore
38382 //c.el.setStyle("display", "none");
38384 if(north && north.isVisible()){
38385 var b = north.getBox();
38386 var m = north.getMargins();
38387 b.width = w - (m.left+m.right);
38390 centerY = b.height + b.y + m.bottom;
38391 centerH -= centerY;
38392 north.updateBox(this.safeBox(b));
38394 if(south && south.isVisible()){
38395 var b = south.getBox();
38396 var m = south.getMargins();
38397 b.width = w - (m.left+m.right);
38399 var totalHeight = (b.height + m.top + m.bottom);
38400 b.y = h - totalHeight + m.top;
38401 centerH -= totalHeight;
38402 south.updateBox(this.safeBox(b));
38404 if(west && west.isVisible()){
38405 var b = west.getBox();
38406 var m = west.getMargins();
38407 b.height = centerH - (m.top+m.bottom);
38409 b.y = centerY + m.top;
38410 var totalWidth = (b.width + m.left + m.right);
38411 centerX += totalWidth;
38412 centerW -= totalWidth;
38413 west.updateBox(this.safeBox(b));
38415 if(east && east.isVisible()){
38416 var b = east.getBox();
38417 var m = east.getMargins();
38418 b.height = centerH - (m.top+m.bottom);
38419 var totalWidth = (b.width + m.left + m.right);
38420 b.x = w - totalWidth + m.left;
38421 b.y = centerY + m.top;
38422 centerW -= totalWidth;
38423 east.updateBox(this.safeBox(b));
38426 var m = center.getMargins();
38428 x: centerX + m.left,
38429 y: centerY + m.top,
38430 width: centerW - (m.left+m.right),
38431 height: centerH - (m.top+m.bottom)
38433 //if(this.hideOnLayout){
38434 //center.el.setStyle("display", "block");
38436 center.updateBox(this.safeBox(centerBox));
38439 this.fireEvent("layout", this);
38443 safeBox : function(box){
38444 box.width = Math.max(0, box.width);
38445 box.height = Math.max(0, box.height);
38450 * Adds a ContentPanel (or subclass) to this layout.
38451 * @param {String} target The target region key (north, south, east, west or center).
38452 * @param {Roo.ContentPanel} panel The panel to add
38453 * @return {Roo.ContentPanel} The added panel
38455 add : function(target, panel){
38457 target = target.toLowerCase();
38458 return this.regions[target].add(panel);
38462 * Remove a ContentPanel (or subclass) to this layout.
38463 * @param {String} target The target region key (north, south, east, west or center).
38464 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38465 * @return {Roo.ContentPanel} The removed panel
38467 remove : function(target, panel){
38468 target = target.toLowerCase();
38469 return this.regions[target].remove(panel);
38473 * Searches all regions for a panel with the specified id
38474 * @param {String} panelId
38475 * @return {Roo.ContentPanel} The panel or null if it wasn't found
38477 findPanel : function(panelId){
38478 var rs = this.regions;
38479 for(var target in rs){
38480 if(typeof rs[target] != "function"){
38481 var p = rs[target].getPanel(panelId);
38491 * Searches all regions for a panel with the specified id and activates (shows) it.
38492 * @param {String/ContentPanel} panelId The panels id or the panel itself
38493 * @return {Roo.ContentPanel} The shown panel or null
38495 showPanel : function(panelId) {
38496 var rs = this.regions;
38497 for(var target in rs){
38498 var r = rs[target];
38499 if(typeof r != "function"){
38500 if(r.hasPanel(panelId)){
38501 return r.showPanel(panelId);
38509 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38510 * @param {Roo.state.Provider} provider (optional) An alternate state provider
38513 restoreState : function(provider){
38515 provider = Roo.state.Manager;
38517 var sm = new Roo.LayoutStateManager();
38518 sm.init(this, provider);
38524 * Adds a xtype elements to the layout.
38528 xtype : 'ContentPanel',
38535 xtype : 'NestedLayoutPanel',
38541 items : [ ... list of content panels or nested layout panels.. ]
38545 * @param {Object} cfg Xtype definition of item to add.
38547 addxtype : function(cfg)
38549 // basically accepts a pannel...
38550 // can accept a layout region..!?!?
38551 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38554 // theory? children can only be panels??
38556 //if (!cfg.xtype.match(/Panel$/)) {
38561 if (typeof(cfg.region) == 'undefined') {
38562 Roo.log("Failed to add Panel, region was not set");
38566 var region = cfg.region;
38572 xitems = cfg.items;
38577 if ( region == 'center') {
38578 Roo.log("Center: " + cfg.title);
38584 case 'Content': // ContentPanel (el, cfg)
38585 case 'Scroll': // ContentPanel (el, cfg)
38587 cfg.autoCreate = cfg.autoCreate || true;
38588 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38590 // var el = this.el.createChild();
38591 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38594 this.add(region, ret);
38598 case 'TreePanel': // our new panel!
38599 cfg.el = this.el.createChild();
38600 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38601 this.add(region, ret);
38606 // create a new Layout (which is a Border Layout...
38608 var clayout = cfg.layout;
38609 clayout.el = this.el.createChild();
38610 clayout.items = clayout.items || [];
38614 // replace this exitems with the clayout ones..
38615 xitems = clayout.items;
38617 // force background off if it's in center...
38618 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38619 cfg.background = false;
38621 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
38624 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38625 //console.log('adding nested layout panel ' + cfg.toSource());
38626 this.add(region, ret);
38627 nb = {}; /// find first...
38632 // needs grid and region
38634 //var el = this.getRegion(region).el.createChild();
38636 *var el = this.el.createChild();
38637 // create the grid first...
38638 cfg.grid.container = el;
38639 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38642 if (region == 'center' && this.active ) {
38643 cfg.background = false;
38646 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38648 this.add(region, ret);
38650 if (cfg.background) {
38651 // render grid on panel activation (if panel background)
38652 ret.on('activate', function(gp) {
38653 if (!gp.grid.rendered) {
38654 // gp.grid.render(el);
38658 // cfg.grid.render(el);
38664 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38665 // it was the old xcomponent building that caused this before.
38666 // espeically if border is the top element in the tree.
38676 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38678 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38679 this.add(region, ret);
38683 throw "Can not add '" + cfg.xtype + "' to Border";
38689 this.beginUpdate();
38693 Roo.each(xitems, function(i) {
38694 region = nb && i.region ? i.region : false;
38696 var add = ret.addxtype(i);
38699 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38700 if (!i.background) {
38701 abn[region] = nb[region] ;
38708 // make the last non-background panel active..
38709 //if (nb) { Roo.log(abn); }
38712 for(var r in abn) {
38713 region = this.getRegion(r);
38715 // tried using nb[r], but it does not work..
38717 region.showPanel(abn[r]);
38728 factory : function(cfg)
38731 var validRegions = Roo.bootstrap.layout.Border.regions;
38733 var target = cfg.region;
38736 var r = Roo.bootstrap.layout;
38740 return new r.North(cfg);
38742 return new r.South(cfg);
38744 return new r.East(cfg);
38746 return new r.West(cfg);
38748 return new r.Center(cfg);
38750 throw 'Layout region "'+target+'" not supported.';
38757 * Ext JS Library 1.1.1
38758 * Copyright(c) 2006-2007, Ext JS, LLC.
38760 * Originally Released Under LGPL - original licence link has changed is not relivant.
38763 * <script type="text/javascript">
38767 * @class Roo.bootstrap.layout.Basic
38768 * @extends Roo.util.Observable
38769 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38770 * and does not have a titlebar, tabs or any other features. All it does is size and position
38771 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38772 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38773 * @cfg {string} region the region that it inhabits..
38774 * @cfg {bool} skipConfig skip config?
38778 Roo.bootstrap.layout.Basic = function(config){
38780 this.mgr = config.mgr;
38782 this.position = config.region;
38784 var skipConfig = config.skipConfig;
38788 * @scope Roo.BasicLayoutRegion
38792 * @event beforeremove
38793 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38794 * @param {Roo.LayoutRegion} this
38795 * @param {Roo.ContentPanel} panel The panel
38796 * @param {Object} e The cancel event object
38798 "beforeremove" : true,
38800 * @event invalidated
38801 * Fires when the layout for this region is changed.
38802 * @param {Roo.LayoutRegion} this
38804 "invalidated" : true,
38806 * @event visibilitychange
38807 * Fires when this region is shown or hidden
38808 * @param {Roo.LayoutRegion} this
38809 * @param {Boolean} visibility true or false
38811 "visibilitychange" : true,
38813 * @event paneladded
38814 * Fires when a panel is added.
38815 * @param {Roo.LayoutRegion} this
38816 * @param {Roo.ContentPanel} panel The panel
38818 "paneladded" : true,
38820 * @event panelremoved
38821 * Fires when a panel is removed.
38822 * @param {Roo.LayoutRegion} this
38823 * @param {Roo.ContentPanel} panel The panel
38825 "panelremoved" : true,
38827 * @event beforecollapse
38828 * Fires when this region before collapse.
38829 * @param {Roo.LayoutRegion} this
38831 "beforecollapse" : true,
38834 * Fires when this region is collapsed.
38835 * @param {Roo.LayoutRegion} this
38837 "collapsed" : true,
38840 * Fires when this region is expanded.
38841 * @param {Roo.LayoutRegion} this
38846 * Fires when this region is slid into view.
38847 * @param {Roo.LayoutRegion} this
38849 "slideshow" : true,
38852 * Fires when this region slides out of view.
38853 * @param {Roo.LayoutRegion} this
38855 "slidehide" : true,
38857 * @event panelactivated
38858 * Fires when a panel is activated.
38859 * @param {Roo.LayoutRegion} this
38860 * @param {Roo.ContentPanel} panel The activated panel
38862 "panelactivated" : true,
38865 * Fires when the user resizes this region.
38866 * @param {Roo.LayoutRegion} this
38867 * @param {Number} newSize The new size (width for east/west, height for north/south)
38871 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38872 this.panels = new Roo.util.MixedCollection();
38873 this.panels.getKey = this.getPanelId.createDelegate(this);
38875 this.activePanel = null;
38876 // ensure listeners are added...
38878 if (config.listeners || config.events) {
38879 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38880 listeners : config.listeners || {},
38881 events : config.events || {}
38885 if(skipConfig !== true){
38886 this.applyConfig(config);
38890 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38892 getPanelId : function(p){
38896 applyConfig : function(config){
38897 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38898 this.config = config;
38903 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38904 * the width, for horizontal (north, south) the height.
38905 * @param {Number} newSize The new width or height
38907 resizeTo : function(newSize){
38908 var el = this.el ? this.el :
38909 (this.activePanel ? this.activePanel.getEl() : null);
38911 switch(this.position){
38914 el.setWidth(newSize);
38915 this.fireEvent("resized", this, newSize);
38919 el.setHeight(newSize);
38920 this.fireEvent("resized", this, newSize);
38926 getBox : function(){
38927 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38930 getMargins : function(){
38931 return this.margins;
38934 updateBox : function(box){
38936 var el = this.activePanel.getEl();
38937 el.dom.style.left = box.x + "px";
38938 el.dom.style.top = box.y + "px";
38939 this.activePanel.setSize(box.width, box.height);
38943 * Returns the container element for this region.
38944 * @return {Roo.Element}
38946 getEl : function(){
38947 return this.activePanel;
38951 * Returns true if this region is currently visible.
38952 * @return {Boolean}
38954 isVisible : function(){
38955 return this.activePanel ? true : false;
38958 setActivePanel : function(panel){
38959 panel = this.getPanel(panel);
38960 if(this.activePanel && this.activePanel != panel){
38961 this.activePanel.setActiveState(false);
38962 this.activePanel.getEl().setLeftTop(-10000,-10000);
38964 this.activePanel = panel;
38965 panel.setActiveState(true);
38967 panel.setSize(this.box.width, this.box.height);
38969 this.fireEvent("panelactivated", this, panel);
38970 this.fireEvent("invalidated");
38974 * Show the specified panel.
38975 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38976 * @return {Roo.ContentPanel} The shown panel or null
38978 showPanel : function(panel){
38979 panel = this.getPanel(panel);
38981 this.setActivePanel(panel);
38987 * Get the active panel for this region.
38988 * @return {Roo.ContentPanel} The active panel or null
38990 getActivePanel : function(){
38991 return this.activePanel;
38995 * Add the passed ContentPanel(s)
38996 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38997 * @return {Roo.ContentPanel} The panel added (if only one was added)
38999 add : function(panel){
39000 if(arguments.length > 1){
39001 for(var i = 0, len = arguments.length; i < len; i++) {
39002 this.add(arguments[i]);
39006 if(this.hasPanel(panel)){
39007 this.showPanel(panel);
39010 var el = panel.getEl();
39011 if(el.dom.parentNode != this.mgr.el.dom){
39012 this.mgr.el.dom.appendChild(el.dom);
39014 if(panel.setRegion){
39015 panel.setRegion(this);
39017 this.panels.add(panel);
39018 el.setStyle("position", "absolute");
39019 if(!panel.background){
39020 this.setActivePanel(panel);
39021 if(this.config.initialSize && this.panels.getCount()==1){
39022 this.resizeTo(this.config.initialSize);
39025 this.fireEvent("paneladded", this, panel);
39030 * Returns true if the panel is in this region.
39031 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39032 * @return {Boolean}
39034 hasPanel : function(panel){
39035 if(typeof panel == "object"){ // must be panel obj
39036 panel = panel.getId();
39038 return this.getPanel(panel) ? true : false;
39042 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39043 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39044 * @param {Boolean} preservePanel Overrides the config preservePanel option
39045 * @return {Roo.ContentPanel} The panel that was removed
39047 remove : function(panel, preservePanel){
39048 panel = this.getPanel(panel);
39053 this.fireEvent("beforeremove", this, panel, e);
39054 if(e.cancel === true){
39057 var panelId = panel.getId();
39058 this.panels.removeKey(panelId);
39063 * Returns the panel specified or null if it's not in this region.
39064 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39065 * @return {Roo.ContentPanel}
39067 getPanel : function(id){
39068 if(typeof id == "object"){ // must be panel obj
39071 return this.panels.get(id);
39075 * Returns this regions position (north/south/east/west/center).
39078 getPosition: function(){
39079 return this.position;
39083 * Ext JS Library 1.1.1
39084 * Copyright(c) 2006-2007, Ext JS, LLC.
39086 * Originally Released Under LGPL - original licence link has changed is not relivant.
39089 * <script type="text/javascript">
39093 * @class Roo.bootstrap.layout.Region
39094 * @extends Roo.bootstrap.layout.Basic
39095 * This class represents a region in a layout manager.
39097 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
39098 * @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})
39099 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
39100 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
39101 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
39102 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
39103 * @cfg {String} title The title for the region (overrides panel titles)
39104 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
39105 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
39106 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
39107 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
39108 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
39109 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
39110 * the space available, similar to FireFox 1.5 tabs (defaults to false)
39111 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
39112 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
39113 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
39115 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
39116 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
39117 * @cfg {Boolean} disableTabTips True to disable tab tooltips
39118 * @cfg {Number} width For East/West panels
39119 * @cfg {Number} height For North/South panels
39120 * @cfg {Boolean} split To show the splitter
39121 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
39123 * @cfg {string} cls Extra CSS classes to add to region
39125 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
39126 * @cfg {string} region the region that it inhabits..
39129 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
39130 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
39132 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
39133 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
39134 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
39136 Roo.bootstrap.layout.Region = function(config)
39138 this.applyConfig(config);
39140 var mgr = config.mgr;
39141 var pos = config.region;
39142 config.skipConfig = true;
39143 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
39146 this.onRender(mgr.el);
39149 this.visible = true;
39150 this.collapsed = false;
39151 this.unrendered_panels = [];
39154 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
39156 position: '', // set by wrapper (eg. north/south etc..)
39157 unrendered_panels : null, // unrendered panels.
39159 tabPosition : false,
39161 mgr: false, // points to 'Border'
39164 createBody : function(){
39165 /** This region's body element
39166 * @type Roo.Element */
39167 this.bodyEl = this.el.createChild({
39169 cls: "roo-layout-panel-body tab-content" // bootstrap added...
39173 onRender: function(ctr, pos)
39175 var dh = Roo.DomHelper;
39176 /** This region's container element
39177 * @type Roo.Element */
39178 this.el = dh.append(ctr.dom, {
39180 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
39182 /** This region's title element
39183 * @type Roo.Element */
39185 this.titleEl = dh.append(this.el.dom, {
39187 unselectable: "on",
39188 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
39190 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
39191 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
39195 this.titleEl.enableDisplayMode();
39196 /** This region's title text element
39197 * @type HTMLElement */
39198 this.titleTextEl = this.titleEl.dom.firstChild;
39199 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
39201 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
39202 this.closeBtn.enableDisplayMode();
39203 this.closeBtn.on("click", this.closeClicked, this);
39204 this.closeBtn.hide();
39206 this.createBody(this.config);
39207 if(this.config.hideWhenEmpty){
39209 this.on("paneladded", this.validateVisibility, this);
39210 this.on("panelremoved", this.validateVisibility, this);
39212 if(this.autoScroll){
39213 this.bodyEl.setStyle("overflow", "auto");
39215 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
39217 //if(c.titlebar !== false){
39218 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
39219 this.titleEl.hide();
39221 this.titleEl.show();
39222 if(this.config.title){
39223 this.titleTextEl.innerHTML = this.config.title;
39227 if(this.config.collapsed){
39228 this.collapse(true);
39230 if(this.config.hidden){
39234 if (this.unrendered_panels && this.unrendered_panels.length) {
39235 for (var i =0;i< this.unrendered_panels.length; i++) {
39236 this.add(this.unrendered_panels[i]);
39238 this.unrendered_panels = null;
39244 applyConfig : function(c)
39247 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39248 var dh = Roo.DomHelper;
39249 if(c.titlebar !== false){
39250 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39251 this.collapseBtn.on("click", this.collapse, this);
39252 this.collapseBtn.enableDisplayMode();
39254 if(c.showPin === true || this.showPin){
39255 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39256 this.stickBtn.enableDisplayMode();
39257 this.stickBtn.on("click", this.expand, this);
39258 this.stickBtn.hide();
39263 /** This region's collapsed element
39264 * @type Roo.Element */
39267 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39268 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39271 if(c.floatable !== false){
39272 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39273 this.collapsedEl.on("click", this.collapseClick, this);
39276 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39277 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39278 id: "message", unselectable: "on", style:{"float":"left"}});
39279 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39281 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39282 this.expandBtn.on("click", this.expand, this);
39286 if(this.collapseBtn){
39287 this.collapseBtn.setVisible(c.collapsible == true);
39290 this.cmargins = c.cmargins || this.cmargins ||
39291 (this.position == "west" || this.position == "east" ?
39292 {top: 0, left: 2, right:2, bottom: 0} :
39293 {top: 2, left: 0, right:0, bottom: 2});
39295 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39298 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39300 this.autoScroll = c.autoScroll || false;
39305 this.duration = c.duration || .30;
39306 this.slideDuration = c.slideDuration || .45;
39311 * Returns true if this region is currently visible.
39312 * @return {Boolean}
39314 isVisible : function(){
39315 return this.visible;
39319 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39320 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
39322 //setCollapsedTitle : function(title){
39323 // title = title || " ";
39324 // if(this.collapsedTitleTextEl){
39325 // this.collapsedTitleTextEl.innerHTML = title;
39329 getBox : function(){
39331 // if(!this.collapsed){
39332 b = this.el.getBox(false, true);
39334 // b = this.collapsedEl.getBox(false, true);
39339 getMargins : function(){
39340 return this.margins;
39341 //return this.collapsed ? this.cmargins : this.margins;
39344 highlight : function(){
39345 this.el.addClass("x-layout-panel-dragover");
39348 unhighlight : function(){
39349 this.el.removeClass("x-layout-panel-dragover");
39352 updateBox : function(box)
39354 if (!this.bodyEl) {
39355 return; // not rendered yet..
39359 if(!this.collapsed){
39360 this.el.dom.style.left = box.x + "px";
39361 this.el.dom.style.top = box.y + "px";
39362 this.updateBody(box.width, box.height);
39364 this.collapsedEl.dom.style.left = box.x + "px";
39365 this.collapsedEl.dom.style.top = box.y + "px";
39366 this.collapsedEl.setSize(box.width, box.height);
39369 this.tabs.autoSizeTabs();
39373 updateBody : function(w, h)
39376 this.el.setWidth(w);
39377 w -= this.el.getBorderWidth("rl");
39378 if(this.config.adjustments){
39379 w += this.config.adjustments[0];
39382 if(h !== null && h > 0){
39383 this.el.setHeight(h);
39384 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39385 h -= this.el.getBorderWidth("tb");
39386 if(this.config.adjustments){
39387 h += this.config.adjustments[1];
39389 this.bodyEl.setHeight(h);
39391 h = this.tabs.syncHeight(h);
39394 if(this.panelSize){
39395 w = w !== null ? w : this.panelSize.width;
39396 h = h !== null ? h : this.panelSize.height;
39398 if(this.activePanel){
39399 var el = this.activePanel.getEl();
39400 w = w !== null ? w : el.getWidth();
39401 h = h !== null ? h : el.getHeight();
39402 this.panelSize = {width: w, height: h};
39403 this.activePanel.setSize(w, h);
39405 if(Roo.isIE && this.tabs){
39406 this.tabs.el.repaint();
39411 * Returns the container element for this region.
39412 * @return {Roo.Element}
39414 getEl : function(){
39419 * Hides this region.
39422 //if(!this.collapsed){
39423 this.el.dom.style.left = "-2000px";
39426 // this.collapsedEl.dom.style.left = "-2000px";
39427 // this.collapsedEl.hide();
39429 this.visible = false;
39430 this.fireEvent("visibilitychange", this, false);
39434 * Shows this region if it was previously hidden.
39437 //if(!this.collapsed){
39440 // this.collapsedEl.show();
39442 this.visible = true;
39443 this.fireEvent("visibilitychange", this, true);
39446 closeClicked : function(){
39447 if(this.activePanel){
39448 this.remove(this.activePanel);
39452 collapseClick : function(e){
39454 e.stopPropagation();
39457 e.stopPropagation();
39463 * Collapses this region.
39464 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39467 collapse : function(skipAnim, skipCheck = false){
39468 if(this.collapsed) {
39472 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39474 this.collapsed = true;
39476 this.split.el.hide();
39478 if(this.config.animate && skipAnim !== true){
39479 this.fireEvent("invalidated", this);
39480 this.animateCollapse();
39482 this.el.setLocation(-20000,-20000);
39484 this.collapsedEl.show();
39485 this.fireEvent("collapsed", this);
39486 this.fireEvent("invalidated", this);
39492 animateCollapse : function(){
39497 * Expands this region if it was previously collapsed.
39498 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39499 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39502 expand : function(e, skipAnim){
39504 e.stopPropagation();
39506 if(!this.collapsed || this.el.hasActiveFx()) {
39510 this.afterSlideIn();
39513 this.collapsed = false;
39514 if(this.config.animate && skipAnim !== true){
39515 this.animateExpand();
39519 this.split.el.show();
39521 this.collapsedEl.setLocation(-2000,-2000);
39522 this.collapsedEl.hide();
39523 this.fireEvent("invalidated", this);
39524 this.fireEvent("expanded", this);
39528 animateExpand : function(){
39532 initTabs : function()
39534 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39536 var ts = new Roo.bootstrap.panel.Tabs({
39537 el: this.bodyEl.dom,
39539 tabPosition: this.tabPosition ? this.tabPosition : 'top',
39540 disableTooltips: this.config.disableTabTips,
39541 toolbar : this.config.toolbar
39544 if(this.config.hideTabs){
39545 ts.stripWrap.setDisplayed(false);
39548 ts.resizeTabs = this.config.resizeTabs === true;
39549 ts.minTabWidth = this.config.minTabWidth || 40;
39550 ts.maxTabWidth = this.config.maxTabWidth || 250;
39551 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39552 ts.monitorResize = false;
39553 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39554 ts.bodyEl.addClass('roo-layout-tabs-body');
39555 this.panels.each(this.initPanelAsTab, this);
39558 initPanelAsTab : function(panel){
39559 var ti = this.tabs.addTab(
39563 this.config.closeOnTab && panel.isClosable(),
39566 if(panel.tabTip !== undefined){
39567 ti.setTooltip(panel.tabTip);
39569 ti.on("activate", function(){
39570 this.setActivePanel(panel);
39573 if(this.config.closeOnTab){
39574 ti.on("beforeclose", function(t, e){
39576 this.remove(panel);
39580 panel.tabItem = ti;
39585 updatePanelTitle : function(panel, title)
39587 if(this.activePanel == panel){
39588 this.updateTitle(title);
39591 var ti = this.tabs.getTab(panel.getEl().id);
39593 if(panel.tabTip !== undefined){
39594 ti.setTooltip(panel.tabTip);
39599 updateTitle : function(title){
39600 if(this.titleTextEl && !this.config.title){
39601 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
39605 setActivePanel : function(panel)
39607 panel = this.getPanel(panel);
39608 if(this.activePanel && this.activePanel != panel){
39609 if(this.activePanel.setActiveState(false) === false){
39613 this.activePanel = panel;
39614 panel.setActiveState(true);
39615 if(this.panelSize){
39616 panel.setSize(this.panelSize.width, this.panelSize.height);
39619 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39621 this.updateTitle(panel.getTitle());
39623 this.fireEvent("invalidated", this);
39625 this.fireEvent("panelactivated", this, panel);
39629 * Shows the specified panel.
39630 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39631 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39633 showPanel : function(panel)
39635 panel = this.getPanel(panel);
39638 var tab = this.tabs.getTab(panel.getEl().id);
39639 if(tab.isHidden()){
39640 this.tabs.unhideTab(tab.id);
39644 this.setActivePanel(panel);
39651 * Get the active panel for this region.
39652 * @return {Roo.ContentPanel} The active panel or null
39654 getActivePanel : function(){
39655 return this.activePanel;
39658 validateVisibility : function(){
39659 if(this.panels.getCount() < 1){
39660 this.updateTitle(" ");
39661 this.closeBtn.hide();
39664 if(!this.isVisible()){
39671 * Adds the passed ContentPanel(s) to this region.
39672 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39673 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39675 add : function(panel)
39677 if(arguments.length > 1){
39678 for(var i = 0, len = arguments.length; i < len; i++) {
39679 this.add(arguments[i]);
39684 // if we have not been rendered yet, then we can not really do much of this..
39685 if (!this.bodyEl) {
39686 this.unrendered_panels.push(panel);
39693 if(this.hasPanel(panel)){
39694 this.showPanel(panel);
39697 panel.setRegion(this);
39698 this.panels.add(panel);
39699 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39700 // sinle panel - no tab...?? would it not be better to render it with the tabs,
39701 // and hide them... ???
39702 this.bodyEl.dom.appendChild(panel.getEl().dom);
39703 if(panel.background !== true){
39704 this.setActivePanel(panel);
39706 this.fireEvent("paneladded", this, panel);
39713 this.initPanelAsTab(panel);
39717 if(panel.background !== true){
39718 this.tabs.activate(panel.getEl().id);
39720 this.fireEvent("paneladded", this, panel);
39725 * Hides the tab for the specified panel.
39726 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39728 hidePanel : function(panel){
39729 if(this.tabs && (panel = this.getPanel(panel))){
39730 this.tabs.hideTab(panel.getEl().id);
39735 * Unhides the tab for a previously hidden panel.
39736 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39738 unhidePanel : function(panel){
39739 if(this.tabs && (panel = this.getPanel(panel))){
39740 this.tabs.unhideTab(panel.getEl().id);
39744 clearPanels : function(){
39745 while(this.panels.getCount() > 0){
39746 this.remove(this.panels.first());
39751 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39752 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39753 * @param {Boolean} preservePanel Overrides the config preservePanel option
39754 * @return {Roo.ContentPanel} The panel that was removed
39756 remove : function(panel, preservePanel)
39758 panel = this.getPanel(panel);
39763 this.fireEvent("beforeremove", this, panel, e);
39764 if(e.cancel === true){
39767 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39768 var panelId = panel.getId();
39769 this.panels.removeKey(panelId);
39771 document.body.appendChild(panel.getEl().dom);
39774 this.tabs.removeTab(panel.getEl().id);
39775 }else if (!preservePanel){
39776 this.bodyEl.dom.removeChild(panel.getEl().dom);
39778 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39779 var p = this.panels.first();
39780 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39781 tempEl.appendChild(p.getEl().dom);
39782 this.bodyEl.update("");
39783 this.bodyEl.dom.appendChild(p.getEl().dom);
39785 this.updateTitle(p.getTitle());
39787 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39788 this.setActivePanel(p);
39790 panel.setRegion(null);
39791 if(this.activePanel == panel){
39792 this.activePanel = null;
39794 if(this.config.autoDestroy !== false && preservePanel !== true){
39795 try{panel.destroy();}catch(e){}
39797 this.fireEvent("panelremoved", this, panel);
39802 * Returns the TabPanel component used by this region
39803 * @return {Roo.TabPanel}
39805 getTabs : function(){
39809 createTool : function(parentEl, className){
39810 var btn = Roo.DomHelper.append(parentEl, {
39812 cls: "x-layout-tools-button",
39815 cls: "roo-layout-tools-button-inner " + className,
39819 btn.addClassOnOver("roo-layout-tools-button-over");
39824 * Ext JS Library 1.1.1
39825 * Copyright(c) 2006-2007, Ext JS, LLC.
39827 * Originally Released Under LGPL - original licence link has changed is not relivant.
39830 * <script type="text/javascript">
39836 * @class Roo.SplitLayoutRegion
39837 * @extends Roo.LayoutRegion
39838 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39840 Roo.bootstrap.layout.Split = function(config){
39841 this.cursor = config.cursor;
39842 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39845 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39847 splitTip : "Drag to resize.",
39848 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39849 useSplitTips : false,
39851 applyConfig : function(config){
39852 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39855 onRender : function(ctr,pos) {
39857 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39858 if(!this.config.split){
39863 var splitEl = Roo.DomHelper.append(ctr.dom, {
39865 id: this.el.id + "-split",
39866 cls: "roo-layout-split roo-layout-split-"+this.position,
39869 /** The SplitBar for this region
39870 * @type Roo.SplitBar */
39871 // does not exist yet...
39872 Roo.log([this.position, this.orientation]);
39874 this.split = new Roo.bootstrap.SplitBar({
39875 dragElement : splitEl,
39876 resizingElement: this.el,
39877 orientation : this.orientation
39880 this.split.on("moved", this.onSplitMove, this);
39881 this.split.useShim = this.config.useShim === true;
39882 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39883 if(this.useSplitTips){
39884 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39886 //if(config.collapsible){
39887 // this.split.el.on("dblclick", this.collapse, this);
39890 if(typeof this.config.minSize != "undefined"){
39891 this.split.minSize = this.config.minSize;
39893 if(typeof this.config.maxSize != "undefined"){
39894 this.split.maxSize = this.config.maxSize;
39896 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39897 this.hideSplitter();
39902 getHMaxSize : function(){
39903 var cmax = this.config.maxSize || 10000;
39904 var center = this.mgr.getRegion("center");
39905 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39908 getVMaxSize : function(){
39909 var cmax = this.config.maxSize || 10000;
39910 var center = this.mgr.getRegion("center");
39911 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39914 onSplitMove : function(split, newSize){
39915 this.fireEvent("resized", this, newSize);
39919 * Returns the {@link Roo.SplitBar} for this region.
39920 * @return {Roo.SplitBar}
39922 getSplitBar : function(){
39927 this.hideSplitter();
39928 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39931 hideSplitter : function(){
39933 this.split.el.setLocation(-2000,-2000);
39934 this.split.el.hide();
39940 this.split.el.show();
39942 Roo.bootstrap.layout.Split.superclass.show.call(this);
39945 beforeSlide: function(){
39946 if(Roo.isGecko){// firefox overflow auto bug workaround
39947 this.bodyEl.clip();
39949 this.tabs.bodyEl.clip();
39951 if(this.activePanel){
39952 this.activePanel.getEl().clip();
39954 if(this.activePanel.beforeSlide){
39955 this.activePanel.beforeSlide();
39961 afterSlide : function(){
39962 if(Roo.isGecko){// firefox overflow auto bug workaround
39963 this.bodyEl.unclip();
39965 this.tabs.bodyEl.unclip();
39967 if(this.activePanel){
39968 this.activePanel.getEl().unclip();
39969 if(this.activePanel.afterSlide){
39970 this.activePanel.afterSlide();
39976 initAutoHide : function(){
39977 if(this.autoHide !== false){
39978 if(!this.autoHideHd){
39979 var st = new Roo.util.DelayedTask(this.slideIn, this);
39980 this.autoHideHd = {
39981 "mouseout": function(e){
39982 if(!e.within(this.el, true)){
39986 "mouseover" : function(e){
39992 this.el.on(this.autoHideHd);
39996 clearAutoHide : function(){
39997 if(this.autoHide !== false){
39998 this.el.un("mouseout", this.autoHideHd.mouseout);
39999 this.el.un("mouseover", this.autoHideHd.mouseover);
40003 clearMonitor : function(){
40004 Roo.get(document).un("click", this.slideInIf, this);
40007 // these names are backwards but not changed for compat
40008 slideOut : function(){
40009 if(this.isSlid || this.el.hasActiveFx()){
40012 this.isSlid = true;
40013 if(this.collapseBtn){
40014 this.collapseBtn.hide();
40016 this.closeBtnState = this.closeBtn.getStyle('display');
40017 this.closeBtn.hide();
40019 this.stickBtn.show();
40022 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
40023 this.beforeSlide();
40024 this.el.setStyle("z-index", 10001);
40025 this.el.slideIn(this.getSlideAnchor(), {
40026 callback: function(){
40028 this.initAutoHide();
40029 Roo.get(document).on("click", this.slideInIf, this);
40030 this.fireEvent("slideshow", this);
40037 afterSlideIn : function(){
40038 this.clearAutoHide();
40039 this.isSlid = false;
40040 this.clearMonitor();
40041 this.el.setStyle("z-index", "");
40042 if(this.collapseBtn){
40043 this.collapseBtn.show();
40045 this.closeBtn.setStyle('display', this.closeBtnState);
40047 this.stickBtn.hide();
40049 this.fireEvent("slidehide", this);
40052 slideIn : function(cb){
40053 if(!this.isSlid || this.el.hasActiveFx()){
40057 this.isSlid = false;
40058 this.beforeSlide();
40059 this.el.slideOut(this.getSlideAnchor(), {
40060 callback: function(){
40061 this.el.setLeftTop(-10000, -10000);
40063 this.afterSlideIn();
40071 slideInIf : function(e){
40072 if(!e.within(this.el)){
40077 animateCollapse : function(){
40078 this.beforeSlide();
40079 this.el.setStyle("z-index", 20000);
40080 var anchor = this.getSlideAnchor();
40081 this.el.slideOut(anchor, {
40082 callback : function(){
40083 this.el.setStyle("z-index", "");
40084 this.collapsedEl.slideIn(anchor, {duration:.3});
40086 this.el.setLocation(-10000,-10000);
40088 this.fireEvent("collapsed", this);
40095 animateExpand : function(){
40096 this.beforeSlide();
40097 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
40098 this.el.setStyle("z-index", 20000);
40099 this.collapsedEl.hide({
40102 this.el.slideIn(this.getSlideAnchor(), {
40103 callback : function(){
40104 this.el.setStyle("z-index", "");
40107 this.split.el.show();
40109 this.fireEvent("invalidated", this);
40110 this.fireEvent("expanded", this);
40138 getAnchor : function(){
40139 return this.anchors[this.position];
40142 getCollapseAnchor : function(){
40143 return this.canchors[this.position];
40146 getSlideAnchor : function(){
40147 return this.sanchors[this.position];
40150 getAlignAdj : function(){
40151 var cm = this.cmargins;
40152 switch(this.position){
40168 getExpandAdj : function(){
40169 var c = this.collapsedEl, cm = this.cmargins;
40170 switch(this.position){
40172 return [-(cm.right+c.getWidth()+cm.left), 0];
40175 return [cm.right+c.getWidth()+cm.left, 0];
40178 return [0, -(cm.top+cm.bottom+c.getHeight())];
40181 return [0, cm.top+cm.bottom+c.getHeight()];
40187 * Ext JS Library 1.1.1
40188 * Copyright(c) 2006-2007, Ext JS, LLC.
40190 * Originally Released Under LGPL - original licence link has changed is not relivant.
40193 * <script type="text/javascript">
40196 * These classes are private internal classes
40198 Roo.bootstrap.layout.Center = function(config){
40199 config.region = "center";
40200 Roo.bootstrap.layout.Region.call(this, config);
40201 this.visible = true;
40202 this.minWidth = config.minWidth || 20;
40203 this.minHeight = config.minHeight || 20;
40206 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
40208 // center panel can't be hidden
40212 // center panel can't be hidden
40215 getMinWidth: function(){
40216 return this.minWidth;
40219 getMinHeight: function(){
40220 return this.minHeight;
40234 Roo.bootstrap.layout.North = function(config)
40236 config.region = 'north';
40237 config.cursor = 'n-resize';
40239 Roo.bootstrap.layout.Split.call(this, config);
40243 this.split.placement = Roo.bootstrap.SplitBar.TOP;
40244 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40245 this.split.el.addClass("roo-layout-split-v");
40247 //var size = config.initialSize || config.height;
40248 //if(this.el && typeof size != "undefined"){
40249 // this.el.setHeight(size);
40252 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40254 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40257 onRender : function(ctr, pos)
40259 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40260 var size = this.config.initialSize || this.config.height;
40261 if(this.el && typeof size != "undefined"){
40262 this.el.setHeight(size);
40267 getBox : function(){
40268 if(this.collapsed){
40269 return this.collapsedEl.getBox();
40271 var box = this.el.getBox();
40273 box.height += this.split.el.getHeight();
40278 updateBox : function(box){
40279 if(this.split && !this.collapsed){
40280 box.height -= this.split.el.getHeight();
40281 this.split.el.setLeft(box.x);
40282 this.split.el.setTop(box.y+box.height);
40283 this.split.el.setWidth(box.width);
40285 if(this.collapsed){
40286 this.updateBody(box.width, null);
40288 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40296 Roo.bootstrap.layout.South = function(config){
40297 config.region = 'south';
40298 config.cursor = 's-resize';
40299 Roo.bootstrap.layout.Split.call(this, config);
40301 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40302 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40303 this.split.el.addClass("roo-layout-split-v");
40308 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40309 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40311 onRender : function(ctr, pos)
40313 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40314 var size = this.config.initialSize || this.config.height;
40315 if(this.el && typeof size != "undefined"){
40316 this.el.setHeight(size);
40321 getBox : function(){
40322 if(this.collapsed){
40323 return this.collapsedEl.getBox();
40325 var box = this.el.getBox();
40327 var sh = this.split.el.getHeight();
40334 updateBox : function(box){
40335 if(this.split && !this.collapsed){
40336 var sh = this.split.el.getHeight();
40339 this.split.el.setLeft(box.x);
40340 this.split.el.setTop(box.y-sh);
40341 this.split.el.setWidth(box.width);
40343 if(this.collapsed){
40344 this.updateBody(box.width, null);
40346 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40350 Roo.bootstrap.layout.East = function(config){
40351 config.region = "east";
40352 config.cursor = "e-resize";
40353 Roo.bootstrap.layout.Split.call(this, config);
40355 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40356 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40357 this.split.el.addClass("roo-layout-split-h");
40361 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40362 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40364 onRender : function(ctr, pos)
40366 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40367 var size = this.config.initialSize || this.config.width;
40368 if(this.el && typeof size != "undefined"){
40369 this.el.setWidth(size);
40374 getBox : function(){
40375 if(this.collapsed){
40376 return this.collapsedEl.getBox();
40378 var box = this.el.getBox();
40380 var sw = this.split.el.getWidth();
40387 updateBox : function(box){
40388 if(this.split && !this.collapsed){
40389 var sw = this.split.el.getWidth();
40391 this.split.el.setLeft(box.x);
40392 this.split.el.setTop(box.y);
40393 this.split.el.setHeight(box.height);
40396 if(this.collapsed){
40397 this.updateBody(null, box.height);
40399 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40403 Roo.bootstrap.layout.West = function(config){
40404 config.region = "west";
40405 config.cursor = "w-resize";
40407 Roo.bootstrap.layout.Split.call(this, config);
40409 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40410 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40411 this.split.el.addClass("roo-layout-split-h");
40415 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40416 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40418 onRender: function(ctr, pos)
40420 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40421 var size = this.config.initialSize || this.config.width;
40422 if(typeof size != "undefined"){
40423 this.el.setWidth(size);
40427 getBox : function(){
40428 if(this.collapsed){
40429 return this.collapsedEl.getBox();
40431 var box = this.el.getBox();
40432 if (box.width == 0) {
40433 box.width = this.config.width; // kludge?
40436 box.width += this.split.el.getWidth();
40441 updateBox : function(box){
40442 if(this.split && !this.collapsed){
40443 var sw = this.split.el.getWidth();
40445 this.split.el.setLeft(box.x+box.width);
40446 this.split.el.setTop(box.y);
40447 this.split.el.setHeight(box.height);
40449 if(this.collapsed){
40450 this.updateBody(null, box.height);
40452 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40454 });Roo.namespace("Roo.bootstrap.panel");/*
40456 * Ext JS Library 1.1.1
40457 * Copyright(c) 2006-2007, Ext JS, LLC.
40459 * Originally Released Under LGPL - original licence link has changed is not relivant.
40462 * <script type="text/javascript">
40465 * @class Roo.ContentPanel
40466 * @extends Roo.util.Observable
40467 * A basic ContentPanel element.
40468 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
40469 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
40470 * @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
40471 * @cfg {Boolean} closable True if the panel can be closed/removed
40472 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
40473 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40474 * @cfg {Toolbar} toolbar A toolbar for this panel
40475 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
40476 * @cfg {String} title The title for this panel
40477 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40478 * @cfg {String} url Calls {@link #setUrl} with this value
40479 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40480 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
40481 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
40482 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
40483 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
40484 * @cfg {Boolean} badges render the badges
40485 * @cfg {String} cls extra classes to use
40486 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40489 * Create a new ContentPanel.
40490 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40491 * @param {String/Object} config A string to set only the title or a config object
40492 * @param {String} content (optional) Set the HTML content for this panel
40493 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40495 Roo.bootstrap.panel.Content = function( config){
40497 this.tpl = config.tpl || false;
40499 var el = config.el;
40500 var content = config.content;
40502 if(config.autoCreate){ // xtype is available if this is called from factory
40505 this.el = Roo.get(el);
40506 if(!this.el && config && config.autoCreate){
40507 if(typeof config.autoCreate == "object"){
40508 if(!config.autoCreate.id){
40509 config.autoCreate.id = config.id||el;
40511 this.el = Roo.DomHelper.append(document.body,
40512 config.autoCreate, true);
40516 cls: (config.cls || '') +
40517 (config.background ? ' bg-' + config.background : '') +
40518 " roo-layout-inactive-content",
40521 if (config.iframe) {
40525 style : 'border: 0px',
40526 src : 'about:blank'
40532 elcfg.html = config.html;
40536 this.el = Roo.DomHelper.append(document.body, elcfg , true);
40537 if (config.iframe) {
40538 this.iframeEl = this.el.select('iframe',true).first();
40543 this.closable = false;
40544 this.loaded = false;
40545 this.active = false;
40548 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40550 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40552 this.wrapEl = this.el; //this.el.wrap();
40554 if (config.toolbar.items) {
40555 ti = config.toolbar.items ;
40556 delete config.toolbar.items ;
40560 this.toolbar.render(this.wrapEl, 'before');
40561 for(var i =0;i < ti.length;i++) {
40562 // Roo.log(['add child', items[i]]);
40563 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40565 this.toolbar.items = nitems;
40566 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40567 delete config.toolbar;
40571 // xtype created footer. - not sure if will work as we normally have to render first..
40572 if (this.footer && !this.footer.el && this.footer.xtype) {
40573 if (!this.wrapEl) {
40574 this.wrapEl = this.el.wrap();
40577 this.footer.container = this.wrapEl.createChild();
40579 this.footer = Roo.factory(this.footer, Roo);
40584 if(typeof config == "string"){
40585 this.title = config;
40587 Roo.apply(this, config);
40591 this.resizeEl = Roo.get(this.resizeEl, true);
40593 this.resizeEl = this.el;
40595 // handle view.xtype
40603 * Fires when this panel is activated.
40604 * @param {Roo.ContentPanel} this
40608 * @event deactivate
40609 * Fires when this panel is activated.
40610 * @param {Roo.ContentPanel} this
40612 "deactivate" : true,
40616 * Fires when this panel is resized if fitToFrame is true.
40617 * @param {Roo.ContentPanel} this
40618 * @param {Number} width The width after any component adjustments
40619 * @param {Number} height The height after any component adjustments
40625 * Fires when this tab is created
40626 * @param {Roo.ContentPanel} this
40632 * Fires when this content is scrolled
40633 * @param {Roo.ContentPanel} this
40634 * @param {Event} scrollEvent
40645 if(this.autoScroll && !this.iframe){
40646 this.resizeEl.setStyle("overflow", "auto");
40647 this.resizeEl.on('scroll', this.onScroll, this);
40649 // fix randome scrolling
40650 //this.el.on('scroll', function() {
40651 // Roo.log('fix random scolling');
40652 // this.scrollTo('top',0);
40655 content = content || this.content;
40657 this.setContent(content);
40659 if(config && config.url){
40660 this.setUrl(this.url, this.params, this.loadOnce);
40665 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40667 if (this.view && typeof(this.view.xtype) != 'undefined') {
40668 this.view.el = this.el.appendChild(document.createElement("div"));
40669 this.view = Roo.factory(this.view);
40670 this.view.render && this.view.render(false, '');
40674 this.fireEvent('render', this);
40677 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40687 /* Resize Element - use this to work out scroll etc. */
40690 setRegion : function(region){
40691 this.region = region;
40692 this.setActiveClass(region && !this.background);
40696 setActiveClass: function(state)
40699 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40700 this.el.setStyle('position','relative');
40702 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40703 this.el.setStyle('position', 'absolute');
40708 * Returns the toolbar for this Panel if one was configured.
40709 * @return {Roo.Toolbar}
40711 getToolbar : function(){
40712 return this.toolbar;
40715 setActiveState : function(active)
40717 this.active = active;
40718 this.setActiveClass(active);
40720 if(this.fireEvent("deactivate", this) === false){
40725 this.fireEvent("activate", this);
40729 * Updates this panel's element (not for iframe)
40730 * @param {String} content The new content
40731 * @param {Boolean} loadScripts (optional) true to look for and process scripts
40733 setContent : function(content, loadScripts){
40738 this.el.update(content, loadScripts);
40741 ignoreResize : function(w, h){
40742 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40745 this.lastSize = {width: w, height: h};
40750 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40751 * @return {Roo.UpdateManager} The UpdateManager
40753 getUpdateManager : function(){
40757 return this.el.getUpdateManager();
40760 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40761 * Does not work with IFRAME contents
40762 * @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:
40765 url: "your-url.php",
40766 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40767 callback: yourFunction,
40768 scope: yourObject, //(optional scope)
40771 text: "Loading...",
40777 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40778 * 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.
40779 * @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}
40780 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40781 * @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.
40782 * @return {Roo.ContentPanel} this
40790 var um = this.el.getUpdateManager();
40791 um.update.apply(um, arguments);
40797 * 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.
40798 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40799 * @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)
40800 * @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)
40801 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40803 setUrl : function(url, params, loadOnce){
40805 this.iframeEl.dom.src = url;
40809 if(this.refreshDelegate){
40810 this.removeListener("activate", this.refreshDelegate);
40812 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40813 this.on("activate", this.refreshDelegate);
40814 return this.el.getUpdateManager();
40817 _handleRefresh : function(url, params, loadOnce){
40818 if(!loadOnce || !this.loaded){
40819 var updater = this.el.getUpdateManager();
40820 updater.update(url, params, this._setLoaded.createDelegate(this));
40824 _setLoaded : function(){
40825 this.loaded = true;
40829 * Returns this panel's id
40832 getId : function(){
40837 * Returns this panel's element - used by regiosn to add.
40838 * @return {Roo.Element}
40840 getEl : function(){
40841 return this.wrapEl || this.el;
40846 adjustForComponents : function(width, height)
40848 //Roo.log('adjustForComponents ');
40849 if(this.resizeEl != this.el){
40850 width -= this.el.getFrameWidth('lr');
40851 height -= this.el.getFrameWidth('tb');
40854 var te = this.toolbar.getEl();
40855 te.setWidth(width);
40856 height -= te.getHeight();
40859 var te = this.footer.getEl();
40860 te.setWidth(width);
40861 height -= te.getHeight();
40865 if(this.adjustments){
40866 width += this.adjustments[0];
40867 height += this.adjustments[1];
40869 return {"width": width, "height": height};
40872 setSize : function(width, height){
40873 if(this.fitToFrame && !this.ignoreResize(width, height)){
40874 if(this.fitContainer && this.resizeEl != this.el){
40875 this.el.setSize(width, height);
40877 var size = this.adjustForComponents(width, height);
40879 this.iframeEl.setSize(width,height);
40882 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40883 this.fireEvent('resize', this, size.width, size.height);
40890 * Returns this panel's title
40893 getTitle : function(){
40895 if (typeof(this.title) != 'object') {
40900 for (var k in this.title) {
40901 if (!this.title.hasOwnProperty(k)) {
40905 if (k.indexOf('-') >= 0) {
40906 var s = k.split('-');
40907 for (var i = 0; i<s.length; i++) {
40908 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40911 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40918 * Set this panel's title
40919 * @param {String} title
40921 setTitle : function(title){
40922 this.title = title;
40924 this.region.updatePanelTitle(this, title);
40929 * Returns true is this panel was configured to be closable
40930 * @return {Boolean}
40932 isClosable : function(){
40933 return this.closable;
40936 beforeSlide : function(){
40938 this.resizeEl.clip();
40941 afterSlide : function(){
40943 this.resizeEl.unclip();
40947 * Force a content refresh from the URL specified in the {@link #setUrl} method.
40948 * Will fail silently if the {@link #setUrl} method has not been called.
40949 * This does not activate the panel, just updates its content.
40951 refresh : function(){
40952 if(this.refreshDelegate){
40953 this.loaded = false;
40954 this.refreshDelegate();
40959 * Destroys this panel
40961 destroy : function(){
40962 this.el.removeAllListeners();
40963 var tempEl = document.createElement("span");
40964 tempEl.appendChild(this.el.dom);
40965 tempEl.innerHTML = "";
40971 * form - if the content panel contains a form - this is a reference to it.
40972 * @type {Roo.form.Form}
40976 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40977 * This contains a reference to it.
40983 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40993 * @param {Object} cfg Xtype definition of item to add.
40997 getChildContainer: function () {
40998 return this.getEl();
41002 onScroll : function(e)
41004 this.fireEvent('scroll', this, e);
41009 var ret = new Roo.factory(cfg);
41014 if (cfg.xtype.match(/^Form$/)) {
41017 //if (this.footer) {
41018 // el = this.footer.container.insertSibling(false, 'before');
41020 el = this.el.createChild();
41023 this.form = new Roo.form.Form(cfg);
41026 if ( this.form.allItems.length) {
41027 this.form.render(el.dom);
41031 // should only have one of theses..
41032 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
41033 // views.. should not be just added - used named prop 'view''
41035 cfg.el = this.el.appendChild(document.createElement("div"));
41038 var ret = new Roo.factory(cfg);
41040 ret.render && ret.render(false, ''); // render blank..
41050 * @class Roo.bootstrap.panel.Grid
41051 * @extends Roo.bootstrap.panel.Content
41053 * Create a new GridPanel.
41054 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
41055 * @param {Object} config A the config object
41061 Roo.bootstrap.panel.Grid = function(config)
41065 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
41066 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
41068 config.el = this.wrapper;
41069 //this.el = this.wrapper;
41071 if (config.container) {
41072 // ctor'ed from a Border/panel.grid
41075 this.wrapper.setStyle("overflow", "hidden");
41076 this.wrapper.addClass('roo-grid-container');
41081 if(config.toolbar){
41082 var tool_el = this.wrapper.createChild();
41083 this.toolbar = Roo.factory(config.toolbar);
41085 if (config.toolbar.items) {
41086 ti = config.toolbar.items ;
41087 delete config.toolbar.items ;
41091 this.toolbar.render(tool_el);
41092 for(var i =0;i < ti.length;i++) {
41093 // Roo.log(['add child', items[i]]);
41094 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
41096 this.toolbar.items = nitems;
41098 delete config.toolbar;
41101 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
41102 config.grid.scrollBody = true;;
41103 config.grid.monitorWindowResize = false; // turn off autosizing
41104 config.grid.autoHeight = false;
41105 config.grid.autoWidth = false;
41107 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
41109 if (config.background) {
41110 // render grid on panel activation (if panel background)
41111 this.on('activate', function(gp) {
41112 if (!gp.grid.rendered) {
41113 gp.grid.render(this.wrapper);
41114 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
41119 this.grid.render(this.wrapper);
41120 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
41123 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
41124 // ??? needed ??? config.el = this.wrapper;
41129 // xtype created footer. - not sure if will work as we normally have to render first..
41130 if (this.footer && !this.footer.el && this.footer.xtype) {
41132 var ctr = this.grid.getView().getFooterPanel(true);
41133 this.footer.dataSource = this.grid.dataSource;
41134 this.footer = Roo.factory(this.footer, Roo);
41135 this.footer.render(ctr);
41145 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
41146 getId : function(){
41147 return this.grid.id;
41151 * Returns the grid for this panel
41152 * @return {Roo.bootstrap.Table}
41154 getGrid : function(){
41158 setSize : function(width, height){
41159 if(!this.ignoreResize(width, height)){
41160 var grid = this.grid;
41161 var size = this.adjustForComponents(width, height);
41162 // tfoot is not a footer?
41165 var gridel = grid.getGridEl();
41166 gridel.setSize(size.width, size.height);
41168 var tbd = grid.getGridEl().select('tbody', true).first();
41169 var thd = grid.getGridEl().select('thead',true).first();
41170 var tbf= grid.getGridEl().select('tfoot', true).first();
41173 size.height -= tbf.getHeight();
41176 size.height -= thd.getHeight();
41179 tbd.setSize(size.width, size.height );
41180 // this is for the account management tab -seems to work there.
41181 var thd = grid.getGridEl().select('thead',true).first();
41183 // tbd.setSize(size.width, size.height - thd.getHeight());
41192 beforeSlide : function(){
41193 this.grid.getView().scroller.clip();
41196 afterSlide : function(){
41197 this.grid.getView().scroller.unclip();
41200 destroy : function(){
41201 this.grid.destroy();
41203 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
41208 * @class Roo.bootstrap.panel.Nest
41209 * @extends Roo.bootstrap.panel.Content
41211 * Create a new Panel, that can contain a layout.Border.
41214 * @param {Roo.BorderLayout} layout The layout for this panel
41215 * @param {String/Object} config A string to set only the title or a config object
41217 Roo.bootstrap.panel.Nest = function(config)
41219 // construct with only one argument..
41220 /* FIXME - implement nicer consturctors
41221 if (layout.layout) {
41223 layout = config.layout;
41224 delete config.layout;
41226 if (layout.xtype && !layout.getEl) {
41227 // then layout needs constructing..
41228 layout = Roo.factory(layout, Roo);
41232 config.el = config.layout.getEl();
41234 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
41236 config.layout.monitorWindowResize = false; // turn off autosizing
41237 this.layout = config.layout;
41238 this.layout.getEl().addClass("roo-layout-nested-layout");
41239 this.layout.parent = this;
41246 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41248 setSize : function(width, height){
41249 if(!this.ignoreResize(width, height)){
41250 var size = this.adjustForComponents(width, height);
41251 var el = this.layout.getEl();
41252 if (size.height < 1) {
41253 el.setWidth(size.width);
41255 el.setSize(size.width, size.height);
41257 var touch = el.dom.offsetWidth;
41258 this.layout.layout();
41259 // ie requires a double layout on the first pass
41260 if(Roo.isIE && !this.initialized){
41261 this.initialized = true;
41262 this.layout.layout();
41267 // activate all subpanels if not currently active..
41269 setActiveState : function(active){
41270 this.active = active;
41271 this.setActiveClass(active);
41274 this.fireEvent("deactivate", this);
41278 this.fireEvent("activate", this);
41279 // not sure if this should happen before or after..
41280 if (!this.layout) {
41281 return; // should not happen..
41284 for (var r in this.layout.regions) {
41285 reg = this.layout.getRegion(r);
41286 if (reg.getActivePanel()) {
41287 //reg.showPanel(reg.getActivePanel()); // force it to activate..
41288 reg.setActivePanel(reg.getActivePanel());
41291 if (!reg.panels.length) {
41294 reg.showPanel(reg.getPanel(0));
41303 * Returns the nested BorderLayout for this panel
41304 * @return {Roo.BorderLayout}
41306 getLayout : function(){
41307 return this.layout;
41311 * Adds a xtype elements to the layout of the nested panel
41315 xtype : 'ContentPanel',
41322 xtype : 'NestedLayoutPanel',
41328 items : [ ... list of content panels or nested layout panels.. ]
41332 * @param {Object} cfg Xtype definition of item to add.
41334 addxtype : function(cfg) {
41335 return this.layout.addxtype(cfg);
41340 * Ext JS Library 1.1.1
41341 * Copyright(c) 2006-2007, Ext JS, LLC.
41343 * Originally Released Under LGPL - original licence link has changed is not relivant.
41346 * <script type="text/javascript">
41349 * @class Roo.TabPanel
41350 * @extends Roo.util.Observable
41351 * A lightweight tab container.
41355 // basic tabs 1, built from existing content
41356 var tabs = new Roo.TabPanel("tabs1");
41357 tabs.addTab("script", "View Script");
41358 tabs.addTab("markup", "View Markup");
41359 tabs.activate("script");
41361 // more advanced tabs, built from javascript
41362 var jtabs = new Roo.TabPanel("jtabs");
41363 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41365 // set up the UpdateManager
41366 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41367 var updater = tab2.getUpdateManager();
41368 updater.setDefaultUrl("ajax1.htm");
41369 tab2.on('activate', updater.refresh, updater, true);
41371 // Use setUrl for Ajax loading
41372 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41373 tab3.setUrl("ajax2.htm", null, true);
41376 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41379 jtabs.activate("jtabs-1");
41382 * Create a new TabPanel.
41383 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41384 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41386 Roo.bootstrap.panel.Tabs = function(config){
41388 * The container element for this TabPanel.
41389 * @type Roo.Element
41391 this.el = Roo.get(config.el);
41394 if(typeof config == "boolean"){
41395 this.tabPosition = config ? "bottom" : "top";
41397 Roo.apply(this, config);
41401 if(this.tabPosition == "bottom"){
41402 // if tabs are at the bottom = create the body first.
41403 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41404 this.el.addClass("roo-tabs-bottom");
41406 // next create the tabs holders
41408 if (this.tabPosition == "west"){
41410 var reg = this.region; // fake it..
41412 if (!reg.mgr.parent) {
41415 reg = reg.mgr.parent.region;
41417 Roo.log("got nest?");
41419 if (reg.mgr.getRegion('west')) {
41420 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41421 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41422 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41423 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41424 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41432 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41433 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41434 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41435 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41440 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41443 // finally - if tabs are at the top, then create the body last..
41444 if(this.tabPosition != "bottom"){
41445 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41446 * @type Roo.Element
41448 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41449 this.el.addClass("roo-tabs-top");
41453 this.bodyEl.setStyle("position", "relative");
41455 this.active = null;
41456 this.activateDelegate = this.activate.createDelegate(this);
41461 * Fires when the active tab changes
41462 * @param {Roo.TabPanel} this
41463 * @param {Roo.TabPanelItem} activePanel The new active tab
41467 * @event beforetabchange
41468 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41469 * @param {Roo.TabPanel} this
41470 * @param {Object} e Set cancel to true on this object to cancel the tab change
41471 * @param {Roo.TabPanelItem} tab The tab being changed to
41473 "beforetabchange" : true
41476 Roo.EventManager.onWindowResize(this.onResize, this);
41477 this.cpad = this.el.getPadding("lr");
41478 this.hiddenCount = 0;
41481 // toolbar on the tabbar support...
41482 if (this.toolbar) {
41483 alert("no toolbar support yet");
41484 this.toolbar = false;
41486 var tcfg = this.toolbar;
41487 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
41488 this.toolbar = new Roo.Toolbar(tcfg);
41489 if (Roo.isSafari) {
41490 var tbl = tcfg.container.child('table', true);
41491 tbl.setAttribute('width', '100%');
41499 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41502 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41504 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41506 tabPosition : "top",
41508 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41510 currentTabWidth : 0,
41512 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41516 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41520 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41522 preferredTabWidth : 175,
41524 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41526 resizeTabs : false,
41528 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41530 monitorResize : true,
41532 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
41534 toolbar : false, // set by caller..
41536 region : false, /// set by caller
41538 disableTooltips : true, // not used yet...
41541 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41542 * @param {String} id The id of the div to use <b>or create</b>
41543 * @param {String} text The text for the tab
41544 * @param {String} content (optional) Content to put in the TabPanelItem body
41545 * @param {Boolean} closable (optional) True to create a close icon on the tab
41546 * @return {Roo.TabPanelItem} The created TabPanelItem
41548 addTab : function(id, text, content, closable, tpl)
41550 var item = new Roo.bootstrap.panel.TabItem({
41554 closable : closable,
41557 this.addTabItem(item);
41559 item.setContent(content);
41565 * Returns the {@link Roo.TabPanelItem} with the specified id/index
41566 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41567 * @return {Roo.TabPanelItem}
41569 getTab : function(id){
41570 return this.items[id];
41574 * Hides the {@link Roo.TabPanelItem} with the specified id/index
41575 * @param {String/Number} id The id or index of the TabPanelItem to hide.
41577 hideTab : function(id){
41578 var t = this.items[id];
41581 this.hiddenCount++;
41582 this.autoSizeTabs();
41587 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41588 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41590 unhideTab : function(id){
41591 var t = this.items[id];
41593 t.setHidden(false);
41594 this.hiddenCount--;
41595 this.autoSizeTabs();
41600 * Adds an existing {@link Roo.TabPanelItem}.
41601 * @param {Roo.TabPanelItem} item The TabPanelItem to add
41603 addTabItem : function(item)
41605 this.items[item.id] = item;
41606 this.items.push(item);
41607 this.autoSizeTabs();
41608 // if(this.resizeTabs){
41609 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41610 // this.autoSizeTabs();
41612 // item.autoSize();
41617 * Removes a {@link Roo.TabPanelItem}.
41618 * @param {String/Number} id The id or index of the TabPanelItem to remove.
41620 removeTab : function(id){
41621 var items = this.items;
41622 var tab = items[id];
41623 if(!tab) { return; }
41624 var index = items.indexOf(tab);
41625 if(this.active == tab && items.length > 1){
41626 var newTab = this.getNextAvailable(index);
41631 this.stripEl.dom.removeChild(tab.pnode.dom);
41632 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41633 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41635 items.splice(index, 1);
41636 delete this.items[tab.id];
41637 tab.fireEvent("close", tab);
41638 tab.purgeListeners();
41639 this.autoSizeTabs();
41642 getNextAvailable : function(start){
41643 var items = this.items;
41645 // look for a next tab that will slide over to
41646 // replace the one being removed
41647 while(index < items.length){
41648 var item = items[++index];
41649 if(item && !item.isHidden()){
41653 // if one isn't found select the previous tab (on the left)
41656 var item = items[--index];
41657 if(item && !item.isHidden()){
41665 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41666 * @param {String/Number} id The id or index of the TabPanelItem to disable.
41668 disableTab : function(id){
41669 var tab = this.items[id];
41670 if(tab && this.active != tab){
41676 * Enables a {@link Roo.TabPanelItem} that is disabled.
41677 * @param {String/Number} id The id or index of the TabPanelItem to enable.
41679 enableTab : function(id){
41680 var tab = this.items[id];
41685 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41686 * @param {String/Number} id The id or index of the TabPanelItem to activate.
41687 * @return {Roo.TabPanelItem} The TabPanelItem.
41689 activate : function(id)
41691 //Roo.log('activite:' + id);
41693 var tab = this.items[id];
41697 if(tab == this.active || tab.disabled){
41701 this.fireEvent("beforetabchange", this, e, tab);
41702 if(e.cancel !== true && !tab.disabled){
41704 this.active.hide();
41706 this.active = this.items[id];
41707 this.active.show();
41708 this.fireEvent("tabchange", this, this.active);
41714 * Gets the active {@link Roo.TabPanelItem}.
41715 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41717 getActiveTab : function(){
41718 return this.active;
41722 * Updates the tab body element to fit the height of the container element
41723 * for overflow scrolling
41724 * @param {Number} targetHeight (optional) Override the starting height from the elements height
41726 syncHeight : function(targetHeight){
41727 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41728 var bm = this.bodyEl.getMargins();
41729 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41730 this.bodyEl.setHeight(newHeight);
41734 onResize : function(){
41735 if(this.monitorResize){
41736 this.autoSizeTabs();
41741 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41743 beginUpdate : function(){
41744 this.updating = true;
41748 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41750 endUpdate : function(){
41751 this.updating = false;
41752 this.autoSizeTabs();
41756 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41758 autoSizeTabs : function()
41760 var count = this.items.length;
41761 var vcount = count - this.hiddenCount;
41764 this.stripEl.hide();
41766 this.stripEl.show();
41769 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41774 var w = Math.max(this.el.getWidth() - this.cpad, 10);
41775 var availWidth = Math.floor(w / vcount);
41776 var b = this.stripBody;
41777 if(b.getWidth() > w){
41778 var tabs = this.items;
41779 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41780 if(availWidth < this.minTabWidth){
41781 /*if(!this.sleft){ // incomplete scrolling code
41782 this.createScrollButtons();
41785 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41788 if(this.currentTabWidth < this.preferredTabWidth){
41789 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41795 * Returns the number of tabs in this TabPanel.
41798 getCount : function(){
41799 return this.items.length;
41803 * Resizes all the tabs to the passed width
41804 * @param {Number} The new width
41806 setTabWidth : function(width){
41807 this.currentTabWidth = width;
41808 for(var i = 0, len = this.items.length; i < len; i++) {
41809 if(!this.items[i].isHidden()) {
41810 this.items[i].setWidth(width);
41816 * Destroys this TabPanel
41817 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41819 destroy : function(removeEl){
41820 Roo.EventManager.removeResizeListener(this.onResize, this);
41821 for(var i = 0, len = this.items.length; i < len; i++){
41822 this.items[i].purgeListeners();
41824 if(removeEl === true){
41825 this.el.update("");
41830 createStrip : function(container)
41832 var strip = document.createElement("nav");
41833 strip.className = Roo.bootstrap.version == 4 ?
41834 "navbar-light bg-light" :
41835 "navbar navbar-default"; //"x-tabs-wrap";
41836 container.appendChild(strip);
41840 createStripList : function(strip)
41842 // div wrapper for retard IE
41843 // returns the "tr" element.
41844 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41845 //'<div class="x-tabs-strip-wrap">'+
41846 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41847 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41848 return strip.firstChild; //.firstChild.firstChild.firstChild;
41850 createBody : function(container)
41852 var body = document.createElement("div");
41853 Roo.id(body, "tab-body");
41854 //Roo.fly(body).addClass("x-tabs-body");
41855 Roo.fly(body).addClass("tab-content");
41856 container.appendChild(body);
41859 createItemBody :function(bodyEl, id){
41860 var body = Roo.getDom(id);
41862 body = document.createElement("div");
41865 //Roo.fly(body).addClass("x-tabs-item-body");
41866 Roo.fly(body).addClass("tab-pane");
41867 bodyEl.insertBefore(body, bodyEl.firstChild);
41871 createStripElements : function(stripEl, text, closable, tpl)
41873 var td = document.createElement("li"); // was td..
41874 td.className = 'nav-item';
41876 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41879 stripEl.appendChild(td);
41881 td.className = "x-tabs-closable";
41882 if(!this.closeTpl){
41883 this.closeTpl = new Roo.Template(
41884 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41885 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41886 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41889 var el = this.closeTpl.overwrite(td, {"text": text});
41890 var close = el.getElementsByTagName("div")[0];
41891 var inner = el.getElementsByTagName("em")[0];
41892 return {"el": el, "close": close, "inner": inner};
41895 // not sure what this is..
41896 // if(!this.tabTpl){
41897 //this.tabTpl = new Roo.Template(
41898 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41899 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41901 // this.tabTpl = new Roo.Template(
41902 // '<a href="#">' +
41903 // '<span unselectable="on"' +
41904 // (this.disableTooltips ? '' : ' title="{text}"') +
41905 // ' >{text}</span></a>'
41911 var template = tpl || this.tabTpl || false;
41914 template = new Roo.Template(
41915 Roo.bootstrap.version == 4 ?
41917 '<a class="nav-link" href="#" unselectable="on"' +
41918 (this.disableTooltips ? '' : ' title="{text}"') +
41921 '<a class="nav-link" href="#">' +
41922 '<span unselectable="on"' +
41923 (this.disableTooltips ? '' : ' title="{text}"') +
41924 ' >{text}</span></a>'
41929 switch (typeof(template)) {
41933 template = new Roo.Template(template);
41939 var el = template.overwrite(td, {"text": text});
41941 var inner = el.getElementsByTagName("span")[0];
41943 return {"el": el, "inner": inner};
41951 * @class Roo.TabPanelItem
41952 * @extends Roo.util.Observable
41953 * Represents an individual item (tab plus body) in a TabPanel.
41954 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41955 * @param {String} id The id of this TabPanelItem
41956 * @param {String} text The text for the tab of this TabPanelItem
41957 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41959 Roo.bootstrap.panel.TabItem = function(config){
41961 * The {@link Roo.TabPanel} this TabPanelItem belongs to
41962 * @type Roo.TabPanel
41964 this.tabPanel = config.panel;
41966 * The id for this TabPanelItem
41969 this.id = config.id;
41971 this.disabled = false;
41973 this.text = config.text;
41975 this.loaded = false;
41976 this.closable = config.closable;
41979 * The body element for this TabPanelItem.
41980 * @type Roo.Element
41982 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41983 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41984 this.bodyEl.setStyle("display", "block");
41985 this.bodyEl.setStyle("zoom", "1");
41986 //this.hideAction();
41988 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41990 this.el = Roo.get(els.el);
41991 this.inner = Roo.get(els.inner, true);
41992 this.textEl = Roo.bootstrap.version == 4 ?
41993 this.el : Roo.get(this.el.dom.firstChild, true);
41995 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41996 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41999 // this.el.on("mousedown", this.onTabMouseDown, this);
42000 this.el.on("click", this.onTabClick, this);
42002 if(config.closable){
42003 var c = Roo.get(els.close, true);
42004 c.dom.title = this.closeText;
42005 c.addClassOnOver("close-over");
42006 c.on("click", this.closeClick, this);
42012 * Fires when this tab becomes the active tab.
42013 * @param {Roo.TabPanel} tabPanel The parent TabPanel
42014 * @param {Roo.TabPanelItem} this
42018 * @event beforeclose
42019 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
42020 * @param {Roo.TabPanelItem} this
42021 * @param {Object} e Set cancel to true on this object to cancel the close.
42023 "beforeclose": true,
42026 * Fires when this tab is closed.
42027 * @param {Roo.TabPanelItem} this
42031 * @event deactivate
42032 * Fires when this tab is no longer the active tab.
42033 * @param {Roo.TabPanel} tabPanel The parent TabPanel
42034 * @param {Roo.TabPanelItem} this
42036 "deactivate" : true
42038 this.hidden = false;
42040 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
42043 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
42045 purgeListeners : function(){
42046 Roo.util.Observable.prototype.purgeListeners.call(this);
42047 this.el.removeAllListeners();
42050 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
42053 this.status_node.addClass("active");
42056 this.tabPanel.stripWrap.repaint();
42058 this.fireEvent("activate", this.tabPanel, this);
42062 * Returns true if this tab is the active tab.
42063 * @return {Boolean}
42065 isActive : function(){
42066 return this.tabPanel.getActiveTab() == this;
42070 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
42073 this.status_node.removeClass("active");
42075 this.fireEvent("deactivate", this.tabPanel, this);
42078 hideAction : function(){
42079 this.bodyEl.hide();
42080 this.bodyEl.setStyle("position", "absolute");
42081 this.bodyEl.setLeft("-20000px");
42082 this.bodyEl.setTop("-20000px");
42085 showAction : function(){
42086 this.bodyEl.setStyle("position", "relative");
42087 this.bodyEl.setTop("");
42088 this.bodyEl.setLeft("");
42089 this.bodyEl.show();
42093 * Set the tooltip for the tab.
42094 * @param {String} tooltip The tab's tooltip
42096 setTooltip : function(text){
42097 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
42098 this.textEl.dom.qtip = text;
42099 this.textEl.dom.removeAttribute('title');
42101 this.textEl.dom.title = text;
42105 onTabClick : function(e){
42106 e.preventDefault();
42107 this.tabPanel.activate(this.id);
42110 onTabMouseDown : function(e){
42111 e.preventDefault();
42112 this.tabPanel.activate(this.id);
42115 getWidth : function(){
42116 return this.inner.getWidth();
42119 setWidth : function(width){
42120 var iwidth = width - this.linode.getPadding("lr");
42121 this.inner.setWidth(iwidth);
42122 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
42123 this.linode.setWidth(width);
42127 * Show or hide the tab
42128 * @param {Boolean} hidden True to hide or false to show.
42130 setHidden : function(hidden){
42131 this.hidden = hidden;
42132 this.linode.setStyle("display", hidden ? "none" : "");
42136 * Returns true if this tab is "hidden"
42137 * @return {Boolean}
42139 isHidden : function(){
42140 return this.hidden;
42144 * Returns the text for this tab
42147 getText : function(){
42151 autoSize : function(){
42152 //this.el.beginMeasure();
42153 this.textEl.setWidth(1);
42155 * #2804 [new] Tabs in Roojs
42156 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
42158 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
42159 //this.el.endMeasure();
42163 * Sets the text for the tab (Note: this also sets the tooltip text)
42164 * @param {String} text The tab's text and tooltip
42166 setText : function(text){
42168 this.textEl.update(text);
42169 this.setTooltip(text);
42170 //if(!this.tabPanel.resizeTabs){
42171 // this.autoSize();
42175 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
42177 activate : function(){
42178 this.tabPanel.activate(this.id);
42182 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
42184 disable : function(){
42185 if(this.tabPanel.active != this){
42186 this.disabled = true;
42187 this.status_node.addClass("disabled");
42192 * Enables this TabPanelItem if it was previously disabled.
42194 enable : function(){
42195 this.disabled = false;
42196 this.status_node.removeClass("disabled");
42200 * Sets the content for this TabPanelItem.
42201 * @param {String} content The content
42202 * @param {Boolean} loadScripts true to look for and load scripts
42204 setContent : function(content, loadScripts){
42205 this.bodyEl.update(content, loadScripts);
42209 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
42210 * @return {Roo.UpdateManager} The UpdateManager
42212 getUpdateManager : function(){
42213 return this.bodyEl.getUpdateManager();
42217 * Set a URL to be used to load the content for this TabPanelItem.
42218 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
42219 * @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)
42220 * @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)
42221 * @return {Roo.UpdateManager} The UpdateManager
42223 setUrl : function(url, params, loadOnce){
42224 if(this.refreshDelegate){
42225 this.un('activate', this.refreshDelegate);
42227 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
42228 this.on("activate", this.refreshDelegate);
42229 return this.bodyEl.getUpdateManager();
42233 _handleRefresh : function(url, params, loadOnce){
42234 if(!loadOnce || !this.loaded){
42235 var updater = this.bodyEl.getUpdateManager();
42236 updater.update(url, params, this._setLoaded.createDelegate(this));
42241 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
42242 * Will fail silently if the setUrl method has not been called.
42243 * This does not activate the panel, just updates its content.
42245 refresh : function(){
42246 if(this.refreshDelegate){
42247 this.loaded = false;
42248 this.refreshDelegate();
42253 _setLoaded : function(){
42254 this.loaded = true;
42258 closeClick : function(e){
42261 this.fireEvent("beforeclose", this, o);
42262 if(o.cancel !== true){
42263 this.tabPanel.removeTab(this.id);
42267 * The text displayed in the tooltip for the close icon.
42270 closeText : "Close this tab"
42273 * This script refer to:
42274 * Title: International Telephone Input
42275 * Author: Jack O'Connor
42276 * Code version: v12.1.12
42277 * Availability: https://github.com/jackocnr/intl-tel-input.git
42280 Roo.bootstrap.PhoneInputData = function() {
42283 "Afghanistan (افغانستان)",
42288 "Albania (Shqipëri)",
42293 "Algeria (الجزائر)",
42318 "Antigua and Barbuda",
42328 "Armenia (Հայաստան)",
42344 "Austria (Österreich)",
42349 "Azerbaijan (Azərbaycan)",
42359 "Bahrain (البحرين)",
42364 "Bangladesh (বাংলাদেশ)",
42374 "Belarus (Беларусь)",
42379 "Belgium (België)",
42409 "Bosnia and Herzegovina (Босна и Херцеговина)",
42424 "British Indian Ocean Territory",
42429 "British Virgin Islands",
42439 "Bulgaria (България)",
42449 "Burundi (Uburundi)",
42454 "Cambodia (កម្ពុជា)",
42459 "Cameroon (Cameroun)",
42468 ["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"]
42471 "Cape Verde (Kabu Verdi)",
42476 "Caribbean Netherlands",
42487 "Central African Republic (République centrafricaine)",
42507 "Christmas Island",
42513 "Cocos (Keeling) Islands",
42524 "Comoros (جزر القمر)",
42529 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42534 "Congo (Republic) (Congo-Brazzaville)",
42554 "Croatia (Hrvatska)",
42575 "Czech Republic (Česká republika)",
42580 "Denmark (Danmark)",
42595 "Dominican Republic (República Dominicana)",
42599 ["809", "829", "849"]
42617 "Equatorial Guinea (Guinea Ecuatorial)",
42637 "Falkland Islands (Islas Malvinas)",
42642 "Faroe Islands (Føroyar)",
42663 "French Guiana (Guyane française)",
42668 "French Polynesia (Polynésie française)",
42683 "Georgia (საქართველო)",
42688 "Germany (Deutschland)",
42708 "Greenland (Kalaallit Nunaat)",
42745 "Guinea-Bissau (Guiné Bissau)",
42770 "Hungary (Magyarország)",
42775 "Iceland (Ísland)",
42795 "Iraq (العراق)",
42811 "Israel (ישראל)",
42838 "Jordan (الأردن)",
42843 "Kazakhstan (Казахстан)",
42864 "Kuwait (الكويت)",
42869 "Kyrgyzstan (Кыргызстан)",
42879 "Latvia (Latvija)",
42884 "Lebanon (لبنان)",
42899 "Libya (ليبيا)",
42909 "Lithuania (Lietuva)",
42924 "Macedonia (FYROM) (Македонија)",
42929 "Madagascar (Madagasikara)",
42959 "Marshall Islands",
42969 "Mauritania (موريتانيا)",
42974 "Mauritius (Moris)",
42995 "Moldova (Republica Moldova)",
43005 "Mongolia (Монгол)",
43010 "Montenegro (Crna Gora)",
43020 "Morocco (المغرب)",
43026 "Mozambique (Moçambique)",
43031 "Myanmar (Burma) (မြန်မာ)",
43036 "Namibia (Namibië)",
43051 "Netherlands (Nederland)",
43056 "New Caledonia (Nouvelle-Calédonie)",
43091 "North Korea (조선 민주주의 인민 공화국)",
43096 "Northern Mariana Islands",
43112 "Pakistan (پاکستان)",
43122 "Palestine (فلسطين)",
43132 "Papua New Guinea",
43174 "Réunion (La Réunion)",
43180 "Romania (România)",
43196 "Saint Barthélemy",
43207 "Saint Kitts and Nevis",
43217 "Saint Martin (Saint-Martin (partie française))",
43223 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
43228 "Saint Vincent and the Grenadines",
43243 "São Tomé and Príncipe (São Tomé e Príncipe)",
43248 "Saudi Arabia (المملكة العربية السعودية)",
43253 "Senegal (Sénégal)",
43283 "Slovakia (Slovensko)",
43288 "Slovenia (Slovenija)",
43298 "Somalia (Soomaaliya)",
43308 "South Korea (대한민국)",
43313 "South Sudan (جنوب السودان)",
43323 "Sri Lanka (ශ්රී ලංකාව)",
43328 "Sudan (السودان)",
43338 "Svalbard and Jan Mayen",
43349 "Sweden (Sverige)",
43354 "Switzerland (Schweiz)",
43359 "Syria (سوريا)",
43404 "Trinidad and Tobago",
43409 "Tunisia (تونس)",
43414 "Turkey (Türkiye)",
43424 "Turks and Caicos Islands",
43434 "U.S. Virgin Islands",
43444 "Ukraine (Україна)",
43449 "United Arab Emirates (الإمارات العربية المتحدة)",
43471 "Uzbekistan (Oʻzbekiston)",
43481 "Vatican City (Città del Vaticano)",
43492 "Vietnam (Việt Nam)",
43497 "Wallis and Futuna (Wallis-et-Futuna)",
43502 "Western Sahara (الصحراء الغربية)",
43508 "Yemen (اليمن)",
43532 * This script refer to:
43533 * Title: International Telephone Input
43534 * Author: Jack O'Connor
43535 * Code version: v12.1.12
43536 * Availability: https://github.com/jackocnr/intl-tel-input.git
43540 * @class Roo.bootstrap.PhoneInput
43541 * @extends Roo.bootstrap.TriggerField
43542 * An input with International dial-code selection
43544 * @cfg {String} defaultDialCode default '+852'
43545 * @cfg {Array} preferedCountries default []
43548 * Create a new PhoneInput.
43549 * @param {Object} config Configuration options
43552 Roo.bootstrap.PhoneInput = function(config) {
43553 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43556 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43558 listWidth: undefined,
43560 selectedClass: 'active',
43562 invalidClass : "has-warning",
43564 validClass: 'has-success',
43566 allowed: '0123456789',
43571 * @cfg {String} defaultDialCode The default dial code when initializing the input
43573 defaultDialCode: '+852',
43576 * @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
43578 preferedCountries: false,
43580 getAutoCreate : function()
43582 var data = Roo.bootstrap.PhoneInputData();
43583 var align = this.labelAlign || this.parentLabelAlign();
43586 this.allCountries = [];
43587 this.dialCodeMapping = [];
43589 for (var i = 0; i < data.length; i++) {
43591 this.allCountries[i] = {
43595 priority: c[3] || 0,
43596 areaCodes: c[4] || null
43598 this.dialCodeMapping[c[2]] = {
43601 priority: c[3] || 0,
43602 areaCodes: c[4] || null
43614 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43615 maxlength: this.max_length,
43616 cls : 'form-control tel-input',
43617 autocomplete: 'new-password'
43620 var hiddenInput = {
43623 cls: 'hidden-tel-input'
43627 hiddenInput.name = this.name;
43630 if (this.disabled) {
43631 input.disabled = true;
43634 var flag_container = {
43651 cls: this.hasFeedback ? 'has-feedback' : '',
43657 cls: 'dial-code-holder',
43664 cls: 'roo-select2-container input-group',
43671 if (this.fieldLabel.length) {
43674 tooltip: 'This field is required'
43680 cls: 'control-label',
43686 html: this.fieldLabel
43689 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43695 if(this.indicatorpos == 'right') {
43696 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43703 if(align == 'left') {
43711 if(this.labelWidth > 12){
43712 label.style = "width: " + this.labelWidth + 'px';
43714 if(this.labelWidth < 13 && this.labelmd == 0){
43715 this.labelmd = this.labelWidth;
43717 if(this.labellg > 0){
43718 label.cls += ' col-lg-' + this.labellg;
43719 input.cls += ' col-lg-' + (12 - this.labellg);
43721 if(this.labelmd > 0){
43722 label.cls += ' col-md-' + this.labelmd;
43723 container.cls += ' col-md-' + (12 - this.labelmd);
43725 if(this.labelsm > 0){
43726 label.cls += ' col-sm-' + this.labelsm;
43727 container.cls += ' col-sm-' + (12 - this.labelsm);
43729 if(this.labelxs > 0){
43730 label.cls += ' col-xs-' + this.labelxs;
43731 container.cls += ' col-xs-' + (12 - this.labelxs);
43741 var settings = this;
43743 ['xs','sm','md','lg'].map(function(size){
43744 if (settings[size]) {
43745 cfg.cls += ' col-' + size + '-' + settings[size];
43749 this.store = new Roo.data.Store({
43750 proxy : new Roo.data.MemoryProxy({}),
43751 reader : new Roo.data.JsonReader({
43762 'name' : 'dialCode',
43766 'name' : 'priority',
43770 'name' : 'areaCodes',
43777 if(!this.preferedCountries) {
43778 this.preferedCountries = [
43785 var p = this.preferedCountries.reverse();
43788 for (var i = 0; i < p.length; i++) {
43789 for (var j = 0; j < this.allCountries.length; j++) {
43790 if(this.allCountries[j].iso2 == p[i]) {
43791 var t = this.allCountries[j];
43792 this.allCountries.splice(j,1);
43793 this.allCountries.unshift(t);
43799 this.store.proxy.data = {
43801 data: this.allCountries
43807 initEvents : function()
43810 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43812 this.indicator = this.indicatorEl();
43813 this.flag = this.flagEl();
43814 this.dialCodeHolder = this.dialCodeHolderEl();
43816 this.trigger = this.el.select('div.flag-box',true).first();
43817 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43822 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43823 _this.list.setWidth(lw);
43826 this.list.on('mouseover', this.onViewOver, this);
43827 this.list.on('mousemove', this.onViewMove, this);
43828 this.inputEl().on("keyup", this.onKeyUp, this);
43829 this.inputEl().on("keypress", this.onKeyPress, this);
43831 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43833 this.view = new Roo.View(this.list, this.tpl, {
43834 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43837 this.view.on('click', this.onViewClick, this);
43838 this.setValue(this.defaultDialCode);
43841 onTriggerClick : function(e)
43843 Roo.log('trigger click');
43848 if(this.isExpanded()){
43850 this.hasFocus = false;
43852 this.store.load({});
43853 this.hasFocus = true;
43858 isExpanded : function()
43860 return this.list.isVisible();
43863 collapse : function()
43865 if(!this.isExpanded()){
43869 Roo.get(document).un('mousedown', this.collapseIf, this);
43870 Roo.get(document).un('mousewheel', this.collapseIf, this);
43871 this.fireEvent('collapse', this);
43875 expand : function()
43879 if(this.isExpanded() || !this.hasFocus){
43883 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43884 this.list.setWidth(lw);
43887 this.restrictHeight();
43889 Roo.get(document).on('mousedown', this.collapseIf, this);
43890 Roo.get(document).on('mousewheel', this.collapseIf, this);
43892 this.fireEvent('expand', this);
43895 restrictHeight : function()
43897 this.list.alignTo(this.inputEl(), this.listAlign);
43898 this.list.alignTo(this.inputEl(), this.listAlign);
43901 onViewOver : function(e, t)
43903 if(this.inKeyMode){
43906 var item = this.view.findItemFromChild(t);
43909 var index = this.view.indexOf(item);
43910 this.select(index, false);
43915 onViewClick : function(view, doFocus, el, e)
43917 var index = this.view.getSelectedIndexes()[0];
43919 var r = this.store.getAt(index);
43922 this.onSelect(r, index);
43924 if(doFocus !== false && !this.blockFocus){
43925 this.inputEl().focus();
43929 onViewMove : function(e, t)
43931 this.inKeyMode = false;
43934 select : function(index, scrollIntoView)
43936 this.selectedIndex = index;
43937 this.view.select(index);
43938 if(scrollIntoView !== false){
43939 var el = this.view.getNode(index);
43941 this.list.scrollChildIntoView(el, false);
43946 createList : function()
43948 this.list = Roo.get(document.body).createChild({
43950 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43951 style: 'display:none'
43954 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43957 collapseIf : function(e)
43959 var in_combo = e.within(this.el);
43960 var in_list = e.within(this.list);
43961 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43963 if (in_combo || in_list || is_list) {
43969 onSelect : function(record, index)
43971 if(this.fireEvent('beforeselect', this, record, index) !== false){
43973 this.setFlagClass(record.data.iso2);
43974 this.setDialCode(record.data.dialCode);
43975 this.hasFocus = false;
43977 this.fireEvent('select', this, record, index);
43981 flagEl : function()
43983 var flag = this.el.select('div.flag',true).first();
43990 dialCodeHolderEl : function()
43992 var d = this.el.select('input.dial-code-holder',true).first();
43999 setDialCode : function(v)
44001 this.dialCodeHolder.dom.value = '+'+v;
44004 setFlagClass : function(n)
44006 this.flag.dom.className = 'flag '+n;
44009 getValue : function()
44011 var v = this.inputEl().getValue();
44012 if(this.dialCodeHolder) {
44013 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
44018 setValue : function(v)
44020 var d = this.getDialCode(v);
44022 //invalid dial code
44023 if(v.length == 0 || !d || d.length == 0) {
44025 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
44026 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44032 this.setFlagClass(this.dialCodeMapping[d].iso2);
44033 this.setDialCode(d);
44034 this.inputEl().dom.value = v.replace('+'+d,'');
44035 this.hiddenEl().dom.value = this.getValue();
44040 getDialCode : function(v)
44044 if (v.length == 0) {
44045 return this.dialCodeHolder.dom.value;
44049 if (v.charAt(0) != "+") {
44052 var numericChars = "";
44053 for (var i = 1; i < v.length; i++) {
44054 var c = v.charAt(i);
44057 if (this.dialCodeMapping[numericChars]) {
44058 dialCode = v.substr(1, i);
44060 if (numericChars.length == 4) {
44070 this.setValue(this.defaultDialCode);
44074 hiddenEl : function()
44076 return this.el.select('input.hidden-tel-input',true).first();
44079 // after setting val
44080 onKeyUp : function(e){
44081 this.setValue(this.getValue());
44084 onKeyPress : function(e){
44085 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
44092 * @class Roo.bootstrap.MoneyField
44093 * @extends Roo.bootstrap.ComboBox
44094 * Bootstrap MoneyField class
44097 * Create a new MoneyField.
44098 * @param {Object} config Configuration options
44101 Roo.bootstrap.MoneyField = function(config) {
44103 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
44107 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
44110 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
44112 allowDecimals : true,
44114 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
44116 decimalSeparator : ".",
44118 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
44120 decimalPrecision : 0,
44122 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
44124 allowNegative : true,
44126 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
44130 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
44132 minValue : Number.NEGATIVE_INFINITY,
44134 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
44136 maxValue : Number.MAX_VALUE,
44138 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
44140 minText : "The minimum value for this field is {0}",
44142 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
44144 maxText : "The maximum value for this field is {0}",
44146 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
44147 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
44149 nanText : "{0} is not a valid number",
44151 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
44155 * @cfg {String} defaults currency of the MoneyField
44156 * value should be in lkey
44158 defaultCurrency : false,
44160 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
44162 thousandsDelimiter : false,
44164 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
44175 getAutoCreate : function()
44177 var align = this.labelAlign || this.parentLabelAlign();
44189 cls : 'form-control roo-money-amount-input',
44190 autocomplete: 'new-password'
44193 var hiddenInput = {
44197 cls: 'hidden-number-input'
44200 if(this.max_length) {
44201 input.maxlength = this.max_length;
44205 hiddenInput.name = this.name;
44208 if (this.disabled) {
44209 input.disabled = true;
44212 var clg = 12 - this.inputlg;
44213 var cmd = 12 - this.inputmd;
44214 var csm = 12 - this.inputsm;
44215 var cxs = 12 - this.inputxs;
44219 cls : 'row roo-money-field',
44223 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
44227 cls: 'roo-select2-container input-group',
44231 cls : 'form-control roo-money-currency-input',
44232 autocomplete: 'new-password',
44234 name : this.currencyName
44238 cls : 'input-group-addon',
44252 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44256 cls: this.hasFeedback ? 'has-feedback' : '',
44267 if (this.fieldLabel.length) {
44270 tooltip: 'This field is required'
44276 cls: 'control-label',
44282 html: this.fieldLabel
44285 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44291 if(this.indicatorpos == 'right') {
44292 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44299 if(align == 'left') {
44307 if(this.labelWidth > 12){
44308 label.style = "width: " + this.labelWidth + 'px';
44310 if(this.labelWidth < 13 && this.labelmd == 0){
44311 this.labelmd = this.labelWidth;
44313 if(this.labellg > 0){
44314 label.cls += ' col-lg-' + this.labellg;
44315 input.cls += ' col-lg-' + (12 - this.labellg);
44317 if(this.labelmd > 0){
44318 label.cls += ' col-md-' + this.labelmd;
44319 container.cls += ' col-md-' + (12 - this.labelmd);
44321 if(this.labelsm > 0){
44322 label.cls += ' col-sm-' + this.labelsm;
44323 container.cls += ' col-sm-' + (12 - this.labelsm);
44325 if(this.labelxs > 0){
44326 label.cls += ' col-xs-' + this.labelxs;
44327 container.cls += ' col-xs-' + (12 - this.labelxs);
44338 var settings = this;
44340 ['xs','sm','md','lg'].map(function(size){
44341 if (settings[size]) {
44342 cfg.cls += ' col-' + size + '-' + settings[size];
44349 initEvents : function()
44351 this.indicator = this.indicatorEl();
44353 this.initCurrencyEvent();
44355 this.initNumberEvent();
44358 initCurrencyEvent : function()
44361 throw "can not find store for combo";
44364 this.store = Roo.factory(this.store, Roo.data);
44365 this.store.parent = this;
44369 this.triggerEl = this.el.select('.input-group-addon', true).first();
44371 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44376 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44377 _this.list.setWidth(lw);
44380 this.list.on('mouseover', this.onViewOver, this);
44381 this.list.on('mousemove', this.onViewMove, this);
44382 this.list.on('scroll', this.onViewScroll, this);
44385 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44388 this.view = new Roo.View(this.list, this.tpl, {
44389 singleSelect:true, store: this.store, selectedClass: this.selectedClass
44392 this.view.on('click', this.onViewClick, this);
44394 this.store.on('beforeload', this.onBeforeLoad, this);
44395 this.store.on('load', this.onLoad, this);
44396 this.store.on('loadexception', this.onLoadException, this);
44398 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44399 "up" : function(e){
44400 this.inKeyMode = true;
44404 "down" : function(e){
44405 if(!this.isExpanded()){
44406 this.onTriggerClick();
44408 this.inKeyMode = true;
44413 "enter" : function(e){
44416 if(this.fireEvent("specialkey", this, e)){
44417 this.onViewClick(false);
44423 "esc" : function(e){
44427 "tab" : function(e){
44430 if(this.fireEvent("specialkey", this, e)){
44431 this.onViewClick(false);
44439 doRelay : function(foo, bar, hname){
44440 if(hname == 'down' || this.scope.isExpanded()){
44441 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44449 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44453 initNumberEvent : function(e)
44455 this.inputEl().on("keydown" , this.fireKey, this);
44456 this.inputEl().on("focus", this.onFocus, this);
44457 this.inputEl().on("blur", this.onBlur, this);
44459 this.inputEl().relayEvent('keyup', this);
44461 if(this.indicator){
44462 this.indicator.addClass('invisible');
44465 this.originalValue = this.getValue();
44467 if(this.validationEvent == 'keyup'){
44468 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44469 this.inputEl().on('keyup', this.filterValidation, this);
44471 else if(this.validationEvent !== false){
44472 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44475 if(this.selectOnFocus){
44476 this.on("focus", this.preFocus, this);
44479 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44480 this.inputEl().on("keypress", this.filterKeys, this);
44482 this.inputEl().relayEvent('keypress', this);
44485 var allowed = "0123456789";
44487 if(this.allowDecimals){
44488 allowed += this.decimalSeparator;
44491 if(this.allowNegative){
44495 if(this.thousandsDelimiter) {
44499 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44501 var keyPress = function(e){
44503 var k = e.getKey();
44505 var c = e.getCharCode();
44508 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44509 allowed.indexOf(String.fromCharCode(c)) === -1
44515 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44519 if(allowed.indexOf(String.fromCharCode(c)) === -1){
44524 this.inputEl().on("keypress", keyPress, this);
44528 onTriggerClick : function(e)
44535 this.loadNext = false;
44537 if(this.isExpanded()){
44542 this.hasFocus = true;
44544 if(this.triggerAction == 'all') {
44545 this.doQuery(this.allQuery, true);
44549 this.doQuery(this.getRawValue());
44552 getCurrency : function()
44554 var v = this.currencyEl().getValue();
44559 restrictHeight : function()
44561 this.list.alignTo(this.currencyEl(), this.listAlign);
44562 this.list.alignTo(this.currencyEl(), this.listAlign);
44565 onViewClick : function(view, doFocus, el, e)
44567 var index = this.view.getSelectedIndexes()[0];
44569 var r = this.store.getAt(index);
44572 this.onSelect(r, index);
44576 onSelect : function(record, index){
44578 if(this.fireEvent('beforeselect', this, record, index) !== false){
44580 this.setFromCurrencyData(index > -1 ? record.data : false);
44584 this.fireEvent('select', this, record, index);
44588 setFromCurrencyData : function(o)
44592 this.lastCurrency = o;
44594 if (this.currencyField) {
44595 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44597 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
44600 this.lastSelectionText = currency;
44602 //setting default currency
44603 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44604 this.setCurrency(this.defaultCurrency);
44608 this.setCurrency(currency);
44611 setFromData : function(o)
44615 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44617 this.setFromCurrencyData(c);
44622 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44624 Roo.log('no value set for '+ (this.name ? this.name : this.id));
44627 this.setValue(value);
44631 setCurrency : function(v)
44633 this.currencyValue = v;
44636 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44641 setValue : function(v)
44643 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44649 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44651 this.inputEl().dom.value = (v == '') ? '' :
44652 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44654 if(!this.allowZero && v === '0') {
44655 this.hiddenEl().dom.value = '';
44656 this.inputEl().dom.value = '';
44663 getRawValue : function()
44665 var v = this.inputEl().getValue();
44670 getValue : function()
44672 return this.fixPrecision(this.parseValue(this.getRawValue()));
44675 parseValue : function(value)
44677 if(this.thousandsDelimiter) {
44679 r = new RegExp(",", "g");
44680 value = value.replace(r, "");
44683 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44684 return isNaN(value) ? '' : value;
44688 fixPrecision : function(value)
44690 if(this.thousandsDelimiter) {
44692 r = new RegExp(",", "g");
44693 value = value.replace(r, "");
44696 var nan = isNaN(value);
44698 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44699 return nan ? '' : value;
44701 return parseFloat(value).toFixed(this.decimalPrecision);
44704 decimalPrecisionFcn : function(v)
44706 return Math.floor(v);
44709 validateValue : function(value)
44711 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44715 var num = this.parseValue(value);
44718 this.markInvalid(String.format(this.nanText, value));
44722 if(num < this.minValue){
44723 this.markInvalid(String.format(this.minText, this.minValue));
44727 if(num > this.maxValue){
44728 this.markInvalid(String.format(this.maxText, this.maxValue));
44735 validate : function()
44737 if(this.disabled || this.allowBlank){
44742 var currency = this.getCurrency();
44744 if(this.validateValue(this.getRawValue()) && currency.length){
44749 this.markInvalid();
44753 getName: function()
44758 beforeBlur : function()
44764 var v = this.parseValue(this.getRawValue());
44771 onBlur : function()
44775 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44776 //this.el.removeClass(this.focusClass);
44779 this.hasFocus = false;
44781 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44785 var v = this.getValue();
44787 if(String(v) !== String(this.startValue)){
44788 this.fireEvent('change', this, v, this.startValue);
44791 this.fireEvent("blur", this);
44794 inputEl : function()
44796 return this.el.select('.roo-money-amount-input', true).first();
44799 currencyEl : function()
44801 return this.el.select('.roo-money-currency-input', true).first();
44804 hiddenEl : function()
44806 return this.el.select('input.hidden-number-input',true).first();
44810 * @class Roo.bootstrap.BezierSignature
44811 * @extends Roo.bootstrap.Component
44812 * Bootstrap BezierSignature class
44813 * This script refer to:
44814 * Title: Signature Pad
44816 * Availability: https://github.com/szimek/signature_pad
44819 * Create a new BezierSignature
44820 * @param {Object} config The config object
44823 Roo.bootstrap.BezierSignature = function(config){
44824 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44830 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44837 mouse_btn_down: true,
44840 * @cfg {int} canvas height
44842 canvas_height: '200px',
44845 * @cfg {float|function} Radius of a single dot.
44850 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44855 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44860 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44865 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44870 * @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.
44872 bg_color: 'rgba(0, 0, 0, 0)',
44875 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44877 dot_color: 'black',
44880 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44882 velocity_filter_weight: 0.7,
44885 * @cfg {function} Callback when stroke begin.
44890 * @cfg {function} Callback when stroke end.
44894 getAutoCreate : function()
44896 var cls = 'roo-signature column';
44899 cls += ' ' + this.cls;
44909 for(var i = 0; i < col_sizes.length; i++) {
44910 if(this[col_sizes[i]]) {
44911 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44921 cls: 'roo-signature-body',
44925 cls: 'roo-signature-body-canvas',
44926 height: this.canvas_height,
44927 width: this.canvas_width
44934 style: 'display: none'
44942 initEvents: function()
44944 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44946 var canvas = this.canvasEl();
44948 // mouse && touch event swapping...
44949 canvas.dom.style.touchAction = 'none';
44950 canvas.dom.style.msTouchAction = 'none';
44952 this.mouse_btn_down = false;
44953 canvas.on('mousedown', this._handleMouseDown, this);
44954 canvas.on('mousemove', this._handleMouseMove, this);
44955 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44957 if (window.PointerEvent) {
44958 canvas.on('pointerdown', this._handleMouseDown, this);
44959 canvas.on('pointermove', this._handleMouseMove, this);
44960 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44963 if ('ontouchstart' in window) {
44964 canvas.on('touchstart', this._handleTouchStart, this);
44965 canvas.on('touchmove', this._handleTouchMove, this);
44966 canvas.on('touchend', this._handleTouchEnd, this);
44969 Roo.EventManager.onWindowResize(this.resize, this, true);
44971 // file input event
44972 this.fileEl().on('change', this.uploadImage, this);
44979 resize: function(){
44981 var canvas = this.canvasEl().dom;
44982 var ctx = this.canvasElCtx();
44983 var img_data = false;
44985 if(canvas.width > 0) {
44986 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44988 // setting canvas width will clean img data
44991 var style = window.getComputedStyle ?
44992 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44994 var padding_left = parseInt(style.paddingLeft) || 0;
44995 var padding_right = parseInt(style.paddingRight) || 0;
44997 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
45000 ctx.putImageData(img_data, 0, 0);
45004 _handleMouseDown: function(e)
45006 if (e.browserEvent.which === 1) {
45007 this.mouse_btn_down = true;
45008 this.strokeBegin(e);
45012 _handleMouseMove: function (e)
45014 if (this.mouse_btn_down) {
45015 this.strokeMoveUpdate(e);
45019 _handleMouseUp: function (e)
45021 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
45022 this.mouse_btn_down = false;
45027 _handleTouchStart: function (e) {
45029 e.preventDefault();
45030 if (e.browserEvent.targetTouches.length === 1) {
45031 // var touch = e.browserEvent.changedTouches[0];
45032 // this.strokeBegin(touch);
45034 this.strokeBegin(e); // assume e catching the correct xy...
45038 _handleTouchMove: function (e) {
45039 e.preventDefault();
45040 // var touch = event.targetTouches[0];
45041 // _this._strokeMoveUpdate(touch);
45042 this.strokeMoveUpdate(e);
45045 _handleTouchEnd: function (e) {
45046 var wasCanvasTouched = e.target === this.canvasEl().dom;
45047 if (wasCanvasTouched) {
45048 e.preventDefault();
45049 // var touch = event.changedTouches[0];
45050 // _this._strokeEnd(touch);
45055 reset: function () {
45056 this._lastPoints = [];
45057 this._lastVelocity = 0;
45058 this._lastWidth = (this.min_width + this.max_width) / 2;
45059 this.canvasElCtx().fillStyle = this.dot_color;
45062 strokeMoveUpdate: function(e)
45064 this.strokeUpdate(e);
45066 if (this.throttle) {
45067 this.throttleStroke(this.strokeUpdate, this.throttle);
45070 this.strokeUpdate(e);
45074 strokeBegin: function(e)
45076 var newPointGroup = {
45077 color: this.dot_color,
45081 if (typeof this.onBegin === 'function') {
45085 this.curve_data.push(newPointGroup);
45087 this.strokeUpdate(e);
45090 strokeUpdate: function(e)
45092 var rect = this.canvasEl().dom.getBoundingClientRect();
45093 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
45094 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
45095 var lastPoints = lastPointGroup.points;
45096 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
45097 var isLastPointTooClose = lastPoint
45098 ? point.distanceTo(lastPoint) <= this.min_distance
45100 var color = lastPointGroup.color;
45101 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
45102 var curve = this.addPoint(point);
45104 this.drawDot({color: color, point: point});
45107 this.drawCurve({color: color, curve: curve});
45117 strokeEnd: function(e)
45119 this.strokeUpdate(e);
45120 if (typeof this.onEnd === 'function') {
45125 addPoint: function (point) {
45126 var _lastPoints = this._lastPoints;
45127 _lastPoints.push(point);
45128 if (_lastPoints.length > 2) {
45129 if (_lastPoints.length === 3) {
45130 _lastPoints.unshift(_lastPoints[0]);
45132 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
45133 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
45134 _lastPoints.shift();
45140 calculateCurveWidths: function (startPoint, endPoint) {
45141 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
45142 (1 - this.velocity_filter_weight) * this._lastVelocity;
45144 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
45147 start: this._lastWidth
45150 this._lastVelocity = velocity;
45151 this._lastWidth = newWidth;
45155 drawDot: function (_a) {
45156 var color = _a.color, point = _a.point;
45157 var ctx = this.canvasElCtx();
45158 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
45160 this.drawCurveSegment(point.x, point.y, width);
45162 ctx.fillStyle = color;
45166 drawCurve: function (_a) {
45167 var color = _a.color, curve = _a.curve;
45168 var ctx = this.canvasElCtx();
45169 var widthDelta = curve.endWidth - curve.startWidth;
45170 var drawSteps = Math.floor(curve.length()) * 2;
45172 ctx.fillStyle = color;
45173 for (var i = 0; i < drawSteps; i += 1) {
45174 var t = i / drawSteps;
45180 var x = uuu * curve.startPoint.x;
45181 x += 3 * uu * t * curve.control1.x;
45182 x += 3 * u * tt * curve.control2.x;
45183 x += ttt * curve.endPoint.x;
45184 var y = uuu * curve.startPoint.y;
45185 y += 3 * uu * t * curve.control1.y;
45186 y += 3 * u * tt * curve.control2.y;
45187 y += ttt * curve.endPoint.y;
45188 var width = curve.startWidth + ttt * widthDelta;
45189 this.drawCurveSegment(x, y, width);
45195 drawCurveSegment: function (x, y, width) {
45196 var ctx = this.canvasElCtx();
45198 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
45199 this.is_empty = false;
45204 var ctx = this.canvasElCtx();
45205 var canvas = this.canvasEl().dom;
45206 ctx.fillStyle = this.bg_color;
45207 ctx.clearRect(0, 0, canvas.width, canvas.height);
45208 ctx.fillRect(0, 0, canvas.width, canvas.height);
45209 this.curve_data = [];
45211 this.is_empty = true;
45216 return this.el.select('input',true).first();
45219 canvasEl: function()
45221 return this.el.select('canvas',true).first();
45224 canvasElCtx: function()
45226 return this.el.select('canvas',true).first().dom.getContext('2d');
45229 getImage: function(type)
45231 if(this.is_empty) {
45236 return this.canvasEl().dom.toDataURL('image/'+type, 1);
45239 drawFromImage: function(img_src)
45241 var img = new Image();
45243 img.onload = function(){
45244 this.canvasElCtx().drawImage(img, 0, 0);
45249 this.is_empty = false;
45252 selectImage: function()
45254 this.fileEl().dom.click();
45257 uploadImage: function(e)
45259 var reader = new FileReader();
45261 reader.onload = function(e){
45262 var img = new Image();
45263 img.onload = function(){
45265 this.canvasElCtx().drawImage(img, 0, 0);
45267 img.src = e.target.result;
45270 reader.readAsDataURL(e.target.files[0]);
45273 // Bezier Point Constructor
45274 Point: (function () {
45275 function Point(x, y, time) {
45278 this.time = time || Date.now();
45280 Point.prototype.distanceTo = function (start) {
45281 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45283 Point.prototype.equals = function (other) {
45284 return this.x === other.x && this.y === other.y && this.time === other.time;
45286 Point.prototype.velocityFrom = function (start) {
45287 return this.time !== start.time
45288 ? this.distanceTo(start) / (this.time - start.time)
45295 // Bezier Constructor
45296 Bezier: (function () {
45297 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45298 this.startPoint = startPoint;
45299 this.control2 = control2;
45300 this.control1 = control1;
45301 this.endPoint = endPoint;
45302 this.startWidth = startWidth;
45303 this.endWidth = endWidth;
45305 Bezier.fromPoints = function (points, widths, scope) {
45306 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45307 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45308 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45310 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45311 var dx1 = s1.x - s2.x;
45312 var dy1 = s1.y - s2.y;
45313 var dx2 = s2.x - s3.x;
45314 var dy2 = s2.y - s3.y;
45315 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45316 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45317 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45318 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45319 var dxm = m1.x - m2.x;
45320 var dym = m1.y - m2.y;
45321 var k = l2 / (l1 + l2);
45322 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45323 var tx = s2.x - cm.x;
45324 var ty = s2.y - cm.y;
45326 c1: new scope.Point(m1.x + tx, m1.y + ty),
45327 c2: new scope.Point(m2.x + tx, m2.y + ty)
45330 Bezier.prototype.length = function () {
45335 for (var i = 0; i <= steps; i += 1) {
45337 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45338 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45340 var xdiff = cx - px;
45341 var ydiff = cy - py;
45342 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45349 Bezier.prototype.point = function (t, start, c1, c2, end) {
45350 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45351 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45352 + (3.0 * c2 * (1.0 - t) * t * t)
45353 + (end * t * t * t);
45358 throttleStroke: function(fn, wait) {
45359 if (wait === void 0) { wait = 250; }
45361 var timeout = null;
45365 var later = function () {
45366 previous = Date.now();
45368 result = fn.apply(storedContext, storedArgs);
45370 storedContext = null;
45374 return function wrapper() {
45376 for (var _i = 0; _i < arguments.length; _i++) {
45377 args[_i] = arguments[_i];
45379 var now = Date.now();
45380 var remaining = wait - (now - previous);
45381 storedContext = this;
45383 if (remaining <= 0 || remaining > wait) {
45385 clearTimeout(timeout);
45389 result = fn.apply(storedContext, storedArgs);
45391 storedContext = null;
45395 else if (!timeout) {
45396 timeout = window.setTimeout(later, remaining);