2 * set the version of bootstrap based on the stylesheet...
6 Roo.bootstrap.version = ( function() {
8 Roo.each(document.styleSheets, function(s) {
9 if ( s.href && s.href.match(/css-bootstrap4/)) {
14 Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
19 * Ext JS Library 1.1.1
20 * Copyright(c) 2006-2007, Ext JS, LLC.
22 * Originally Released Under LGPL - original licence link has changed is not relivant.
25 * <script type="text/javascript">
31 * Simple class that can provide a shadow effect for any element. Note that the element MUST be absolutely positioned,
32 * and the shadow does not provide any shimming. This should be used only in simple cases -- for more advanced
33 * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
36 * @param {Object} config The config object
38 Roo.Shadow = function(config){
39 Roo.apply(this, config);
40 if(typeof this.mode != "string"){
41 this.mode = this.defaultMode;
43 var o = this.offset, a = {h: 0};
44 var rad = Math.floor(this.offset/2);
45 switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
51 a.l -= this.offset + rad;
52 a.t -= this.offset + rad;
63 a.l -= (this.offset - rad);
64 a.t -= this.offset + rad;
66 a.w -= (this.offset - rad)*2;
77 a.l -= (this.offset - rad);
78 a.t -= (this.offset - rad);
80 a.w -= (this.offset + rad + 1);
81 a.h -= (this.offset + rad);
90 Roo.Shadow.prototype = {
93 * The shadow display mode. Supports the following options:<br />
94 * sides: Shadow displays on both sides and bottom only<br />
95 * frame: Shadow displays equally on all four sides<br />
96 * drop: Traditional bottom-right drop shadow (default)
99 * @cfg {String} offset
100 * The number of pixels to offset the shadow from the element (defaults to 4)
108 * Displays the shadow under the target element
109 * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
111 show : function(target){
112 target = Roo.get(target);
114 this.el = Roo.Shadow.Pool.pull();
115 if(this.el.dom.nextSibling != target.dom){
116 this.el.insertBefore(target);
119 this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
121 this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
124 target.getLeft(true),
129 this.el.dom.style.display = "block";
133 * Returns true if the shadow is visible, else false
135 isVisible : function(){
136 return this.el ? true : false;
140 * Direct alignment when values are already available. Show must be called at least once before
141 * calling this method to ensure it is initialized.
142 * @param {Number} left The target element left position
143 * @param {Number} top The target element top position
144 * @param {Number} width The target element width
145 * @param {Number} height The target element height
147 realign : function(l, t, w, h){
151 var a = this.adjusts, d = this.el.dom, s = d.style;
153 s.left = (l+a.l)+"px";
154 s.top = (t+a.t)+"px";
155 var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
157 if(s.width != sws || s.height != shs){
161 var cn = d.childNodes;
162 var sww = Math.max(0, (sw-12))+"px";
163 cn[0].childNodes[1].style.width = sww;
164 cn[1].childNodes[1].style.width = sww;
165 cn[2].childNodes[1].style.width = sww;
166 cn[1].style.height = Math.max(0, (sh-12))+"px";
176 this.el.dom.style.display = "none";
177 Roo.Shadow.Pool.push(this.el);
183 * Adjust the z-index of this shadow
184 * @param {Number} zindex The new z-index
186 setZIndex : function(z){
189 this.el.setStyle("z-index", z);
194 // Private utility class that manages the internal Shadow cache
195 Roo.Shadow.Pool = function(){
197 var markup = Roo.isIE ?
198 '<div class="x-ie-shadow"></div>' :
199 '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
204 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
205 sh.autoBoxAdjust = false;
217 * base class for bootstrap elements.
221 Roo.bootstrap = Roo.bootstrap || {};
223 * @class Roo.bootstrap.Component
224 * @extends Roo.Component
225 * Bootstrap Component base class
226 * @cfg {String} cls css class
227 * @cfg {String} style any extra css
228 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
229 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
230 * @cfg {string} dataId cutomer id
231 * @cfg {string} name Specifies name attribute
232 * @cfg {string} tooltip Text for the tooltip
233 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
234 * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
237 * Do not use directly - it does not do anything..
238 * @param {Object} config The config object
243 Roo.bootstrap.Component = function(config){
244 Roo.bootstrap.Component.superclass.constructor.call(this, config);
248 * @event childrenrendered
249 * Fires when the children have been rendered..
250 * @param {Roo.bootstrap.Component} this
252 "childrenrendered" : true
261 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
264 allowDomMove : false, // to stop relocations in parent onRender...
274 * Initialize Events for the element
276 initEvents : function() { },
282 can_build_overlaid : true,
284 container_method : false,
291 // returns the parent component..
292 return Roo.ComponentMgr.get(this.parentId)
298 onRender : function(ct, position)
300 // Roo.log("Call onRender: " + this.xtype);
302 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
305 if (this.el.attr('xtype')) {
306 this.el.attr('xtypex', this.el.attr('xtype'));
307 this.el.dom.removeAttribute('xtype');
317 var cfg = Roo.apply({}, this.getAutoCreate());
319 cfg.id = this.id || Roo.id();
321 // fill in the extra attributes
322 if (this.xattr && typeof(this.xattr) =='object') {
323 for (var i in this.xattr) {
324 cfg[i] = this.xattr[i];
329 cfg.dataId = this.dataId;
333 cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
336 if (this.style) { // fixme needs to support more complex style data.
337 cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
341 cfg.name = this.name;
344 this.el = ct.createChild(cfg, position);
347 this.tooltipEl().attr('tooltip', this.tooltip);
350 if(this.tabIndex !== undefined){
351 this.el.dom.setAttribute('tabIndex', this.tabIndex);
358 * Fetch the element to add children to
359 * @return {Roo.Element} defaults to this.el
361 getChildContainer : function()
365 getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
367 return Roo.get(document.body);
371 * Fetch the element to display the tooltip on.
372 * @return {Roo.Element} defaults to this.el
374 tooltipEl : function()
379 addxtype : function(tree,cntr)
383 cn = Roo.factory(tree);
384 //Roo.log(['addxtype', cn]);
386 cn.parentType = this.xtype; //??
387 cn.parentId = this.id;
389 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
390 if (typeof(cn.container_method) == 'string') {
391 cntr = cn.container_method;
395 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
397 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
399 var build_from_html = Roo.XComponent.build_from_html;
401 var is_body = (tree.xtype == 'Body') ;
403 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
405 var self_cntr_el = Roo.get(this[cntr](false));
407 // do not try and build conditional elements
408 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
412 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
413 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
414 return this.addxtypeChild(tree,cntr, is_body);
417 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
420 return this.addxtypeChild(Roo.apply({}, tree),cntr);
423 Roo.log('skipping render');
429 if (!build_from_html) {
433 // this i think handles overlaying multiple children of the same type
434 // with the sam eelement.. - which might be buggy..
436 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
442 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
446 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
453 addxtypeChild : function (tree, cntr, is_body)
455 Roo.debug && Roo.log('addxtypeChild:' + cntr);
457 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
460 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
461 (typeof(tree['flexy:foreach']) != 'undefined');
465 skip_children = false;
466 // render the element if it's not BODY.
469 // if parent was disabled, then do not try and create the children..
470 if(!this[cntr](true)){
475 cn = Roo.factory(tree);
477 cn.parentType = this.xtype; //??
478 cn.parentId = this.id;
480 var build_from_html = Roo.XComponent.build_from_html;
483 // does the container contain child eleemnts with 'xtype' attributes.
484 // that match this xtype..
485 // note - when we render we create these as well..
486 // so we should check to see if body has xtype set.
487 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
489 var self_cntr_el = Roo.get(this[cntr](false));
490 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
492 //Roo.log(Roo.XComponent.build_from_html);
493 //Roo.log("got echild:");
496 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
497 // and are not displayed -this causes this to use up the wrong element when matching.
498 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
501 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
502 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
508 //echild.dom.removeAttribute('xtype');
510 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
511 Roo.debug && Roo.log(self_cntr_el);
512 Roo.debug && Roo.log(echild);
513 Roo.debug && Roo.log(cn);
519 // if object has flexy:if - then it may or may not be rendered.
520 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
521 // skip a flexy if element.
522 Roo.debug && Roo.log('skipping render');
523 Roo.debug && Roo.log(tree);
525 Roo.debug && Roo.log('skipping all children');
526 skip_children = true;
531 // actually if flexy:foreach is found, we really want to create
532 // multiple copies here...
534 //Roo.log(this[cntr]());
535 // some elements do not have render methods.. like the layouts...
537 if(this[cntr](true) === false){
542 cn.render && cn.render(this[cntr](true));
545 // then add the element..
552 if (typeof (tree.menu) != 'undefined') {
553 tree.menu.parentType = cn.xtype;
554 tree.menu.triggerEl = cn.el;
555 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
559 if (!tree.items || !tree.items.length) {
561 //Roo.log(["no children", this]);
566 var items = tree.items;
569 //Roo.log(items.length);
571 if (!skip_children) {
572 for(var i =0;i < items.length;i++) {
573 // Roo.log(['add child', items[i]]);
574 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
580 //Roo.log("fire childrenrendered");
582 cn.fireEvent('childrenrendered', this);
588 * Set the element that will be used to show or hide
590 setVisibilityEl : function(el)
592 this.visibilityEl = el;
596 * Get the element that will be used to show or hide
598 getVisibilityEl : function()
600 if (typeof(this.visibilityEl) == 'object') {
601 return this.visibilityEl;
604 if (typeof(this.visibilityEl) == 'string') {
605 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
612 * Show a component - removes 'hidden' class
616 if(!this.getVisibilityEl()){
620 this.getVisibilityEl().removeClass(['hidden','d-none']);
622 this.fireEvent('show', this);
627 * Hide a component - adds 'hidden' class
631 if(!this.getVisibilityEl()){
635 this.getVisibilityEl().addClass(['hidden','d-none']);
637 this.fireEvent('hide', this);
650 * @class Roo.bootstrap.Element
651 * @extends Roo.bootstrap.Component
652 * Bootstrap Element class
653 * @cfg {String} html contents of the element
654 * @cfg {String} tag tag of the element
655 * @cfg {String} cls class of the element
656 * @cfg {Boolean} preventDefault (true|false) default false
657 * @cfg {Boolean} clickable (true|false) default false
658 * @cfg {String} role default blank - set to button to force cursor pointer
662 * Create a new Element
663 * @param {Object} config The config object
666 Roo.bootstrap.Element = function(config){
667 Roo.bootstrap.Element.superclass.constructor.call(this, config);
673 * When a element is chick
674 * @param {Roo.bootstrap.Element} this
675 * @param {Roo.EventObject} e
683 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
688 preventDefault: false,
693 getAutoCreate : function(){
697 // cls: this.cls, double assign in parent class Component.js :: onRender
700 if (this.role !== false) {
701 cfg.role = this.role;
707 initEvents: function()
709 Roo.bootstrap.Element.superclass.initEvents.call(this);
712 this.el.on('click', this.onClick, this);
718 onClick : function(e)
720 if(this.preventDefault){
724 this.fireEvent('click', this, e); // why was this double click before?
732 getValue : function()
734 return this.el.dom.innerHTML;
737 setValue : function(value)
739 this.el.dom.innerHTML = value;
754 * @class Roo.bootstrap.DropTarget
755 * @extends Roo.bootstrap.Element
756 * Bootstrap DropTarget class
758 * @cfg {string} name dropable name
761 * Create a new Dropable Area
762 * @param {Object} config The config object
765 Roo.bootstrap.DropTarget = function(config){
766 Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
772 * When a element is chick
773 * @param {Roo.bootstrap.Element} this
774 * @param {Roo.EventObject} e
780 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element, {
783 getAutoCreate : function(){
788 initEvents: function()
790 Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
791 this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
794 drop : this.dragDrop.createDelegate(this),
795 enter : this.dragEnter.createDelegate(this),
796 out : this.dragOut.createDelegate(this),
797 over : this.dragOver.createDelegate(this)
801 this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
804 dragDrop : function(source,e,data)
806 // user has to decide how to impliment this.
809 //this.fireEvent('drop', this, source, e ,data);
813 dragEnter : function(n, dd, e, data)
815 // probably want to resize the element to match the dropped element..
817 this.originalSize = this.el.getSize();
818 this.el.setSize( n.el.getSize());
819 this.dropZone.DDM.refreshCache(this.name);
820 Roo.log([n, dd, e, data]);
823 dragOut : function(value)
825 // resize back to normal
827 this.el.setSize(this.originalSize);
828 this.dropZone.resetConstraints();
831 dragOver : function()
848 * @class Roo.bootstrap.Body
849 * @extends Roo.bootstrap.Component
850 * Bootstrap Body class
854 * @param {Object} config The config object
857 Roo.bootstrap.Body = function(config){
859 config = config || {};
861 Roo.bootstrap.Body.superclass.constructor.call(this, config);
862 this.el = Roo.get(config.el ? config.el : document.body );
863 if (this.cls && this.cls.length) {
864 Roo.get(document.body).addClass(this.cls);
868 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
870 is_body : true,// just to make sure it's constructed?
875 onRender : function(ct, position)
877 /* Roo.log("Roo.bootstrap.Body - onRender");
878 if (this.cls && this.cls.length) {
879 Roo.get(document.body).addClass(this.cls);
898 * @class Roo.bootstrap.ButtonGroup
899 * @extends Roo.bootstrap.Component
900 * Bootstrap ButtonGroup class
901 * @cfg {String} size lg | sm | xs (default empty normal)
902 * @cfg {String} align vertical | justified (default none)
903 * @cfg {String} direction up | down (default down)
904 * @cfg {Boolean} toolbar false | true
905 * @cfg {Boolean} btn true | false
910 * @param {Object} config The config object
913 Roo.bootstrap.ButtonGroup = function(config){
914 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
917 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
925 getAutoCreate : function(){
931 cfg.html = this.html || cfg.html;
942 if (['vertical','justified'].indexOf(this.align)!==-1) {
943 cfg.cls = 'btn-group-' + this.align;
945 if (this.align == 'justified') {
946 console.log(this.items);
950 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
951 cfg.cls += ' btn-group-' + this.size;
954 if (this.direction == 'up') {
955 cfg.cls += ' dropup' ;
961 * Add a button to the group (similar to NavItem API.)
963 addItem : function(cfg)
965 var cn = new Roo.bootstrap.Button(cfg);
967 cn.parentId = this.id;
968 cn.onRender(this.el, null);
982 * @class Roo.bootstrap.Button
983 * @extends Roo.bootstrap.Component
984 * Bootstrap Button class
985 * @cfg {String} html The button content
986 * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
987 * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
988 * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
989 * @cfg {String} size (lg|sm|xs)
990 * @cfg {String} tag (a|input|submit)
991 * @cfg {String} href empty or href
992 * @cfg {Boolean} disabled default false;
993 * @cfg {Boolean} isClose default false;
994 * @cfg {String} glyphicon depricated - use fa
995 * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
996 * @cfg {String} badge text for badge
997 * @cfg {String} theme (default|glow)
998 * @cfg {Boolean} inverse dark themed version
999 * @cfg {Boolean} toggle is it a slidy toggle button
1000 * @cfg {Boolean} pressed default null - if the button ahs active state
1001 * @cfg {String} ontext text for on slidy toggle state
1002 * @cfg {String} offtext text for off slidy toggle state
1003 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
1004 * @cfg {Boolean} removeClass remove the standard class..
1005 * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href.
1006 * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1009 * Create a new button
1010 * @param {Object} config The config object
1014 Roo.bootstrap.Button = function(config){
1015 Roo.bootstrap.Button.superclass.constructor.call(this, config);
1021 * When a button is pressed
1022 * @param {Roo.bootstrap.Button} btn
1023 * @param {Roo.EventObject} e
1028 * When a button is double clicked
1029 * @param {Roo.bootstrap.Button} btn
1030 * @param {Roo.EventObject} e
1035 * After the button has been toggles
1036 * @param {Roo.bootstrap.Button} btn
1037 * @param {Roo.EventObject} e
1038 * @param {boolean} pressed (also available as button.pressed)
1044 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
1065 preventDefault: true,
1074 getAutoCreate : function(){
1082 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1083 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1084 this.tag = 'button';
1088 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1090 if (this.toggle == true) {
1093 cls: 'slider-frame roo-button',
1097 'data-on-text':'ON',
1098 'data-off-text':'OFF',
1099 cls: 'slider-button',
1104 // why are we validating the weights?
1105 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1106 cfg.cls += ' ' + this.weight;
1113 cfg.cls += ' close';
1115 cfg["aria-hidden"] = true;
1117 cfg.html = "×";
1123 if (this.theme==='default') {
1124 cfg.cls = 'btn roo-button';
1126 //if (this.parentType != 'Navbar') {
1127 this.weight = this.weight.length ? this.weight : 'default';
1129 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1131 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1132 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1133 cfg.cls += ' btn-' + outline + weight;
1134 if (this.weight == 'default') {
1136 cfg.cls += ' btn-' + this.weight;
1139 } else if (this.theme==='glow') {
1142 cfg.cls = 'btn-glow roo-button';
1144 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1146 cfg.cls += ' ' + this.weight;
1152 this.cls += ' inverse';
1156 if (this.active || this.pressed === true) {
1157 cfg.cls += ' active';
1160 if (this.disabled) {
1161 cfg.disabled = 'disabled';
1165 Roo.log('changing to ul' );
1167 this.glyphicon = 'caret';
1168 if (Roo.bootstrap.version == 4) {
1169 this.fa = 'caret-down';
1174 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1176 //gsRoo.log(this.parentType);
1177 if (this.parentType === 'Navbar' && !this.parent().bar) {
1178 Roo.log('changing to li?');
1187 href : this.href || '#'
1190 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
1191 cfg.cls += ' dropdown';
1198 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
1200 if (this.glyphicon) {
1201 cfg.html = ' ' + cfg.html;
1206 cls: 'glyphicon glyphicon-' + this.glyphicon
1211 cfg.html = ' ' + cfg.html;
1216 cls: 'fa fas fa-' + this.fa
1226 // cfg.cls='btn roo-button';
1230 var value = cfg.html;
1235 cls: 'glyphicon glyphicon-' + this.glyphicon,
1242 cls: 'fa fas fa-' + this.fa,
1247 var bw = this.badge_weight.length ? this.badge_weight :
1248 (this.weight.length ? this.weight : 'secondary');
1249 bw = bw == 'default' ? 'secondary' : bw;
1255 cls: 'badge badge-' + bw,
1264 cfg.cls += ' dropdown';
1265 cfg.html = typeof(cfg.html) != 'undefined' ?
1266 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1269 if (cfg.tag !== 'a' && this.href !== '') {
1270 throw "Tag must be a to set href.";
1271 } else if (this.href.length > 0) {
1272 cfg.href = this.href;
1275 if(this.removeClass){
1280 cfg.target = this.target;
1285 initEvents: function() {
1286 // Roo.log('init events?');
1287 // Roo.log(this.el.dom);
1290 if (typeof (this.menu) != 'undefined') {
1291 this.menu.parentType = this.xtype;
1292 this.menu.triggerEl = this.el;
1293 this.addxtype(Roo.apply({}, this.menu));
1297 if (this.el.hasClass('roo-button')) {
1298 this.el.on('click', this.onClick, this);
1299 this.el.on('dblclick', this.onDblClick, this);
1301 this.el.select('.roo-button').on('click', this.onClick, this);
1302 this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1306 if(this.removeClass){
1307 this.el.on('click', this.onClick, this);
1310 if (this.group === true) {
1311 if (this.pressed === false || this.pressed === true) {
1314 this.pressed = false;
1315 this.setActive(this.pressed);
1320 this.el.enableDisplayMode();
1323 onClick : function(e)
1325 if (this.disabled) {
1329 Roo.log('button on click ');
1330 if(this.preventDefault){
1339 this.setActive(true);
1340 var pi = this.parent().items;
1341 for (var i = 0;i < pi.length;i++) {
1342 if (this == pi[i]) {
1345 if (pi[i].el.hasClass('roo-button')) {
1346 pi[i].setActive(false);
1349 this.fireEvent('click', this, e);
1353 if (this.pressed === true || this.pressed === false) {
1354 this.toggleActive(e);
1358 this.fireEvent('click', this, e);
1360 onDblClick: function(e)
1362 if (this.disabled) {
1365 if(this.preventDefault){
1368 this.fireEvent('dblclick', this, e);
1371 * Enables this button
1375 this.disabled = false;
1376 this.el.removeClass('disabled');
1377 this.el.dom.removeAttribute("disabled");
1381 * Disable this button
1383 disable : function()
1385 this.disabled = true;
1386 this.el.addClass('disabled');
1387 this.el.attr("disabled", "disabled")
1390 * sets the active state on/off,
1391 * @param {Boolean} state (optional) Force a particular state
1393 setActive : function(v) {
1395 this.el[v ? 'addClass' : 'removeClass']('active');
1399 * toggles the current active state
1401 toggleActive : function(e)
1403 this.setActive(!this.pressed); // this modifies pressed...
1404 this.fireEvent('toggle', this, e, this.pressed);
1407 * get the current active state
1408 * @return {boolean} true if it's active
1410 isActive : function()
1412 return this.el.hasClass('active');
1415 * set the text of the first selected button
1417 setText : function(str)
1419 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1422 * get the text of the first selected button
1424 getText : function()
1426 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1429 setWeight : function(str)
1431 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1432 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1434 var outline = this.outline ? 'outline-' : '';
1435 if (str == 'default') {
1436 this.el.addClass('btn-default btn-outline-secondary');
1439 this.el.addClass('btn-' + outline + str);
1444 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1446 Roo.bootstrap.Button.weights = [
1466 * @class Roo.bootstrap.Column
1467 * @extends Roo.bootstrap.Component
1468 * Bootstrap Column class
1469 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1470 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1471 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1472 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1473 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1474 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1475 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1476 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1479 * @cfg {Boolean} hidden (true|false) hide the element
1480 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1481 * @cfg {String} fa (ban|check|...) font awesome icon
1482 * @cfg {Number} fasize (1|2|....) font awsome size
1484 * @cfg {String} icon (info-sign|check|...) glyphicon name
1486 * @cfg {String} html content of column.
1489 * Create a new Column
1490 * @param {Object} config The config object
1493 Roo.bootstrap.Column = function(config){
1494 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1497 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1515 getAutoCreate : function(){
1516 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1524 var sizes = ['xs','sm','md','lg'];
1525 sizes.map(function(size ,ix){
1526 //Roo.log( size + ':' + settings[size]);
1528 if (settings[size+'off'] !== false) {
1529 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1532 if (settings[size] === false) {
1536 if (!settings[size]) { // 0 = hidden
1537 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1539 for (var i = ix; i > -1; i--) {
1540 cfg.cls += ' d-' + sizes[i] + '-none';
1546 cfg.cls += ' col-' + size + '-' + settings[size] + (
1547 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1553 cfg.cls += ' hidden';
1556 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1557 cfg.cls +=' alert alert-' + this.alert;
1561 if (this.html.length) {
1562 cfg.html = this.html;
1566 if (this.fasize > 1) {
1567 fasize = ' fa-' + this.fasize + 'x';
1569 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1574 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1593 * @class Roo.bootstrap.Container
1594 * @extends Roo.bootstrap.Component
1595 * Bootstrap Container class
1596 * @cfg {Boolean} jumbotron is it a jumbotron element
1597 * @cfg {String} html content of element
1598 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1599 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1600 * @cfg {String} header content of header (for panel)
1601 * @cfg {String} footer content of footer (for panel)
1602 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1603 * @cfg {String} tag (header|aside|section) type of HTML tag.
1604 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1605 * @cfg {String} fa font awesome icon
1606 * @cfg {String} icon (info-sign|check|...) glyphicon name
1607 * @cfg {Boolean} hidden (true|false) hide the element
1608 * @cfg {Boolean} expandable (true|false) default false
1609 * @cfg {Boolean} expanded (true|false) default true
1610 * @cfg {String} rheader contet on the right of header
1611 * @cfg {Boolean} clickable (true|false) default false
1615 * Create a new Container
1616 * @param {Object} config The config object
1619 Roo.bootstrap.Container = function(config){
1620 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1626 * After the panel has been expand
1628 * @param {Roo.bootstrap.Container} this
1633 * After the panel has been collapsed
1635 * @param {Roo.bootstrap.Container} this
1640 * When a element is chick
1641 * @param {Roo.bootstrap.Container} this
1642 * @param {Roo.EventObject} e
1648 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1666 getChildContainer : function() {
1672 if (this.panel.length) {
1673 return this.el.select('.panel-body',true).first();
1680 getAutoCreate : function(){
1683 tag : this.tag || 'div',
1687 if (this.jumbotron) {
1688 cfg.cls = 'jumbotron';
1693 // - this is applied by the parent..
1695 // cfg.cls = this.cls + '';
1698 if (this.sticky.length) {
1700 var bd = Roo.get(document.body);
1701 if (!bd.hasClass('bootstrap-sticky')) {
1702 bd.addClass('bootstrap-sticky');
1703 Roo.select('html',true).setStyle('height', '100%');
1706 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1710 if (this.well.length) {
1711 switch (this.well) {
1714 cfg.cls +=' well well-' +this.well;
1723 cfg.cls += ' hidden';
1727 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1728 cfg.cls +=' alert alert-' + this.alert;
1733 if (this.panel.length) {
1734 cfg.cls += ' panel panel-' + this.panel;
1736 if (this.header.length) {
1740 if(this.expandable){
1742 cfg.cls = cfg.cls + ' expandable';
1746 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1754 cls : 'panel-title',
1755 html : (this.expandable ? ' ' : '') + this.header
1759 cls: 'panel-header-right',
1765 cls : 'panel-heading',
1766 style : this.expandable ? 'cursor: pointer' : '',
1774 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1779 if (this.footer.length) {
1781 cls : 'panel-footer',
1790 body.html = this.html || cfg.html;
1791 // prefix with the icons..
1793 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1796 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1801 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1802 cfg.cls = 'container';
1808 initEvents: function()
1810 if(this.expandable){
1811 var headerEl = this.headerEl();
1814 headerEl.on('click', this.onToggleClick, this);
1819 this.el.on('click', this.onClick, this);
1824 onToggleClick : function()
1826 var headerEl = this.headerEl();
1842 if(this.fireEvent('expand', this)) {
1844 this.expanded = true;
1846 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1848 this.el.select('.panel-body',true).first().removeClass('hide');
1850 var toggleEl = this.toggleEl();
1856 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1861 collapse : function()
1863 if(this.fireEvent('collapse', this)) {
1865 this.expanded = false;
1867 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1868 this.el.select('.panel-body',true).first().addClass('hide');
1870 var toggleEl = this.toggleEl();
1876 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1880 toggleEl : function()
1882 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1886 return this.el.select('.panel-heading .fa',true).first();
1889 headerEl : function()
1891 if(!this.el || !this.panel.length || !this.header.length){
1895 return this.el.select('.panel-heading',true).first()
1900 if(!this.el || !this.panel.length){
1904 return this.el.select('.panel-body',true).first()
1907 titleEl : function()
1909 if(!this.el || !this.panel.length || !this.header.length){
1913 return this.el.select('.panel-title',true).first();
1916 setTitle : function(v)
1918 var titleEl = this.titleEl();
1924 titleEl.dom.innerHTML = v;
1927 getTitle : function()
1930 var titleEl = this.titleEl();
1936 return titleEl.dom.innerHTML;
1939 setRightTitle : function(v)
1941 var t = this.el.select('.panel-header-right',true).first();
1947 t.dom.innerHTML = v;
1950 onClick : function(e)
1954 this.fireEvent('click', this, e);
1961 * This is BS4's Card element.. - similar to our containers probably..
1965 * @class Roo.bootstrap.Card
1966 * @extends Roo.bootstrap.Component
1967 * Bootstrap Card class
1970 * possible... may not be implemented..
1971 * @cfg {String} header_image src url of image.
1972 * @cfg {String|Object} header
1973 * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1974 * @cfg {Number} header_weight (primary|secondary|success|info|warning|danger|light|dark)
1976 * @cfg {String} title
1977 * @cfg {String} subtitle
1978 * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1979 * @cfg {String} footer
1981 * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1983 * @cfg {String} margin (0|1|2|3|4|5|auto)
1984 * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1985 * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1986 * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1987 * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1988 * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1989 * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1991 * @cfg {String} padding (0|1|2|3|4|5)
1992 * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1993 * @cfg {String} padding_bottom (0|1|2|3|4|5)
1994 * @cfg {String} padding_left (0|1|2|3|4|5)
1995 * @cfg {String} padding_right (0|1|2|3|4|5)
1996 * @cfg {String} padding_x (0|1|2|3|4|5)
1997 * @cfg {String} padding_y (0|1|2|3|4|5)
1999 * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2000 * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2001 * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2002 * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2003 * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2005 * @config {Boolean} dragable if this card can be dragged.
2006 * @config {String} drag_group group for drag
2007 * @config {Boolean} dropable if this card can recieve other cards being dropped onto it..
2008 * @config {String} drop_group group for drag
2010 * @config {Boolean} collapsable can the body be collapsed.
2011 * @config {Boolean} collapsed is the body collapsed when rendered...
2012 * @config {Boolean} rotateable can the body be rotated by clicking on it..
2013 * @config {Boolean} rotated is the body rotated when rendered...
2016 * Create a new Container
2017 * @param {Object} config The config object
2020 Roo.bootstrap.Card = function(config){
2021 Roo.bootstrap.Card.superclass.constructor.call(this, config);
2027 * When a element a card is dropped
2028 * @param {Roo.bootstrap.Card} this
2031 * @param {Roo.bootstrap.Card} move_card the card being dropped?
2032 * @param {String} position 'above' or 'below'
2033 * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2039 * When a element a card is rotate
2040 * @param {Roo.bootstrap.Card} this
2041 * @param {Roo.Element} n the node being dropped?
2042 * @param {Boolean} rotate status
2047 * When a card element is dragged over ready to drop (return false to block dropable)
2048 * @param {Roo.bootstrap.Card} this
2049 * @param {Object} data from dragdrop
2057 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component, {
2062 margin: '', /// may be better in component?
2092 collapsable : false,
2101 childContainer : false,
2102 dropEl : false, /// the dom placeholde element that indicates drop location.
2103 containerEl: false, // body container
2104 bodyEl: false, // card-body
2105 headerContainerEl : false, //
2107 header_imageEl : false,
2110 layoutCls : function()
2114 Roo.log(this.margin_bottom.length);
2115 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2116 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2118 if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2119 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
2121 if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2122 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
2126 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2127 if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2128 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2132 // more generic support?
2140 // Roo.log("Call onRender: " + this.xtype);
2141 /* We are looking at something like this.
2143 <img src="..." class="card-img-top" alt="...">
2144 <div class="card-body">
2145 <h5 class="card-title">Card title</h5>
2146 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2148 >> this bit is really the body...
2149 <div> << we will ad dthis in hopefully it will not break shit.
2151 ** card text does not actually have any styling...
2153 <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2156 <a href="#" class="card-link">Card link</a>
2159 <div class="card-footer">
2160 <small class="text-muted">Last updated 3 mins ago</small>
2164 getAutoCreate : function(){
2172 if (this.weight.length && this.weight != 'light') {
2173 cfg.cls += ' text-white';
2175 cfg.cls += ' text-dark'; // need as it's nested..
2177 if (this.weight.length) {
2178 cfg.cls += ' bg-' + this.weight;
2181 cfg.cls += ' ' + this.layoutCls();
2184 var hdr_ctr = false;
2185 if (this.header.length) {
2187 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2188 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2196 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2202 if (this.collapsable) {
2205 cls : 'd-block user-select-none',
2209 cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2214 hdr.cn.push(hdr_ctr);
2219 cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2224 if (this.header_image.length) {
2227 cls : 'card-img-top',
2228 src: this.header_image // escape?
2233 cls : 'card-img-top d-none'
2239 cls : 'card-body' + (this.html === false ? ' d-none' : ''),
2243 if (this.collapsable || this.rotateable) {
2246 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2253 if (this.title.length) {
2257 src: this.title // escape?
2261 if (this.subtitle.length) {
2265 src: this.subtitle // escape?
2271 cls : 'roo-card-body-ctr'
2274 if (this.html.length) {
2280 // fixme ? handle objects?
2282 if (this.footer.length) {
2285 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2290 cfg.cn.push({cls : 'card-footer d-none'});
2299 getCardHeader : function()
2301 var ret = this.el.select('.card-header',true).first();
2302 if (ret.hasClass('d-none')) {
2303 ret.removeClass('d-none');
2308 getCardFooter : function()
2310 var ret = this.el.select('.card-footer',true).first();
2311 if (ret.hasClass('d-none')) {
2312 ret.removeClass('d-none');
2317 getCardImageTop : function()
2319 var ret = this.header_imageEl;
2320 if (ret.hasClass('d-none')) {
2321 ret.removeClass('d-none');
2327 getChildContainer : function()
2333 return this.el.select('.roo-card-body-ctr',true).first();
2336 initEvents: function()
2338 this.bodyEl = this.el.select('.card-body',true).first();
2339 this.containerEl = this.getChildContainer();
2341 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2342 containerScroll: true,
2343 ddGroup: this.drag_group || 'default_card_drag_group'
2345 this.dragZone.getDragData = this.getDragData.createDelegate(this);
2347 if (this.dropable) {
2348 this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2349 containerScroll: true,
2350 ddGroup: this.drop_group || 'default_card_drag_group'
2352 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2353 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2354 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2355 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2356 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2359 if (this.collapsable) {
2360 this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2362 if (this.rotateable) {
2363 this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2365 this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2367 this.footerEl = this.el.select('.card-footer',true).first();
2368 this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2369 this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2370 this.headerEl = this.el.select('.card-header',true).first();
2373 this.el.addClass('roo-card-rotated');
2374 this.fireEvent('rotate', this, true);
2376 this.header_imageEl = this.el.select('.card-img-top',true).first();
2377 this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2380 getDragData : function(e)
2382 var target = this.getEl();
2384 //this.handleSelection(e);
2389 nodes: this.getEl(),
2394 dragData.ddel = target.dom ; // the div element
2395 Roo.log(target.getWidth( ));
2396 dragData.ddel.style.width = target.getWidth() + 'px';
2403 * Part of the Roo.dd.DropZone interface. If no target node is found, the
2404 * whole Element becomes the target, and this causes the drop gesture to append.
2406 * Returns an object:
2409 position : 'below' or 'above'
2410 card : relateive to card OBJECT (or true for no cards listed)
2411 items_n : relative to nth item in list
2412 card_n : relative to nth card in list
2417 getTargetFromEvent : function(e, dragged_card_el)
2419 var target = e.getTarget();
2420 while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2421 target = target.parentNode;
2432 //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2433 // see if target is one of the 'cards'...
2436 //Roo.log(this.items.length);
2439 var last_card_n = 0;
2441 for (var i = 0;i< this.items.length;i++) {
2443 if (!this.items[i].el.hasClass('card')) {
2446 pos = this.getDropPoint(e, this.items[i].el.dom);
2448 cards_len = ret.cards.length;
2449 //Roo.log(this.items[i].el.dom.id);
2450 ret.cards.push(this.items[i]);
2452 if (ret.card_n < 0 && pos == 'above') {
2453 ret.position = cards_len > 0 ? 'below' : pos;
2454 ret.items_n = i > 0 ? i - 1 : 0;
2455 ret.card_n = cards_len > 0 ? cards_len - 1 : 0;
2456 ret.card = ret.cards[ret.card_n];
2459 if (!ret.cards.length) {
2461 ret.position = 'below';
2465 // could not find a card.. stick it at the end..
2466 if (ret.card_n < 0) {
2467 ret.card_n = last_card_n;
2468 ret.card = ret.cards[last_card_n];
2469 ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2470 ret.position = 'below';
2473 if (this.items[ret.items_n].el == dragged_card_el) {
2477 if (ret.position == 'below') {
2478 var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2480 if (card_after && card_after.el == dragged_card_el) {
2487 var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2489 if (card_before && card_before.el == dragged_card_el) {
2496 onNodeEnter : function(n, dd, e, data){
2499 onNodeOver : function(n, dd, e, data)
2502 var target_info = this.getTargetFromEvent(e,data.source.el);
2503 if (target_info === false) {
2504 this.dropPlaceHolder('hide');
2507 Roo.log(['getTargetFromEvent', target_info ]);
2510 if (this.fireEvent('cardover', this, [ data ]) === false) {
2514 this.dropPlaceHolder('show', target_info,data);
2518 onNodeOut : function(n, dd, e, data){
2519 this.dropPlaceHolder('hide');
2522 onNodeDrop : function(n, dd, e, data)
2525 // call drop - return false if
2527 // this could actually fail - if the Network drops..
2528 // we will ignore this at present..- client should probably reload
2529 // the whole set of cards if stuff like that fails.
2532 var info = this.getTargetFromEvent(e,data.source.el);
2533 if (info === false) {
2536 this.dropPlaceHolder('hide');
2540 this.acceptCard(data.source, info.position, info.card, info.items_n);
2544 firstChildCard : function()
2546 for (var i = 0;i< this.items.length;i++) {
2548 if (!this.items[i].el.hasClass('card')) {
2551 return this.items[i];
2553 return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2558 * - card.acceptCard(move_card, info.position, info.card, info.items_n);
2560 acceptCard : function(move_card, position, next_to_card )
2562 if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2566 var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2568 move_card.parent().removeCard(move_card);
2571 var dom = move_card.el.dom;
2572 dom.style.width = ''; // clear with - which is set by drag.
2574 if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2575 var cardel = next_to_card.el.dom;
2577 if (position == 'above' ) {
2578 cardel.parentNode.insertBefore(dom, cardel);
2579 } else if (cardel.nextSibling) {
2580 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2582 cardel.parentNode.append(dom);
2585 // card container???
2586 this.containerEl.dom.append(dom);
2589 //FIXME HANDLE card = true
2591 // add this to the correct place in items.
2593 // remove Card from items.
2596 if (this.items.length) {
2598 //Roo.log([info.items_n, info.position, this.items.length]);
2599 for (var i =0; i < this.items.length; i++) {
2600 if (i == to_items_n && position == 'above') {
2601 nitems.push(move_card);
2603 nitems.push(this.items[i]);
2604 if (i == to_items_n && position == 'below') {
2605 nitems.push(move_card);
2608 this.items = nitems;
2609 Roo.log(this.items);
2611 this.items.push(move_card);
2614 move_card.parentId = this.id;
2620 removeCard : function(c)
2622 this.items = this.items.filter(function(e) { return e != c });
2625 dom.parentNode.removeChild(dom);
2626 dom.style.width = ''; // clear with - which is set by drag.
2631 /** Decide whether to drop above or below a View node. */
2632 getDropPoint : function(e, n, dd)
2637 if (n == this.containerEl.dom) {
2640 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2641 var c = t + (b - t) / 2;
2642 var y = Roo.lib.Event.getPageY(e);
2649 onToggleCollapse : function(e)
2651 if (this.collapsed) {
2652 this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2653 this.collapsableEl.addClass('show');
2654 this.collapsed = false;
2657 this.el.select('.roo-collapse-toggle').addClass('collapsed');
2658 this.collapsableEl.removeClass('show');
2659 this.collapsed = true;
2664 onToggleRotate : function(e)
2666 this.collapsableEl.removeClass('show');
2667 this.footerEl.removeClass('d-none');
2668 this.el.removeClass('roo-card-rotated');
2669 this.el.removeClass('d-none');
2672 this.collapsableEl.addClass('show');
2673 this.rotated = false;
2674 this.fireEvent('rotate', this, this.rotated);
2677 this.el.addClass('roo-card-rotated');
2678 this.footerEl.addClass('d-none');
2679 this.el.select('.roo-collapsable').removeClass('show');
2681 this.rotated = true;
2682 this.fireEvent('rotate', this, this.rotated);
2686 dropPlaceHolder: function (action, info, data)
2688 if (this.dropEl === false) {
2689 this.dropEl = Roo.DomHelper.append(this.containerEl, {
2693 this.dropEl.removeClass(['d-none', 'd-block']);
2694 if (action == 'hide') {
2696 this.dropEl.addClass('d-none');
2699 // FIXME - info.card == true!!!
2700 this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2702 if (info.card !== true) {
2703 var cardel = info.card.el.dom;
2705 if (info.position == 'above') {
2706 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2707 } else if (cardel.nextSibling) {
2708 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2710 cardel.parentNode.append(this.dropEl.dom);
2713 // card container???
2714 this.containerEl.dom.append(this.dropEl.dom);
2717 this.dropEl.addClass('d-block roo-card-dropzone');
2719 this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2726 setHeaderText: function(html)
2729 if (this.headerContainerEl) {
2730 this.headerContainerEl.dom.innerHTML = html;
2733 onHeaderImageLoad : function(ev, he)
2735 if (!this.header_image_fit_square) {
2739 var hw = he.naturalHeight / he.naturalWidth;
2742 //var w = he.dom.naturalWidth;
2745 he.style.position = 'relative';
2747 var nw = (ww * (1/hw));
2748 Roo.get(he).setSize( ww * (1/hw), ww);
2749 he.style.left = ((ww - nw)/ 2) + 'px';
2750 he.style.position = 'relative';
2761 * Card header - holder for the card header elements.
2766 * @class Roo.bootstrap.CardHeader
2767 * @extends Roo.bootstrap.Element
2768 * Bootstrap CardHeader class
2770 * Create a new Card Header - that you can embed children into
2771 * @param {Object} config The config object
2774 Roo.bootstrap.CardHeader = function(config){
2775 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2778 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2781 container_method : 'getCardHeader'
2794 * Card footer - holder for the card footer elements.
2799 * @class Roo.bootstrap.CardFooter
2800 * @extends Roo.bootstrap.Element
2801 * Bootstrap CardFooter class
2803 * Create a new Card Footer - that you can embed children into
2804 * @param {Object} config The config object
2807 Roo.bootstrap.CardFooter = function(config){
2808 Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2811 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element, {
2814 container_method : 'getCardFooter'
2827 * Card header - holder for the card header elements.
2832 * @class Roo.bootstrap.CardImageTop
2833 * @extends Roo.bootstrap.Element
2834 * Bootstrap CardImageTop class
2836 * Create a new Card Image Top container
2837 * @param {Object} config The config object
2840 Roo.bootstrap.CardImageTop = function(config){
2841 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2844 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2847 container_method : 'getCardImageTop'
2862 * @class Roo.bootstrap.ButtonUploader
2863 * @extends Roo.bootstrap.Button
2864 * Bootstrap Button Uploader class - it's a button which when you add files to it
2867 * @cfg {Number} errorTimeout default 3000
2868 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
2869 * @cfg {Array} html The button text.
2870 * @cfg {Boolean} multiple (default true) Should the upload allow multiple files to be uploaded.
2873 * Create a new CardUploader
2874 * @param {Object} config The config object
2877 Roo.bootstrap.ButtonUploader = function(config){
2881 Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2887 * @event beforeselect
2888 * When button is pressed, before show upload files dialog is shown
2889 * @param {Roo.bootstrap.UploaderButton} this
2892 'beforeselect' : true,
2894 * @event fired when files have been selected,
2895 * When a the download link is clicked
2896 * @param {Roo.bootstrap.UploaderButton} this
2897 * @param {Array} Array of files that have been uploaded
2904 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button, {
2907 errorTimeout : 3000,
2911 fileCollection : false,
2916 getAutoCreate : function()
2921 cls : 'd-none roo-card-upload-selector'
2924 if (this.multiple) {
2925 im.multiple = 'multiple';
2931 Roo.bootstrap.Button.prototype.getAutoCreate.call(this),
2941 initEvents : function()
2944 Roo.bootstrap.Button.prototype.initEvents.call(this);
2950 this.urlAPI = (window.createObjectURL && window) ||
2951 (window.URL && URL.revokeObjectURL && URL) ||
2952 (window.webkitURL && webkitURL);
2957 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2959 this.selectorEl.on('change', this.onFileSelected, this);
2966 onClick : function(e)
2970 if ( this.fireEvent('beforeselect', this) === false) {
2974 this.selectorEl.dom.click();
2978 onFileSelected : function(e)
2982 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
2985 var files = Array.prototype.slice.call(this.selectorEl.dom.files);
2986 this.selectorEl.dom.value = '';// hopefully reset..
2988 this.fireEvent('uploaded', this, files );
2996 * addCard - add an Attachment to the uploader
2997 * @param data - the data about the image to upload
3001 title : "Title of file",
3002 is_uploaded : false,
3003 src : "http://.....",
3004 srcfile : { the File upload object },
3005 mimetype : file.type,
3008 .. any other data...
3033 * @class Roo.bootstrap.Img
3034 * @extends Roo.bootstrap.Component
3035 * Bootstrap Img class
3036 * @cfg {Boolean} imgResponsive false | true
3037 * @cfg {String} border rounded | circle | thumbnail
3038 * @cfg {String} src image source
3039 * @cfg {String} alt image alternative text
3040 * @cfg {String} href a tag href
3041 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3042 * @cfg {String} xsUrl xs image source
3043 * @cfg {String} smUrl sm image source
3044 * @cfg {String} mdUrl md image source
3045 * @cfg {String} lgUrl lg image source
3046 * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3049 * Create a new Input
3050 * @param {Object} config The config object
3053 Roo.bootstrap.Img = function(config){
3054 Roo.bootstrap.Img.superclass.constructor.call(this, config);
3060 * The img click event for the img.
3061 * @param {Roo.EventObject} e
3066 * The when any image loads
3067 * @param {Roo.EventObject} e
3073 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
3075 imgResponsive: true,
3084 backgroundContain : false,
3086 getAutoCreate : function()
3088 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3089 return this.createSingleImg();
3094 cls: 'roo-image-responsive-group',
3099 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3101 if(!_this[size + 'Url']){
3107 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3108 html: _this.html || cfg.html,
3109 src: _this[size + 'Url']
3112 img.cls += ' roo-image-responsive-' + size;
3114 var s = ['xs', 'sm', 'md', 'lg'];
3116 s.splice(s.indexOf(size), 1);
3118 Roo.each(s, function(ss){
3119 img.cls += ' hidden-' + ss;
3122 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3123 cfg.cls += ' img-' + _this.border;
3127 cfg.alt = _this.alt;
3140 a.target = _this.target;
3144 cfg.cn.push((_this.href) ? a : img);
3151 createSingleImg : function()
3155 cls: (this.imgResponsive) ? 'img-responsive' : '',
3157 src : Roo.BLANK_IMAGE_URL // just incase src get's set to undefined?!?
3160 if (this.backgroundContain) {
3161 cfg.cls += ' background-contain';
3164 cfg.html = this.html || cfg.html;
3166 if (this.backgroundContain) {
3167 cfg.style="background-image: url(" + this.src + ')';
3169 cfg.src = this.src || cfg.src;
3172 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3173 cfg.cls += ' img-' + this.border;
3190 a.target = this.target;
3195 return (this.href) ? a : cfg;
3198 initEvents: function()
3201 this.el.on('click', this.onClick, this);
3203 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3204 this.el.on('load', this.onImageLoad, this);
3206 // not sure if this works.. not tested
3207 this.el.select('img', true).on('load', this.onImageLoad, this);
3212 onClick : function(e)
3214 Roo.log('img onclick');
3215 this.fireEvent('click', this, e);
3217 onImageLoad: function(e)
3219 Roo.log('img load');
3220 this.fireEvent('load', this, e);
3224 * Sets the url of the image - used to update it
3225 * @param {String} url the url of the image
3228 setSrc : function(url)
3232 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3233 if (this.backgroundContain) {
3234 this.el.dom.style.backgroundImage = 'url(' + url + ')';
3236 this.el.dom.src = url;
3241 this.el.select('img', true).first().dom.src = url;
3257 * @class Roo.bootstrap.Link
3258 * @extends Roo.bootstrap.Component
3259 * Bootstrap Link Class
3260 * @cfg {String} alt image alternative text
3261 * @cfg {String} href a tag href
3262 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3263 * @cfg {String} html the content of the link.
3264 * @cfg {String} anchor name for the anchor link
3265 * @cfg {String} fa - favicon
3267 * @cfg {Boolean} preventDefault (true | false) default false
3271 * Create a new Input
3272 * @param {Object} config The config object
3275 Roo.bootstrap.Link = function(config){
3276 Roo.bootstrap.Link.superclass.constructor.call(this, config);
3282 * The img click event for the img.
3283 * @param {Roo.EventObject} e
3289 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
3293 preventDefault: false,
3299 getAutoCreate : function()
3301 var html = this.html || '';
3303 if (this.fa !== false) {
3304 html = '<i class="fa fa-' + this.fa + '"></i>';
3309 // anchor's do not require html/href...
3310 if (this.anchor === false) {
3312 cfg.href = this.href || '#';
3314 cfg.name = this.anchor;
3315 if (this.html !== false || this.fa !== false) {
3318 if (this.href !== false) {
3319 cfg.href = this.href;
3323 if(this.alt !== false){
3328 if(this.target !== false) {
3329 cfg.target = this.target;
3335 initEvents: function() {
3337 if(!this.href || this.preventDefault){
3338 this.el.on('click', this.onClick, this);
3342 onClick : function(e)
3344 if(this.preventDefault){
3347 //Roo.log('img onclick');
3348 this.fireEvent('click', this, e);
3361 * @class Roo.bootstrap.Header
3362 * @extends Roo.bootstrap.Component
3363 * Bootstrap Header class
3364 * @cfg {String} html content of header
3365 * @cfg {Number} level (1|2|3|4|5|6) default 1
3368 * Create a new Header
3369 * @param {Object} config The config object
3373 Roo.bootstrap.Header = function(config){
3374 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3377 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3385 getAutoCreate : function(){
3390 tag: 'h' + (1 *this.level),
3391 html: this.html || ''
3403 * Ext JS Library 1.1.1
3404 * Copyright(c) 2006-2007, Ext JS, LLC.
3406 * Originally Released Under LGPL - original licence link has changed is not relivant.
3409 * <script type="text/javascript">
3413 * @class Roo.bootstrap.MenuMgr
3414 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3417 Roo.bootstrap.MenuMgr = function(){
3418 var menus, active, groups = {}, attached = false, lastShow = new Date();
3420 // private - called when first menu is created
3423 active = new Roo.util.MixedCollection();
3424 Roo.get(document).addKeyListener(27, function(){
3425 if(active.length > 0){
3433 if(active && active.length > 0){
3434 var c = active.clone();
3444 if(active.length < 1){
3445 Roo.get(document).un("mouseup", onMouseDown);
3453 var last = active.last();
3454 lastShow = new Date();
3457 Roo.get(document).on("mouseup", onMouseDown);
3462 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3463 m.parentMenu.activeChild = m;
3464 }else if(last && last.isVisible()){
3465 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3470 function onBeforeHide(m){
3472 m.activeChild.hide();
3474 if(m.autoHideTimer){
3475 clearTimeout(m.autoHideTimer);
3476 delete m.autoHideTimer;
3481 function onBeforeShow(m){
3482 var pm = m.parentMenu;
3483 if(!pm && !m.allowOtherMenus){
3485 }else if(pm && pm.activeChild && active != m){
3486 pm.activeChild.hide();
3490 // private this should really trigger on mouseup..
3491 function onMouseDown(e){
3492 Roo.log("on Mouse Up");
3494 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3495 Roo.log("MenuManager hideAll");
3504 function onBeforeCheck(mi, state){
3506 var g = groups[mi.group];
3507 for(var i = 0, l = g.length; i < l; i++){
3509 g[i].setChecked(false);
3518 * Hides all menus that are currently visible
3520 hideAll : function(){
3525 register : function(menu){
3529 menus[menu.id] = menu;
3530 menu.on("beforehide", onBeforeHide);
3531 menu.on("hide", onHide);
3532 menu.on("beforeshow", onBeforeShow);
3533 menu.on("show", onShow);
3535 if(g && menu.events["checkchange"]){
3539 groups[g].push(menu);
3540 menu.on("checkchange", onCheck);
3545 * Returns a {@link Roo.menu.Menu} object
3546 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3547 * be used to generate and return a new Menu instance.
3549 get : function(menu){
3550 if(typeof menu == "string"){ // menu id
3552 }else if(menu.events){ // menu instance
3555 /*else if(typeof menu.length == 'number'){ // array of menu items?
3556 return new Roo.bootstrap.Menu({items:menu});
3557 }else{ // otherwise, must be a config
3558 return new Roo.bootstrap.Menu(menu);
3565 unregister : function(menu){
3566 delete menus[menu.id];
3567 menu.un("beforehide", onBeforeHide);
3568 menu.un("hide", onHide);
3569 menu.un("beforeshow", onBeforeShow);
3570 menu.un("show", onShow);
3572 if(g && menu.events["checkchange"]){
3573 groups[g].remove(menu);
3574 menu.un("checkchange", onCheck);
3579 registerCheckable : function(menuItem){
3580 var g = menuItem.group;
3585 groups[g].push(menuItem);
3586 menuItem.on("beforecheckchange", onBeforeCheck);
3591 unregisterCheckable : function(menuItem){
3592 var g = menuItem.group;
3594 groups[g].remove(menuItem);
3595 menuItem.un("beforecheckchange", onBeforeCheck);
3607 * @class Roo.bootstrap.Menu
3608 * @extends Roo.bootstrap.Component
3609 * Bootstrap Menu class - container for MenuItems
3610 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3611 * @cfg {bool} hidden if the menu should be hidden when rendered.
3612 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3613 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3614 * @cfg {bool} hideTrigger (true|false) default false - hide the carret for trigger.
3615 * @cfg {String} align default tl-bl? == below - how the menu should be aligned.
3619 * @param {Object} config The config object
3623 Roo.bootstrap.Menu = function(config){
3625 if (config.type == 'treeview') {
3626 // normally menu's are drawn attached to the document to handle layering etc..
3627 // however treeview (used by the docs menu is drawn into the parent element)
3628 this.container_method = 'getChildContainer';
3631 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3632 if (this.registerMenu && this.type != 'treeview') {
3633 Roo.bootstrap.MenuMgr.register(this);
3640 * Fires before this menu is displayed (return false to block)
3641 * @param {Roo.menu.Menu} this
3646 * Fires before this menu is hidden (return false to block)
3647 * @param {Roo.menu.Menu} this
3652 * Fires after this menu is displayed
3653 * @param {Roo.menu.Menu} this
3658 * Fires after this menu is hidden
3659 * @param {Roo.menu.Menu} this
3664 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3665 * @param {Roo.menu.Menu} this
3666 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3667 * @param {Roo.EventObject} e
3672 * Fires when the mouse is hovering over this menu
3673 * @param {Roo.menu.Menu} this
3674 * @param {Roo.EventObject} e
3675 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3680 * Fires when the mouse exits this menu
3681 * @param {Roo.menu.Menu} this
3682 * @param {Roo.EventObject} e
3683 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3688 * Fires when a menu item contained in this menu is clicked
3689 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3690 * @param {Roo.EventObject} e
3694 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3697 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
3701 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3704 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3706 registerMenu : true,
3708 menuItems :false, // stores the menu items..
3718 container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3720 hideTrigger : false,
3725 getChildContainer : function() {
3729 getAutoCreate : function(){
3731 //if (['right'].indexOf(this.align)!==-1) {
3732 // cfg.cn[1].cls += ' pull-right'
3737 cls : 'dropdown-menu shadow' ,
3738 style : 'z-index:1000'
3742 if (this.type === 'submenu') {
3743 cfg.cls = 'submenu active';
3745 if (this.type === 'treeview') {
3746 cfg.cls = 'treeview-menu';
3751 initEvents : function() {
3753 // Roo.log("ADD event");
3754 // Roo.log(this.triggerEl.dom);
3755 if (this.triggerEl) {
3757 this.triggerEl.on('click', this.onTriggerClick, this);
3759 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3761 if (!this.hideTrigger) {
3762 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3763 // dropdown toggle on the 'a' in BS4?
3764 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3766 this.triggerEl.addClass('dropdown-toggle');
3772 this.el.on('touchstart' , this.onTouch, this);
3774 this.el.on('click' , this.onClick, this);
3776 this.el.on("mouseover", this.onMouseOver, this);
3777 this.el.on("mouseout", this.onMouseOut, this);
3781 findTargetItem : function(e)
3783 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3787 //Roo.log(t); Roo.log(t.id);
3789 //Roo.log(this.menuitems);
3790 return this.menuitems.get(t.id);
3792 //return this.items.get(t.menuItemId);
3798 onTouch : function(e)
3800 Roo.log("menu.onTouch");
3801 //e.stopEvent(); this make the user popdown broken
3805 onClick : function(e)
3807 Roo.log("menu.onClick");
3809 var t = this.findTargetItem(e);
3810 if(!t || t.isContainer){
3815 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3816 if(t == this.activeItem && t.shouldDeactivate(e)){
3817 this.activeItem.deactivate();
3818 delete this.activeItem;
3822 this.setActiveItem(t, true);
3830 Roo.log('pass click event');
3834 this.fireEvent("click", this, t, e);
3838 if(!t.href.length || t.href == '#'){
3839 (function() { _this.hide(); }).defer(100);
3844 onMouseOver : function(e){
3845 var t = this.findTargetItem(e);
3848 // if(t.canActivate && !t.disabled){
3849 // this.setActiveItem(t, true);
3853 this.fireEvent("mouseover", this, e, t);
3855 isVisible : function(){
3856 return !this.hidden;
3858 onMouseOut : function(e){
3859 var t = this.findTargetItem(e);
3862 // if(t == this.activeItem && t.shouldDeactivate(e)){
3863 // this.activeItem.deactivate();
3864 // delete this.activeItem;
3867 this.fireEvent("mouseout", this, e, t);
3872 * Displays this menu relative to another element
3873 * @param {String/HTMLElement/Roo.Element} element The element to align to
3874 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3875 * the element (defaults to this.defaultAlign)
3876 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3878 show : function(el, pos, parentMenu)
3880 if (false === this.fireEvent("beforeshow", this)) {
3881 Roo.log("show canceled");
3884 this.parentMenu = parentMenu;
3888 this.el.addClass('show'); // show otherwise we do not know how big we are..
3890 var xy = this.el.getAlignToXY(el, pos);
3892 // bl-tl << left align below
3893 // tl-bl << left align
3895 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3896 // if it goes to far to the right.. -> align left.
3897 xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3900 // was left align - go right?
3901 xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3904 // goes down the bottom
3905 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3907 var a = this.align.replace('?', '').split('-');
3908 xy = this.el.getAlignToXY(el, a[1] + '-' + a[0] + '?')
3912 this.showAt( xy , parentMenu, false);
3915 * Displays this menu at a specific xy position
3916 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3917 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3919 showAt : function(xy, parentMenu, /* private: */_e){
3920 this.parentMenu = parentMenu;
3925 this.fireEvent("beforeshow", this);
3926 //xy = this.el.adjustForConstraints(xy);
3930 this.hideMenuItems();
3931 this.hidden = false;
3932 if (this.triggerEl) {
3933 this.triggerEl.addClass('open');
3936 this.el.addClass('show');
3940 // reassign x when hitting right
3942 // reassign y when hitting bottom
3944 // but the list may align on trigger left or trigger top... should it be a properity?
3946 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3951 this.fireEvent("show", this);
3957 this.doFocus.defer(50, this);
3961 doFocus : function(){
3963 this.focusEl.focus();
3968 * Hides this menu and optionally all parent menus
3969 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3971 hide : function(deep)
3973 if (false === this.fireEvent("beforehide", this)) {
3974 Roo.log("hide canceled");
3977 this.hideMenuItems();
3978 if(this.el && this.isVisible()){
3980 if(this.activeItem){
3981 this.activeItem.deactivate();
3982 this.activeItem = null;
3984 if (this.triggerEl) {
3985 this.triggerEl.removeClass('open');
3988 this.el.removeClass('show');
3990 this.fireEvent("hide", this);
3992 if(deep === true && this.parentMenu){
3993 this.parentMenu.hide(true);
3997 onTriggerClick : function(e)
3999 Roo.log('trigger click');
4001 var target = e.getTarget();
4003 Roo.log(target.nodeName.toLowerCase());
4005 if(target.nodeName.toLowerCase() === 'i'){
4011 onTriggerPress : function(e)
4013 Roo.log('trigger press');
4014 //Roo.log(e.getTarget());
4015 // Roo.log(this.triggerEl.dom);
4017 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4018 var pel = Roo.get(e.getTarget());
4019 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4020 Roo.log('is treeview or dropdown?');
4024 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4028 if (this.isVisible()) {
4034 this.show(this.triggerEl, this.align, false);
4037 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4044 hideMenuItems : function()
4046 Roo.log("hide Menu Items");
4051 this.el.select('.open',true).each(function(aa) {
4053 aa.removeClass('open');
4057 addxtypeChild : function (tree, cntr) {
4058 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4060 this.menuitems.add(comp);
4072 this.getEl().dom.innerHTML = '';
4073 this.menuitems.clear();
4087 * @class Roo.bootstrap.MenuItem
4088 * @extends Roo.bootstrap.Component
4089 * Bootstrap MenuItem class
4090 * @cfg {String} html the menu label
4091 * @cfg {String} href the link
4092 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4093 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4094 * @cfg {Boolean} active used on sidebars to highlight active itesm
4095 * @cfg {String} fa favicon to show on left of menu item.
4096 * @cfg {Roo.bootsrap.Menu} menu the child menu.
4100 * Create a new MenuItem
4101 * @param {Object} config The config object
4105 Roo.bootstrap.MenuItem = function(config){
4106 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4111 * The raw click event for the entire grid.
4112 * @param {Roo.bootstrap.MenuItem} this
4113 * @param {Roo.EventObject} e
4119 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
4123 preventDefault: false,
4124 isContainer : false,
4128 getAutoCreate : function(){
4130 if(this.isContainer){
4133 cls: 'dropdown-menu-item '
4143 cls : 'dropdown-item',
4148 if (this.fa !== false) {
4151 cls : 'fa fa-' + this.fa
4160 cls: 'dropdown-menu-item',
4163 if (this.parent().type == 'treeview') {
4164 cfg.cls = 'treeview-menu';
4167 cfg.cls += ' active';
4172 anc.href = this.href || cfg.cn[0].href ;
4173 ctag.html = this.html || cfg.cn[0].html ;
4177 initEvents: function()
4179 if (this.parent().type == 'treeview') {
4180 this.el.select('a').on('click', this.onClick, this);
4184 this.menu.parentType = this.xtype;
4185 this.menu.triggerEl = this.el;
4186 this.menu = this.addxtype(Roo.apply({}, this.menu));
4190 onClick : function(e)
4192 Roo.log('item on click ');
4194 if(this.preventDefault){
4197 //this.parent().hideMenuItems();
4199 this.fireEvent('click', this, e);
4218 * @class Roo.bootstrap.MenuSeparator
4219 * @extends Roo.bootstrap.Component
4220 * Bootstrap MenuSeparator class
4223 * Create a new MenuItem
4224 * @param {Object} config The config object
4228 Roo.bootstrap.MenuSeparator = function(config){
4229 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4232 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
4234 getAutoCreate : function(){
4253 * @class Roo.bootstrap.Modal
4254 * @extends Roo.bootstrap.Component
4255 * Bootstrap Modal class
4256 * @cfg {String} title Title of dialog
4257 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4258 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
4259 * @cfg {Boolean} specificTitle default false
4260 * @cfg {Array} buttons Array of buttons or standard button set..
4261 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4262 * @cfg {Boolean} animate default true
4263 * @cfg {Boolean} allow_close default true
4264 * @cfg {Boolean} fitwindow default false
4265 * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4266 * @cfg {Number} width fixed width - usefull for chrome extension only really.
4267 * @cfg {Number} height fixed height - usefull for chrome extension only really.
4268 * @cfg {String} size (sm|lg|xl) default empty
4269 * @cfg {Number} max_width set the max width of modal
4270 * @cfg {Boolean} editableTitle can the title be edited
4275 * Create a new Modal Dialog
4276 * @param {Object} config The config object
4279 Roo.bootstrap.Modal = function(config){
4280 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4285 * The raw btnclick event for the button
4286 * @param {Roo.EventObject} e
4291 * Fire when dialog resize
4292 * @param {Roo.bootstrap.Modal} this
4293 * @param {Roo.EventObject} e
4297 * @event titlechanged
4298 * Fire when the editable title has been changed
4299 * @param {Roo.bootstrap.Modal} this
4300 * @param {Roo.EventObject} value
4302 "titlechanged" : true
4305 this.buttons = this.buttons || [];
4308 this.tmpl = Roo.factory(this.tmpl);
4313 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
4315 title : 'test dialog',
4325 specificTitle: false,
4327 buttonPosition: 'right',
4349 editableTitle : false,
4351 onRender : function(ct, position)
4353 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4356 var cfg = Roo.apply({}, this.getAutoCreate());
4359 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4361 //if (!cfg.name.length) {
4365 cfg.cls += ' ' + this.cls;
4368 cfg.style = this.style;
4370 this.el = Roo.get(document.body).createChild(cfg, position);
4372 //var type = this.el.dom.type;
4375 if(this.tabIndex !== undefined){
4376 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4379 this.dialogEl = this.el.select('.modal-dialog',true).first();
4380 this.bodyEl = this.el.select('.modal-body',true).first();
4381 this.closeEl = this.el.select('.modal-header .close', true).first();
4382 this.headerEl = this.el.select('.modal-header',true).first();
4383 this.titleEl = this.el.select('.modal-title',true).first();
4384 this.footerEl = this.el.select('.modal-footer',true).first();
4386 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4388 //this.el.addClass("x-dlg-modal");
4390 if (this.buttons.length) {
4391 Roo.each(this.buttons, function(bb) {
4392 var b = Roo.apply({}, bb);
4393 b.xns = b.xns || Roo.bootstrap;
4394 b.xtype = b.xtype || 'Button';
4395 if (typeof(b.listeners) == 'undefined') {
4396 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4399 var btn = Roo.factory(b);
4401 btn.render(this.getButtonContainer());
4405 // render the children.
4408 if(typeof(this.items) != 'undefined'){
4409 var items = this.items;
4412 for(var i =0;i < items.length;i++) {
4413 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4417 this.items = nitems;
4419 // where are these used - they used to be body/close/footer
4423 //this.el.addClass([this.fieldClass, this.cls]);
4427 getAutoCreate : function()
4429 // we will default to modal-body-overflow - might need to remove or make optional later.
4431 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''),
4432 html : this.html || ''
4437 cls : 'modal-title',
4441 if(this.specificTitle){ // WTF is this?
4446 if (this.allow_close && Roo.bootstrap.version == 3) {
4456 if (this.editableTitle) {
4458 cls: 'form-control roo-editable-title d-none',
4464 if (this.allow_close && Roo.bootstrap.version == 4) {
4474 if(this.size.length){
4475 size = 'modal-' + this.size;
4478 var footer = Roo.bootstrap.version == 3 ?
4480 cls : 'modal-footer',
4484 cls: 'btn-' + this.buttonPosition
4489 { // BS4 uses mr-auto on left buttons....
4490 cls : 'modal-footer'
4501 cls: "modal-dialog " + size,
4504 cls : "modal-content",
4507 cls : 'modal-header',
4522 modal.cls += ' fade';
4528 getChildContainer : function() {
4533 getButtonContainer : function() {
4535 return Roo.bootstrap.version == 4 ?
4536 this.el.select('.modal-footer',true).first()
4537 : this.el.select('.modal-footer div',true).first();
4540 initEvents : function()
4542 if (this.allow_close) {
4543 this.closeEl.on('click', this.hide, this);
4545 Roo.EventManager.onWindowResize(this.resize, this, true);
4546 if (this.editableTitle) {
4547 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4548 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4549 this.headerEditEl.on('keyup', function(e) {
4550 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4551 this.toggleHeaderInput(false)
4554 this.headerEditEl.on('blur', function(e) {
4555 this.toggleHeaderInput(false)
4564 this.maskEl.setSize(
4565 Roo.lib.Dom.getViewWidth(true),
4566 Roo.lib.Dom.getViewHeight(true)
4569 if (this.fitwindow) {
4571 this.dialogEl.setStyle( { 'max-width' : '100%' });
4573 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4574 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4579 if(this.max_width !== 0) {
4581 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4584 this.setSize(w, this.height);
4588 if(this.max_height) {
4589 this.setSize(w,Math.min(
4591 Roo.lib.Dom.getViewportHeight(true) - 60
4597 if(!this.fit_content) {
4598 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4602 this.setSize(w, Math.min(
4604 this.headerEl.getHeight() +
4605 this.footerEl.getHeight() +
4606 this.getChildHeight(this.bodyEl.dom.childNodes),
4607 Roo.lib.Dom.getViewportHeight(true) - 60)
4613 setSize : function(w,h)
4624 if (!this.rendered) {
4627 this.toggleHeaderInput(false);
4628 //this.el.setStyle('display', 'block');
4629 this.el.removeClass('hideing');
4630 this.el.dom.style.display='block';
4632 Roo.get(document.body).addClass('modal-open');
4634 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4637 this.el.addClass('show');
4638 this.el.addClass('in');
4641 this.el.addClass('show');
4642 this.el.addClass('in');
4645 // not sure how we can show data in here..
4647 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4650 Roo.get(document.body).addClass("x-body-masked");
4652 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4653 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4654 this.maskEl.dom.style.display = 'block';
4655 this.maskEl.addClass('show');
4660 this.fireEvent('show', this);
4662 // set zindex here - otherwise it appears to be ignored...
4663 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4666 this.items.forEach( function(e) {
4667 e.layout ? e.layout() : false;
4675 if(this.fireEvent("beforehide", this) !== false){
4677 this.maskEl.removeClass('show');
4679 this.maskEl.dom.style.display = '';
4680 Roo.get(document.body).removeClass("x-body-masked");
4681 this.el.removeClass('in');
4682 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4684 if(this.animate){ // why
4685 this.el.addClass('hideing');
4686 this.el.removeClass('show');
4688 if (!this.el.hasClass('hideing')) {
4689 return; // it's been shown again...
4692 this.el.dom.style.display='';
4694 Roo.get(document.body).removeClass('modal-open');
4695 this.el.removeClass('hideing');
4699 this.el.removeClass('show');
4700 this.el.dom.style.display='';
4701 Roo.get(document.body).removeClass('modal-open');
4704 this.fireEvent('hide', this);
4707 isVisible : function()
4710 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4714 addButton : function(str, cb)
4718 var b = Roo.apply({}, { html : str } );
4719 b.xns = b.xns || Roo.bootstrap;
4720 b.xtype = b.xtype || 'Button';
4721 if (typeof(b.listeners) == 'undefined') {
4722 b.listeners = { click : cb.createDelegate(this) };
4725 var btn = Roo.factory(b);
4727 btn.render(this.getButtonContainer());
4733 setDefaultButton : function(btn)
4735 //this.el.select('.modal-footer').()
4738 resizeTo: function(w,h)
4740 this.dialogEl.setWidth(w);
4742 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4744 this.bodyEl.setHeight(h - diff);
4746 this.fireEvent('resize', this);
4749 setContentSize : function(w, h)
4753 onButtonClick: function(btn,e)
4756 this.fireEvent('btnclick', btn.name, e);
4759 * Set the title of the Dialog
4760 * @param {String} str new Title
4762 setTitle: function(str) {
4763 this.titleEl.dom.innerHTML = str;
4767 * Set the body of the Dialog
4768 * @param {String} str new Title
4770 setBody: function(str) {
4771 this.bodyEl.dom.innerHTML = str;
4774 * Set the body of the Dialog using the template
4775 * @param {Obj} data - apply this data to the template and replace the body contents.
4777 applyBody: function(obj)
4780 Roo.log("Error - using apply Body without a template");
4783 this.tmpl.overwrite(this.bodyEl, obj);
4786 getChildHeight : function(child_nodes)
4790 child_nodes.length == 0
4795 var child_height = 0;
4797 for(var i = 0; i < child_nodes.length; i++) {
4800 * for modal with tabs...
4801 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4803 var layout_childs = child_nodes[i].childNodes;
4805 for(var j = 0; j < layout_childs.length; j++) {
4807 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4809 var layout_body_childs = layout_childs[j].childNodes;
4811 for(var k = 0; k < layout_body_childs.length; k++) {
4813 if(layout_body_childs[k].classList.contains('navbar')) {
4814 child_height += layout_body_childs[k].offsetHeight;
4818 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4820 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4822 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4824 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4825 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4840 child_height += child_nodes[i].offsetHeight;
4841 // Roo.log(child_nodes[i].offsetHeight);
4844 return child_height;
4846 toggleHeaderInput : function(is_edit)
4848 if (!this.editableTitle) {
4849 return; // not editable.
4851 if (is_edit && this.is_header_editing) {
4852 return; // already editing..
4856 this.headerEditEl.dom.value = this.title;
4857 this.headerEditEl.removeClass('d-none');
4858 this.headerEditEl.dom.focus();
4859 this.titleEl.addClass('d-none');
4861 this.is_header_editing = true;
4864 // flip back to not editing.
4865 this.title = this.headerEditEl.dom.value;
4866 this.headerEditEl.addClass('d-none');
4867 this.titleEl.removeClass('d-none');
4868 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4869 this.is_header_editing = false;
4870 this.fireEvent('titlechanged', this, this.title);
4879 Roo.apply(Roo.bootstrap.Modal, {
4881 * Button config that displays a single OK button
4890 * Button config that displays Yes and No buttons
4906 * Button config that displays OK and Cancel buttons
4921 * Button config that displays Yes, No and Cancel buttons
4946 * messagebox - can be used as a replace
4950 * @class Roo.MessageBox
4951 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4955 Roo.Msg.alert('Status', 'Changes saved successfully.');
4957 // Prompt for user data:
4958 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4960 // process text value...
4964 // Show a dialog using config options:
4966 title:'Save Changes?',
4967 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4968 buttons: Roo.Msg.YESNOCANCEL,
4975 Roo.bootstrap.MessageBox = function(){
4976 var dlg, opt, mask, waitTimer;
4977 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4978 var buttons, activeTextEl, bwidth;
4982 var handleButton = function(button){
4984 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4988 var handleHide = function(){
4990 dlg.el.removeClass(opt.cls);
4993 // Roo.TaskMgr.stop(waitTimer);
4994 // waitTimer = null;
4999 var updateButtons = function(b){
5002 buttons["ok"].hide();
5003 buttons["cancel"].hide();
5004 buttons["yes"].hide();
5005 buttons["no"].hide();
5006 dlg.footerEl.hide();
5010 dlg.footerEl.show();
5011 for(var k in buttons){
5012 if(typeof buttons[k] != "function"){
5015 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5016 width += buttons[k].el.getWidth()+15;
5026 var handleEsc = function(d, k, e){
5027 if(opt && opt.closable !== false){
5037 * Returns a reference to the underlying {@link Roo.BasicDialog} element
5038 * @return {Roo.BasicDialog} The BasicDialog element
5040 getDialog : function(){
5042 dlg = new Roo.bootstrap.Modal( {
5045 //constraintoviewport:false,
5047 //collapsible : false,
5052 //buttonAlign:"center",
5053 closeClick : function(){
5054 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5057 handleButton("cancel");
5062 dlg.on("hide", handleHide);
5064 //dlg.addKeyListener(27, handleEsc);
5066 this.buttons = buttons;
5067 var bt = this.buttonText;
5068 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5069 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5070 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5071 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5073 bodyEl = dlg.bodyEl.createChild({
5075 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5076 '<textarea class="roo-mb-textarea"></textarea>' +
5077 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
5079 msgEl = bodyEl.dom.firstChild;
5080 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5081 textboxEl.enableDisplayMode();
5082 textboxEl.addKeyListener([10,13], function(){
5083 if(dlg.isVisible() && opt && opt.buttons){
5086 }else if(opt.buttons.yes){
5087 handleButton("yes");
5091 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5092 textareaEl.enableDisplayMode();
5093 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5094 progressEl.enableDisplayMode();
5096 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5097 var pf = progressEl.dom.firstChild;
5099 pp = Roo.get(pf.firstChild);
5100 pp.setHeight(pf.offsetHeight);
5108 * Updates the message box body text
5109 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5110 * the XHTML-compliant non-breaking space character '&#160;')
5111 * @return {Roo.MessageBox} This message box
5113 updateText : function(text)
5115 if(!dlg.isVisible() && !opt.width){
5116 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5117 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5119 msgEl.innerHTML = text || ' ';
5121 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5122 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5124 Math.min(opt.width || cw , this.maxWidth),
5125 Math.max(opt.minWidth || this.minWidth, bwidth)
5128 activeTextEl.setWidth(w);
5130 if(dlg.isVisible()){
5131 dlg.fixedcenter = false;
5133 // to big, make it scroll. = But as usual stupid IE does not support
5136 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5137 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5138 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5140 bodyEl.dom.style.height = '';
5141 bodyEl.dom.style.overflowY = '';
5144 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5146 bodyEl.dom.style.overflowX = '';
5149 dlg.setContentSize(w, bodyEl.getHeight());
5150 if(dlg.isVisible()){
5151 dlg.fixedcenter = true;
5157 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
5158 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5159 * @param {Number} value Any number between 0 and 1 (e.g., .5)
5160 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5161 * @return {Roo.MessageBox} This message box
5163 updateProgress : function(value, text){
5165 this.updateText(text);
5168 if (pp) { // weird bug on my firefox - for some reason this is not defined
5169 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5170 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5176 * Returns true if the message box is currently displayed
5177 * @return {Boolean} True if the message box is visible, else false
5179 isVisible : function(){
5180 return dlg && dlg.isVisible();
5184 * Hides the message box if it is displayed
5187 if(this.isVisible()){
5193 * Displays a new message box, or reinitializes an existing message box, based on the config options
5194 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5195 * The following config object properties are supported:
5197 Property Type Description
5198 ---------- --------------- ------------------------------------------------------------------------------------
5199 animEl String/Element An id or Element from which the message box should animate as it opens and
5200 closes (defaults to undefined)
5201 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5202 cancel:'Bar'}), or false to not show any buttons (defaults to false)
5203 closable Boolean False to hide the top-right close button (defaults to true). Note that
5204 progress and wait dialogs will ignore this property and always hide the
5205 close button as they can only be closed programmatically.
5206 cls String A custom CSS class to apply to the message box element
5207 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
5208 displayed (defaults to 75)
5209 fn Function A callback function to execute after closing the dialog. The arguments to the
5210 function will be btn (the name of the button that was clicked, if applicable,
5211 e.g. "ok"), and text (the value of the active text field, if applicable).
5212 Progress and wait dialogs will ignore this option since they do not respond to
5213 user actions and can only be closed programmatically, so any required function
5214 should be called by the same code after it closes the dialog.
5215 icon String A CSS class that provides a background image to be used as an icon for
5216 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5217 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
5218 minWidth Number The minimum width in pixels of the message box (defaults to 100)
5219 modal Boolean False to allow user interaction with the page while the message box is
5220 displayed (defaults to true)
5221 msg String A string that will replace the existing message box body text (defaults
5222 to the XHTML-compliant non-breaking space character ' ')
5223 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
5224 progress Boolean True to display a progress bar (defaults to false)
5225 progressText String The text to display inside the progress bar if progress = true (defaults to '')
5226 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
5227 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
5228 title String The title text
5229 value String The string value to set into the active textbox element if displayed
5230 wait Boolean True to display a progress bar (defaults to false)
5231 width Number The width of the dialog in pixels
5238 msg: 'Please enter your address:',
5240 buttons: Roo.MessageBox.OKCANCEL,
5243 animEl: 'addAddressBtn'
5246 * @param {Object} config Configuration options
5247 * @return {Roo.MessageBox} This message box
5249 show : function(options)
5252 // this causes nightmares if you show one dialog after another
5253 // especially on callbacks..
5255 if(this.isVisible()){
5258 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5259 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
5260 Roo.log("New Dialog Message:" + options.msg )
5261 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5262 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5265 var d = this.getDialog();
5267 d.setTitle(opt.title || " ");
5268 d.closeEl.setDisplayed(opt.closable !== false);
5269 activeTextEl = textboxEl;
5270 opt.prompt = opt.prompt || (opt.multiline ? true : false);
5275 textareaEl.setHeight(typeof opt.multiline == "number" ?
5276 opt.multiline : this.defaultTextHeight);
5277 activeTextEl = textareaEl;
5286 progressEl.setDisplayed(opt.progress === true);
5288 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5290 this.updateProgress(0);
5291 activeTextEl.dom.value = opt.value || "";
5293 dlg.setDefaultButton(activeTextEl);
5295 var bs = opt.buttons;
5299 }else if(bs && bs.yes){
5300 db = buttons["yes"];
5302 dlg.setDefaultButton(db);
5304 bwidth = updateButtons(opt.buttons);
5305 this.updateText(opt.msg);
5307 d.el.addClass(opt.cls);
5309 d.proxyDrag = opt.proxyDrag === true;
5310 d.modal = opt.modal !== false;
5311 d.mask = opt.modal !== false ? mask : false;
5313 // force it to the end of the z-index stack so it gets a cursor in FF
5314 document.body.appendChild(dlg.el.dom);
5315 d.animateTarget = null;
5316 d.show(options.animEl);
5322 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5323 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5324 * and closing the message box when the process is complete.
5325 * @param {String} title The title bar text
5326 * @param {String} msg The message box body text
5327 * @return {Roo.MessageBox} This message box
5329 progress : function(title, msg){
5336 minWidth: this.minProgressWidth,
5343 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5344 * If a callback function is passed it will be called after the user clicks the button, and the
5345 * id of the button that was clicked will be passed as the only parameter to the callback
5346 * (could also be the top-right close button).
5347 * @param {String} title The title bar text
5348 * @param {String} msg The message box body text
5349 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5350 * @param {Object} scope (optional) The scope of the callback function
5351 * @return {Roo.MessageBox} This message box
5353 alert : function(title, msg, fn, scope)
5368 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5369 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5370 * You are responsible for closing the message box when the process is complete.
5371 * @param {String} msg The message box body text
5372 * @param {String} title (optional) The title bar text
5373 * @return {Roo.MessageBox} This message box
5375 wait : function(msg, title){
5386 waitTimer = Roo.TaskMgr.start({
5388 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5396 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5397 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5398 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5399 * @param {String} title The title bar text
5400 * @param {String} msg The message box body text
5401 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5402 * @param {Object} scope (optional) The scope of the callback function
5403 * @return {Roo.MessageBox} This message box
5405 confirm : function(title, msg, fn, scope){
5409 buttons: this.YESNO,
5418 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5419 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5420 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5421 * (could also be the top-right close button) and the text that was entered will be passed as the two
5422 * parameters to the callback.
5423 * @param {String} title The title bar text
5424 * @param {String} msg The message box body text
5425 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5426 * @param {Object} scope (optional) The scope of the callback function
5427 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5428 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5429 * @return {Roo.MessageBox} This message box
5431 prompt : function(title, msg, fn, scope, multiline){
5435 buttons: this.OKCANCEL,
5440 multiline: multiline,
5447 * Button config that displays a single OK button
5452 * Button config that displays Yes and No buttons
5455 YESNO : {yes:true, no:true},
5457 * Button config that displays OK and Cancel buttons
5460 OKCANCEL : {ok:true, cancel:true},
5462 * Button config that displays Yes, No and Cancel buttons
5465 YESNOCANCEL : {yes:true, no:true, cancel:true},
5468 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5471 defaultTextHeight : 75,
5473 * The maximum width in pixels of the message box (defaults to 600)
5478 * The minimum width in pixels of the message box (defaults to 100)
5483 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5484 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5487 minProgressWidth : 250,
5489 * An object containing the default button text strings that can be overriden for localized language support.
5490 * Supported properties are: ok, cancel, yes and no.
5491 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5504 * Shorthand for {@link Roo.MessageBox}
5506 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5507 Roo.Msg = Roo.Msg || Roo.MessageBox;
5516 * @class Roo.bootstrap.Navbar
5517 * @extends Roo.bootstrap.Component
5518 * Bootstrap Navbar class
5521 * Create a new Navbar
5522 * @param {Object} config The config object
5526 Roo.bootstrap.Navbar = function(config){
5527 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5531 * @event beforetoggle
5532 * Fire before toggle the menu
5533 * @param {Roo.EventObject} e
5535 "beforetoggle" : true
5539 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
5548 getAutoCreate : function(){
5551 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5555 initEvents :function ()
5557 //Roo.log(this.el.select('.navbar-toggle',true));
5558 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5565 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5567 var size = this.el.getSize();
5568 this.maskEl.setSize(size.width, size.height);
5569 this.maskEl.enableDisplayMode("block");
5578 getChildContainer : function()
5580 if (this.el && this.el.select('.collapse').getCount()) {
5581 return this.el.select('.collapse',true).first();
5596 onToggle : function()
5599 if(this.fireEvent('beforetoggle', this) === false){
5602 var ce = this.el.select('.navbar-collapse',true).first();
5604 if (!ce.hasClass('show')) {
5614 * Expand the navbar pulldown
5616 expand : function ()
5619 var ce = this.el.select('.navbar-collapse',true).first();
5620 if (ce.hasClass('collapsing')) {
5623 ce.dom.style.height = '';
5625 ce.addClass('in'); // old...
5626 ce.removeClass('collapse');
5627 ce.addClass('show');
5628 var h = ce.getHeight();
5630 ce.removeClass('show');
5631 // at this point we should be able to see it..
5632 ce.addClass('collapsing');
5634 ce.setHeight(0); // resize it ...
5635 ce.on('transitionend', function() {
5636 //Roo.log('done transition');
5637 ce.removeClass('collapsing');
5638 ce.addClass('show');
5639 ce.removeClass('collapse');
5641 ce.dom.style.height = '';
5642 }, this, { single: true} );
5644 ce.dom.scrollTop = 0;
5647 * Collapse the navbar pulldown
5649 collapse : function()
5651 var ce = this.el.select('.navbar-collapse',true).first();
5653 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5654 // it's collapsed or collapsing..
5657 ce.removeClass('in'); // old...
5658 ce.setHeight(ce.getHeight());
5659 ce.removeClass('show');
5660 ce.addClass('collapsing');
5662 ce.on('transitionend', function() {
5663 ce.dom.style.height = '';
5664 ce.removeClass('collapsing');
5665 ce.addClass('collapse');
5666 }, this, { single: true} );
5686 * @class Roo.bootstrap.NavSimplebar
5687 * @extends Roo.bootstrap.Navbar
5688 * Bootstrap Sidebar class
5690 * @cfg {Boolean} inverse is inverted color
5692 * @cfg {String} type (nav | pills | tabs)
5693 * @cfg {Boolean} arrangement stacked | justified
5694 * @cfg {String} align (left | right) alignment
5696 * @cfg {Boolean} main (true|false) main nav bar? default false
5697 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5699 * @cfg {String} tag (header|footer|nav|div) default is nav
5701 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5705 * Create a new Sidebar
5706 * @param {Object} config The config object
5710 Roo.bootstrap.NavSimplebar = function(config){
5711 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5714 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5730 getAutoCreate : function(){
5734 tag : this.tag || 'div',
5735 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5737 if (['light','white'].indexOf(this.weight) > -1) {
5738 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5740 cfg.cls += ' bg-' + this.weight;
5743 cfg.cls += ' navbar-inverse';
5747 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5749 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5758 cls: 'nav nav-' + this.xtype,
5764 this.type = this.type || 'nav';
5765 if (['tabs','pills'].indexOf(this.type) != -1) {
5766 cfg.cn[0].cls += ' nav-' + this.type
5770 if (this.type!=='nav') {
5771 Roo.log('nav type must be nav/tabs/pills')
5773 cfg.cn[0].cls += ' navbar-nav'
5779 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5780 cfg.cn[0].cls += ' nav-' + this.arrangement;
5784 if (this.align === 'right') {
5785 cfg.cn[0].cls += ' navbar-right';
5810 * navbar-expand-md fixed-top
5814 * @class Roo.bootstrap.NavHeaderbar
5815 * @extends Roo.bootstrap.NavSimplebar
5816 * Bootstrap Sidebar class
5818 * @cfg {String} brand what is brand
5819 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5820 * @cfg {String} brand_href href of the brand
5821 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5822 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5823 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5824 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5827 * Create a new Sidebar
5828 * @param {Object} config The config object
5832 Roo.bootstrap.NavHeaderbar = function(config){
5833 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5837 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5844 desktopCenter : false,
5847 getAutoCreate : function(){
5850 tag: this.nav || 'nav',
5851 cls: 'navbar navbar-expand-md',
5857 if (this.desktopCenter) {
5858 cn.push({cls : 'container', cn : []});
5866 cls: 'navbar-toggle navbar-toggler',
5867 'data-toggle': 'collapse',
5872 html: 'Toggle navigation'
5876 cls: 'icon-bar navbar-toggler-icon'
5889 cn.push( Roo.bootstrap.version == 4 ? btn : {
5891 cls: 'navbar-header',
5900 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5904 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5906 if (['light','white'].indexOf(this.weight) > -1) {
5907 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5909 cfg.cls += ' bg-' + this.weight;
5912 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5913 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5915 // tag can override this..
5917 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5920 if (this.brand !== '') {
5921 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5922 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5924 href: this.brand_href ? this.brand_href : '#',
5925 cls: 'navbar-brand',
5933 cfg.cls += ' main-nav';
5941 getHeaderChildContainer : function()
5943 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5944 return this.el.select('.navbar-header',true).first();
5947 return this.getChildContainer();
5950 getChildContainer : function()
5953 return this.el.select('.roo-navbar-collapse',true).first();
5958 initEvents : function()
5960 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5962 if (this.autohide) {
5967 Roo.get(document).on('scroll',function(e) {
5968 var ns = Roo.get(document).getScroll().top;
5969 var os = prevScroll;
5973 ft.removeClass('slideDown');
5974 ft.addClass('slideUp');
5977 ft.removeClass('slideUp');
5978 ft.addClass('slideDown');
5999 * @class Roo.bootstrap.NavSidebar
6000 * @extends Roo.bootstrap.Navbar
6001 * Bootstrap Sidebar class
6004 * Create a new Sidebar
6005 * @param {Object} config The config object
6009 Roo.bootstrap.NavSidebar = function(config){
6010 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
6013 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
6015 sidebar : true, // used by Navbar Item and NavbarGroup at present...
6017 getAutoCreate : function(){
6022 cls: 'sidebar sidebar-nav'
6044 * @class Roo.bootstrap.NavGroup
6045 * @extends Roo.bootstrap.Component
6046 * Bootstrap NavGroup class
6047 * @cfg {String} align (left|right)
6048 * @cfg {Boolean} inverse
6049 * @cfg {String} type (nav|pills|tab) default nav
6050 * @cfg {String} navId - reference Id for navbar.
6051 * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6054 * Create a new nav group
6055 * @param {Object} config The config object
6058 Roo.bootstrap.NavGroup = function(config){
6059 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
6062 Roo.bootstrap.NavGroup.register(this);
6066 * Fires when the active item changes
6067 * @param {Roo.bootstrap.NavGroup} this
6068 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6069 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
6076 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
6088 getAutoCreate : function()
6090 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6096 if (Roo.bootstrap.version == 4) {
6097 if (['tabs','pills'].indexOf(this.type) != -1) {
6098 cfg.cls += ' nav-' + this.type;
6100 // trying to remove so header bar can right align top?
6101 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6102 // do not use on header bar...
6103 cfg.cls += ' navbar-nav';
6108 if (['tabs','pills'].indexOf(this.type) != -1) {
6109 cfg.cls += ' nav-' + this.type
6111 if (this.type !== 'nav') {
6112 Roo.log('nav type must be nav/tabs/pills')
6114 cfg.cls += ' navbar-nav'
6118 if (this.parent() && this.parent().sidebar) {
6121 cls: 'dashboard-menu sidebar-menu'
6127 if (this.form === true) {
6130 cls: 'navbar-form form-inline'
6132 //nav navbar-right ml-md-auto
6133 if (this.align === 'right') {
6134 cfg.cls += ' navbar-right ml-md-auto';
6136 cfg.cls += ' navbar-left';
6140 if (this.align === 'right') {
6141 cfg.cls += ' navbar-right ml-md-auto';
6143 cfg.cls += ' mr-auto';
6147 cfg.cls += ' navbar-inverse';
6155 * sets the active Navigation item
6156 * @param {Roo.bootstrap.NavItem} the new current navitem
6158 setActiveItem : function(item)
6161 Roo.each(this.navItems, function(v){
6166 v.setActive(false, true);
6173 item.setActive(true, true);
6174 this.fireEvent('changed', this, item, prev);
6179 * gets the active Navigation item
6180 * @return {Roo.bootstrap.NavItem} the current navitem
6182 getActive : function()
6186 Roo.each(this.navItems, function(v){
6197 indexOfNav : function()
6201 Roo.each(this.navItems, function(v,i){
6212 * adds a Navigation item
6213 * @param {Roo.bootstrap.NavItem} the navitem to add
6215 addItem : function(cfg)
6217 if (this.form && Roo.bootstrap.version == 4) {
6220 var cn = new Roo.bootstrap.NavItem(cfg);
6222 cn.parentId = this.id;
6223 cn.onRender(this.el, null);
6227 * register a Navigation item
6228 * @param {Roo.bootstrap.NavItem} the navitem to add
6230 register : function(item)
6232 this.navItems.push( item);
6233 item.navId = this.navId;
6238 * clear all the Navigation item
6241 clearAll : function()
6244 this.el.dom.innerHTML = '';
6247 getNavItem: function(tabId)
6250 Roo.each(this.navItems, function(e) {
6251 if (e.tabId == tabId) {
6261 setActiveNext : function()
6263 var i = this.indexOfNav(this.getActive());
6264 if (i > this.navItems.length) {
6267 this.setActiveItem(this.navItems[i+1]);
6269 setActivePrev : function()
6271 var i = this.indexOfNav(this.getActive());
6275 this.setActiveItem(this.navItems[i-1]);
6277 clearWasActive : function(except) {
6278 Roo.each(this.navItems, function(e) {
6279 if (e.tabId != except.tabId && e.was_active) {
6280 e.was_active = false;
6287 getWasActive : function ()
6290 Roo.each(this.navItems, function(e) {
6305 Roo.apply(Roo.bootstrap.NavGroup, {
6309 * register a Navigation Group
6310 * @param {Roo.bootstrap.NavGroup} the navgroup to add
6312 register : function(navgrp)
6314 this.groups[navgrp.navId] = navgrp;
6318 * fetch a Navigation Group based on the navigation ID
6319 * @param {string} the navgroup to add
6320 * @returns {Roo.bootstrap.NavGroup} the navgroup
6322 get: function(navId) {
6323 if (typeof(this.groups[navId]) == 'undefined') {
6325 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6327 return this.groups[navId] ;
6342 * @class Roo.bootstrap.NavItem
6343 * @extends Roo.bootstrap.Component
6344 * Bootstrap Navbar.NavItem class
6345 * @cfg {String} href link to
6346 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6347 * @cfg {Boolean} button_outline show and outlined button
6348 * @cfg {String} html content of button
6349 * @cfg {String} badge text inside badge
6350 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6351 * @cfg {String} glyphicon DEPRICATED - use fa
6352 * @cfg {String} icon DEPRICATED - use fa
6353 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6354 * @cfg {Boolean} active Is item active
6355 * @cfg {Boolean} disabled Is item disabled
6356 * @cfg {String} linkcls Link Class
6357 * @cfg {Boolean} preventDefault (true | false) default false
6358 * @cfg {String} tabId the tab that this item activates.
6359 * @cfg {String} tagtype (a|span) render as a href or span?
6360 * @cfg {Boolean} animateRef (true|false) link to element default false
6363 * Create a new Navbar Item
6364 * @param {Object} config The config object
6366 Roo.bootstrap.NavItem = function(config){
6367 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6372 * The raw click event for the entire grid.
6373 * @param {Roo.EventObject} e
6378 * Fires when the active item active state changes
6379 * @param {Roo.bootstrap.NavItem} this
6380 * @param {boolean} state the new state
6386 * Fires when scroll to element
6387 * @param {Roo.bootstrap.NavItem} this
6388 * @param {Object} options
6389 * @param {Roo.EventObject} e
6397 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
6406 preventDefault : false,
6414 button_outline : false,
6418 getAutoCreate : function(){
6425 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6428 cfg.cls += ' active' ;
6430 if (this.disabled) {
6431 cfg.cls += ' disabled';
6435 if (this.button_weight.length) {
6436 cfg.tag = this.href ? 'a' : 'button';
6437 cfg.html = this.html || '';
6438 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6440 cfg.href = this.href;
6443 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6445 cfg.cls += " nav-html";
6448 // menu .. should add dropdown-menu class - so no need for carat..
6450 if (this.badge !== '') {
6452 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6457 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6461 href : this.href || "#",
6462 html: this.html || '',
6466 if (this.tagtype == 'a') {
6467 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
6471 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6472 } else if (this.fa) {
6473 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6474 } else if(this.glyphicon) {
6475 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6477 cfg.cn[0].cls += " nav-html";
6481 cfg.cn[0].html += " <span class='caret'></span>";
6485 if (this.badge !== '') {
6486 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6494 onRender : function(ct, position)
6496 // Roo.log("Call onRender: " + this.xtype);
6497 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6501 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6502 this.navLink = this.el.select('.nav-link',true).first();
6503 this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6508 initEvents: function()
6510 if (typeof (this.menu) != 'undefined') {
6511 this.menu.parentType = this.xtype;
6512 this.menu.triggerEl = this.el;
6513 this.menu = this.addxtype(Roo.apply({}, this.menu));
6516 this.el.on('click', this.onClick, this);
6518 //if(this.tagtype == 'span'){
6519 // this.el.select('span',true).on('click', this.onClick, this);
6522 // at this point parent should be available..
6523 this.parent().register(this);
6526 onClick : function(e)
6528 if (e.getTarget('.dropdown-menu-item')) {
6529 // did you click on a menu itemm.... - then don't trigger onclick..
6534 this.preventDefault ||
6537 Roo.log("NavItem - prevent Default?");
6541 if (this.disabled) {
6545 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6546 if (tg && tg.transition) {
6547 Roo.log("waiting for the transitionend");
6553 //Roo.log("fire event clicked");
6554 if(this.fireEvent('click', this, e) === false){
6558 if(this.tagtype == 'span'){
6562 //Roo.log(this.href);
6563 var ael = this.el.select('a',true).first();
6566 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6567 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6568 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6569 return; // ignore... - it's a 'hash' to another page.
6571 Roo.log("NavItem - prevent Default?");
6573 this.scrollToElement(e);
6577 var p = this.parent();
6579 if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6580 if (typeof(p.setActiveItem) !== 'undefined') {
6581 p.setActiveItem(this);
6585 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6586 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6587 // remove the collapsed menu expand...
6588 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6592 isActive: function () {
6595 setActive : function(state, fire, is_was_active)
6597 if (this.active && !state && this.navId) {
6598 this.was_active = true;
6599 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6601 nv.clearWasActive(this);
6605 this.active = state;
6608 this.el.removeClass('active');
6609 this.navLink ? this.navLink.removeClass('active') : false;
6610 } else if (!this.el.hasClass('active')) {
6612 this.el.addClass('active');
6613 if (Roo.bootstrap.version == 4 && this.navLink ) {
6614 this.navLink.addClass('active');
6619 this.fireEvent('changed', this, state);
6622 // show a panel if it's registered and related..
6624 if (!this.navId || !this.tabId || !state || is_was_active) {
6628 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6632 var pan = tg.getPanelByName(this.tabId);
6636 // if we can not flip to new panel - go back to old nav highlight..
6637 if (false == tg.showPanel(pan)) {
6638 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6640 var onav = nv.getWasActive();
6642 onav.setActive(true, false, true);
6651 // this should not be here...
6652 setDisabled : function(state)
6654 this.disabled = state;
6656 this.el.removeClass('disabled');
6657 } else if (!this.el.hasClass('disabled')) {
6658 this.el.addClass('disabled');
6664 * Fetch the element to display the tooltip on.
6665 * @return {Roo.Element} defaults to this.el
6667 tooltipEl : function()
6669 return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
6672 scrollToElement : function(e)
6674 var c = document.body;
6677 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6679 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6680 c = document.documentElement;
6683 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6689 var o = target.calcOffsetsTo(c);
6696 this.fireEvent('scrollto', this, options, e);
6698 Roo.get(c).scrollTo('top', options.value, true);
6703 * Set the HTML (text content) of the item
6704 * @param {string} html content for the nav item
6706 setHtml : function(html)
6709 this.htmlEl.dom.innerHTML = html;
6721 * <span> icon </span>
6722 * <span> text </span>
6723 * <span>badge </span>
6727 * @class Roo.bootstrap.NavSidebarItem
6728 * @extends Roo.bootstrap.NavItem
6729 * Bootstrap Navbar.NavSidebarItem class
6730 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6731 * {Boolean} open is the menu open
6732 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6733 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6734 * {String} buttonSize (sm|md|lg)the extra classes for the button
6735 * {Boolean} showArrow show arrow next to the text (default true)
6737 * Create a new Navbar Button
6738 * @param {Object} config The config object
6740 Roo.bootstrap.NavSidebarItem = function(config){
6741 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6746 * The raw click event for the entire grid.
6747 * @param {Roo.EventObject} e
6752 * Fires when the active item active state changes
6753 * @param {Roo.bootstrap.NavSidebarItem} this
6754 * @param {boolean} state the new state
6762 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6764 badgeWeight : 'default',
6770 buttonWeight : 'default',
6776 getAutoCreate : function(){
6781 href : this.href || '#',
6787 if(this.buttonView){
6790 href : this.href || '#',
6791 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6804 cfg.cls += ' active';
6807 if (this.disabled) {
6808 cfg.cls += ' disabled';
6811 cfg.cls += ' open x-open';
6814 if (this.glyphicon || this.icon) {
6815 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6816 a.cn.push({ tag : 'i', cls : c }) ;
6819 if(!this.buttonView){
6822 html : this.html || ''
6829 if (this.badge !== '') {
6830 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6836 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6839 a.cls += ' dropdown-toggle treeview' ;
6845 initEvents : function()
6847 if (typeof (this.menu) != 'undefined') {
6848 this.menu.parentType = this.xtype;
6849 this.menu.triggerEl = this.el;
6850 this.menu = this.addxtype(Roo.apply({}, this.menu));
6853 this.el.on('click', this.onClick, this);
6855 if(this.badge !== ''){
6856 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6861 onClick : function(e)
6868 if(this.preventDefault){
6872 this.fireEvent('click', this, e);
6875 disable : function()
6877 this.setDisabled(true);
6882 this.setDisabled(false);
6885 setDisabled : function(state)
6887 if(this.disabled == state){
6891 this.disabled = state;
6894 this.el.addClass('disabled');
6898 this.el.removeClass('disabled');
6903 setActive : function(state)
6905 if(this.active == state){
6909 this.active = state;
6912 this.el.addClass('active');
6916 this.el.removeClass('active');
6921 isActive: function ()
6926 setBadge : function(str)
6932 this.badgeEl.dom.innerHTML = str;
6947 Roo.namespace('Roo.bootstrap.breadcrumb');
6951 * @class Roo.bootstrap.breadcrumb.Nav
6952 * @extends Roo.bootstrap.Component
6953 * Bootstrap Breadcrumb Nav Class
6955 * @children Roo.bootstrap.breadcrumb.Item
6958 * Create a new breadcrumb.Nav
6959 * @param {Object} config The config object
6963 Roo.bootstrap.breadcrumb.Nav = function(config){
6964 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6969 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
6971 getAutoCreate : function()
6988 initEvents: function()
6990 this.olEl = this.el.select('ol',true).first();
6992 getChildContainer : function()
7008 * @class Roo.bootstrap.breadcrumb.Nav
7009 * @extends Roo.bootstrap.Component
7010 * Bootstrap Breadcrumb Nav Class
7012 * @children Roo.bootstrap.breadcrumb.Component
7013 * @cfg {String} html the content of the link.
7014 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7015 * @cfg {Boolean} active is it active
7019 * Create a new breadcrumb.Nav
7020 * @param {Object} config The config object
7023 Roo.bootstrap.breadcrumb.Item = function(config){
7024 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7029 * The img click event for the img.
7030 * @param {Roo.EventObject} e
7037 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
7042 getAutoCreate : function()
7047 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7049 if (this.href !== false) {
7056 cfg.html = this.html;
7062 initEvents: function()
7065 this.el.select('a', true).first().on('click',this.onClick, this)
7069 onClick : function(e)
7072 this.fireEvent('click',this, e);
7085 * @class Roo.bootstrap.Row
7086 * @extends Roo.bootstrap.Component
7087 * Bootstrap Row class (contains columns...)
7091 * @param {Object} config The config object
7094 Roo.bootstrap.Row = function(config){
7095 Roo.bootstrap.Row.superclass.constructor.call(this, config);
7098 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
7100 getAutoCreate : function(){
7119 * @class Roo.bootstrap.Pagination
7120 * @extends Roo.bootstrap.Component
7121 * Bootstrap Pagination class
7122 * @cfg {String} size xs | sm | md | lg
7123 * @cfg {Boolean} inverse false | true
7126 * Create a new Pagination
7127 * @param {Object} config The config object
7130 Roo.bootstrap.Pagination = function(config){
7131 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7134 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
7140 getAutoCreate : function(){
7146 cfg.cls += ' inverse';
7152 cfg.cls += " " + this.cls;
7170 * @class Roo.bootstrap.PaginationItem
7171 * @extends Roo.bootstrap.Component
7172 * Bootstrap PaginationItem class
7173 * @cfg {String} html text
7174 * @cfg {String} href the link
7175 * @cfg {Boolean} preventDefault (true | false) default true
7176 * @cfg {Boolean} active (true | false) default false
7177 * @cfg {Boolean} disabled default false
7181 * Create a new PaginationItem
7182 * @param {Object} config The config object
7186 Roo.bootstrap.PaginationItem = function(config){
7187 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7192 * The raw click event for the entire grid.
7193 * @param {Roo.EventObject} e
7199 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
7203 preventDefault: true,
7208 getAutoCreate : function(){
7214 href : this.href ? this.href : '#',
7215 html : this.html ? this.html : ''
7225 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7229 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7235 initEvents: function() {
7237 this.el.on('click', this.onClick, this);
7240 onClick : function(e)
7242 Roo.log('PaginationItem on click ');
7243 if(this.preventDefault){
7251 this.fireEvent('click', this, e);
7267 * @class Roo.bootstrap.Slider
7268 * @extends Roo.bootstrap.Component
7269 * Bootstrap Slider class
7272 * Create a new Slider
7273 * @param {Object} config The config object
7276 Roo.bootstrap.Slider = function(config){
7277 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7280 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
7282 getAutoCreate : function(){
7286 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7290 cls: 'ui-slider-handle ui-state-default ui-corner-all'
7302 * Ext JS Library 1.1.1
7303 * Copyright(c) 2006-2007, Ext JS, LLC.
7305 * Originally Released Under LGPL - original licence link has changed is not relivant.
7308 * <script type="text/javascript">
7312 * @class Roo.grid.AbstractSelectionModel
7313 * @extends Roo.util.Observable
7314 * Abstract base class for grid SelectionModels. It provides the interface that should be
7315 * implemented by descendant classes. This class should not be directly instantiated.
7318 Roo.grid.AbstractSelectionModel = function(){
7319 this.locked = false;
7320 Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7323 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable, {
7324 /** @ignore Called by the grid automatically. Do not call directly. */
7325 init : function(grid){
7331 * Locks the selections.
7338 * Unlocks the selections.
7340 unlock : function(){
7341 this.locked = false;
7345 * Returns true if the selections are locked.
7348 isLocked : function(){
7353 * Ext JS Library 1.1.1
7354 * Copyright(c) 2006-2007, Ext JS, LLC.
7356 * Originally Released Under LGPL - original licence link has changed is not relivant.
7359 * <script type="text/javascript">
7362 * @extends Roo.grid.AbstractSelectionModel
7363 * @class Roo.grid.RowSelectionModel
7364 * The default SelectionModel used by {@link Roo.grid.Grid}.
7365 * It supports multiple selections and keyboard selection/navigation.
7367 * @param {Object} config
7369 Roo.grid.RowSelectionModel = function(config){
7370 Roo.apply(this, config);
7371 this.selections = new Roo.util.MixedCollection(false, function(o){
7376 this.lastActive = false;
7380 * @event selectionchange
7381 * Fires when the selection changes
7382 * @param {SelectionModel} this
7384 "selectionchange" : true,
7386 * @event afterselectionchange
7387 * Fires after the selection changes (eg. by key press or clicking)
7388 * @param {SelectionModel} this
7390 "afterselectionchange" : true,
7392 * @event beforerowselect
7393 * Fires when a row is selected being selected, return false to cancel.
7394 * @param {SelectionModel} this
7395 * @param {Number} rowIndex The selected index
7396 * @param {Boolean} keepExisting False if other selections will be cleared
7398 "beforerowselect" : true,
7401 * Fires when a row is selected.
7402 * @param {SelectionModel} this
7403 * @param {Number} rowIndex The selected index
7404 * @param {Roo.data.Record} r The record
7408 * @event rowdeselect
7409 * Fires when a row is deselected.
7410 * @param {SelectionModel} this
7411 * @param {Number} rowIndex The selected index
7413 "rowdeselect" : true
7415 Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7416 this.locked = false;
7419 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel, {
7421 * @cfg {Boolean} singleSelect
7422 * True to allow selection of only one row at a time (defaults to false)
7424 singleSelect : false,
7427 initEvents : function(){
7429 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7430 this.grid.on("mousedown", this.handleMouseDown, this);
7431 }else{ // allow click to work like normal
7432 this.grid.on("rowclick", this.handleDragableRowClick, this);
7434 // bootstrap does not have a view..
7435 var view = this.grid.view ? this.grid.view : this.grid;
7436 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7439 this.selectPrevious(e.shiftKey);
7440 }else if(this.last !== false && this.lastActive !== false){
7441 var last = this.last;
7442 this.selectRange(this.last, this.lastActive-1);
7443 view.focusRow(this.lastActive);
7448 this.selectFirstRow();
7450 this.fireEvent("afterselectionchange", this);
7452 "down" : function(e){
7454 this.selectNext(e.shiftKey);
7455 }else if(this.last !== false && this.lastActive !== false){
7456 var last = this.last;
7457 this.selectRange(this.last, this.lastActive+1);
7458 view.focusRow(this.lastActive);
7463 this.selectFirstRow();
7465 this.fireEvent("afterselectionchange", this);
7471 view.on("refresh", this.onRefresh, this);
7472 view.on("rowupdated", this.onRowUpdated, this);
7473 view.on("rowremoved", this.onRemove, this);
7477 onRefresh : function(){
7478 var ds = this.grid.dataSource, i, v = this.grid.view;
7479 var s = this.selections;
7481 if((i = ds.indexOfId(r.id)) != -1){
7483 s.add(ds.getAt(i)); // updating the selection relate data
7491 onRemove : function(v, index, r){
7492 this.selections.remove(r);
7496 onRowUpdated : function(v, index, r){
7497 if(this.isSelected(r)){
7498 v.onRowSelect(index);
7504 * @param {Array} records The records to select
7505 * @param {Boolean} keepExisting (optional) True to keep existing selections
7507 selectRecords : function(records, keepExisting){
7509 this.clearSelections();
7511 var ds = this.grid.dataSource;
7512 for(var i = 0, len = records.length; i < len; i++){
7513 this.selectRow(ds.indexOf(records[i]), true);
7518 * Gets the number of selected rows.
7521 getCount : function(){
7522 return this.selections.length;
7526 * Selects the first row in the grid.
7528 selectFirstRow : function(){
7533 * Select the last row.
7534 * @param {Boolean} keepExisting (optional) True to keep existing selections
7536 selectLastRow : function(keepExisting){
7537 this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
7541 * Selects the row immediately following the last selected row.
7542 * @param {Boolean} keepExisting (optional) True to keep existing selections
7544 selectNext : function(keepExisting){
7545 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
7546 this.selectRow(this.last+1, keepExisting);
7547 var view = this.grid.view ? this.grid.view : this.grid;
7548 view.focusRow(this.last);
7553 * Selects the row that precedes the last selected row.
7554 * @param {Boolean} keepExisting (optional) True to keep existing selections
7556 selectPrevious : function(keepExisting){
7558 this.selectRow(this.last-1, keepExisting);
7559 this.grid.getView().focusRow(this.last);
7564 * Returns the selected records
7565 * @return {Array} Array of selected records
7567 getSelections : function(){
7568 return [].concat(this.selections.items);
7572 * Returns the first selected record.
7575 getSelected : function(){
7576 return this.selections.itemAt(0);
7581 * Clears all selections.
7583 clearSelections : function(fast){
7588 var ds = this.grid.dataSource;
7589 var s = this.selections;
7591 this.deselectRow(ds.indexOfId(r.id));
7595 this.selections.clear();
7604 selectAll : function(){
7608 this.selections.clear();
7609 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
7610 this.selectRow(i, true);
7615 * Returns True if there is a selection.
7618 hasSelection : function(){
7619 return this.selections.length > 0;
7623 * Returns True if the specified row is selected.
7624 * @param {Number/Record} record The record or index of the record to check
7627 isSelected : function(index){
7628 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
7629 return (r && this.selections.key(r.id) ? true : false);
7633 * Returns True if the specified record id is selected.
7634 * @param {String} id The id of record to check
7637 isIdSelected : function(id){
7638 return (this.selections.key(id) ? true : false);
7642 handleMouseDown : function(e, t)
7644 var view = this.grid.view ? this.grid.view : this.grid;
7646 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
7649 if(e.shiftKey && this.last !== false){
7650 var last = this.last;
7651 this.selectRange(last, rowIndex, e.ctrlKey);
7652 this.last = last; // reset the last
7653 view.focusRow(rowIndex);
7655 var isSelected = this.isSelected(rowIndex);
7656 if(e.button !== 0 && isSelected){
7657 view.focusRow(rowIndex);
7658 }else if(e.ctrlKey && isSelected){
7659 this.deselectRow(rowIndex);
7660 }else if(!isSelected){
7661 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
7662 view.focusRow(rowIndex);
7665 this.fireEvent("afterselectionchange", this);
7668 handleDragableRowClick : function(grid, rowIndex, e)
7670 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
7671 this.selectRow(rowIndex, false);
7672 var view = this.grid.view ? this.grid.view : this.grid;
7673 view.focusRow(rowIndex);
7674 this.fireEvent("afterselectionchange", this);
7679 * Selects multiple rows.
7680 * @param {Array} rows Array of the indexes of the row to select
7681 * @param {Boolean} keepExisting (optional) True to keep existing selections
7683 selectRows : function(rows, keepExisting){
7685 this.clearSelections();
7687 for(var i = 0, len = rows.length; i < len; i++){
7688 this.selectRow(rows[i], true);
7693 * Selects a range of rows. All rows in between startRow and endRow are also selected.
7694 * @param {Number} startRow The index of the first row in the range
7695 * @param {Number} endRow The index of the last row in the range
7696 * @param {Boolean} keepExisting (optional) True to retain existing selections
7698 selectRange : function(startRow, endRow, keepExisting){
7703 this.clearSelections();
7705 if(startRow <= endRow){
7706 for(var i = startRow; i <= endRow; i++){
7707 this.selectRow(i, true);
7710 for(var i = startRow; i >= endRow; i--){
7711 this.selectRow(i, true);
7717 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
7718 * @param {Number} startRow The index of the first row in the range
7719 * @param {Number} endRow The index of the last row in the range
7721 deselectRange : function(startRow, endRow, preventViewNotify){
7725 for(var i = startRow; i <= endRow; i++){
7726 this.deselectRow(i, preventViewNotify);
7732 * @param {Number} row The index of the row to select
7733 * @param {Boolean} keepExisting (optional) True to keep existing selections
7735 selectRow : function(index, keepExisting, preventViewNotify){
7736 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
7739 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
7740 if(!keepExisting || this.singleSelect){
7741 this.clearSelections();
7743 var r = this.grid.dataSource.getAt(index);
7744 this.selections.add(r);
7745 this.last = this.lastActive = index;
7746 if(!preventViewNotify){
7747 this.grid.getView().onRowSelect(index);
7749 this.fireEvent("rowselect", this, index, r);
7750 this.fireEvent("selectionchange", this);
7756 * @param {Number} row The index of the row to deselect
7758 deselectRow : function(index, preventViewNotify){
7762 if(this.last == index){
7765 if(this.lastActive == index){
7766 this.lastActive = false;
7768 var r = this.grid.dataSource.getAt(index);
7769 this.selections.remove(r);
7770 if(!preventViewNotify){
7771 this.grid.getView().onRowDeselect(index);
7773 this.fireEvent("rowdeselect", this, index);
7774 this.fireEvent("selectionchange", this);
7778 restoreLast : function(){
7780 this.last = this._last;
7785 acceptsNav : function(row, col, cm){
7786 return !cm.isHidden(col) && cm.isCellEditable(col, row);
7790 onEditorKey : function(field, e){
7791 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
7796 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
7798 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
7800 }else if(k == e.ENTER && !e.ctrlKey){
7804 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
7806 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
7808 }else if(k == e.ESC){
7812 g.startEditing(newCell[0], newCell[1]);
7817 * Ext JS Library 1.1.1
7818 * Copyright(c) 2006-2007, Ext JS, LLC.
7820 * Originally Released Under LGPL - original licence link has changed is not relivant.
7823 * <script type="text/javascript">
7828 * @class Roo.grid.ColumnModel
7829 * @extends Roo.util.Observable
7830 * This is the default implementation of a ColumnModel used by the Grid. It defines
7831 * the columns in the grid.
7834 var colModel = new Roo.grid.ColumnModel([
7835 {header: "Ticker", width: 60, sortable: true, locked: true},
7836 {header: "Company Name", width: 150, sortable: true},
7837 {header: "Market Cap.", width: 100, sortable: true},
7838 {header: "$ Sales", width: 100, sortable: true, renderer: money},
7839 {header: "Employees", width: 100, sortable: true, resizable: false}
7844 * The config options listed for this class are options which may appear in each
7845 * individual column definition.
7846 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7848 * @param {Object} config An Array of column config objects. See this class's
7849 * config objects for details.
7851 Roo.grid.ColumnModel = function(config){
7853 * The config passed into the constructor
7855 this.config = []; //config;
7858 // if no id, create one
7859 // if the column does not have a dataIndex mapping,
7860 // map it to the order it is in the config
7861 for(var i = 0, len = config.length; i < len; i++){
7862 this.addColumn(config[i]);
7867 * The width of columns which have no width specified (defaults to 100)
7870 this.defaultWidth = 100;
7873 * Default sortable of columns which have no sortable specified (defaults to false)
7876 this.defaultSortable = false;
7880 * @event widthchange
7881 * Fires when the width of a column changes.
7882 * @param {ColumnModel} this
7883 * @param {Number} columnIndex The column index
7884 * @param {Number} newWidth The new width
7886 "widthchange": true,
7888 * @event headerchange
7889 * Fires when the text of a header changes.
7890 * @param {ColumnModel} this
7891 * @param {Number} columnIndex The column index
7892 * @param {Number} newText The new header text
7894 "headerchange": true,
7896 * @event hiddenchange
7897 * Fires when a column is hidden or "unhidden".
7898 * @param {ColumnModel} this
7899 * @param {Number} columnIndex The column index
7900 * @param {Boolean} hidden true if hidden, false otherwise
7902 "hiddenchange": true,
7904 * @event columnmoved
7905 * Fires when a column is moved.
7906 * @param {ColumnModel} this
7907 * @param {Number} oldIndex
7908 * @param {Number} newIndex
7910 "columnmoved" : true,
7912 * @event columlockchange
7913 * Fires when a column's locked state is changed
7914 * @param {ColumnModel} this
7915 * @param {Number} colIndex
7916 * @param {Boolean} locked true if locked
7918 "columnlockchange" : true
7920 Roo.grid.ColumnModel.superclass.constructor.call(this);
7922 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7924 * @cfg {String} header The header text to display in the Grid view.
7927 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7928 * {@link Roo.data.Record} definition from which to draw the column's value. If not
7929 * specified, the column's index is used as an index into the Record's data Array.
7932 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7933 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7936 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7937 * Defaults to the value of the {@link #defaultSortable} property.
7938 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7941 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
7944 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
7947 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7950 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7953 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7954 * given the cell's data value. See {@link #setRenderer}. If not specified, the
7955 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7956 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7959 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
7962 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
7965 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
7968 * @cfg {String} cursor (Optional)
7971 * @cfg {String} tooltip (Optional)
7974 * @cfg {Number} xs (Optional)
7977 * @cfg {Number} sm (Optional)
7980 * @cfg {Number} md (Optional)
7983 * @cfg {Number} lg (Optional)
7986 * Returns the id of the column at the specified index.
7987 * @param {Number} index The column index
7988 * @return {String} the id
7990 getColumnId : function(index){
7991 return this.config[index].id;
7995 * Returns the column for a specified id.
7996 * @param {String} id The column id
7997 * @return {Object} the column
7999 getColumnById : function(id){
8000 return this.lookup[id];
8005 * Returns the column Object for a specified dataIndex.
8006 * @param {String} dataIndex The column dataIndex
8007 * @return {Object|Boolean} the column or false if not found
8009 getColumnByDataIndex: function(dataIndex){
8010 var index = this.findColumnIndex(dataIndex);
8011 return index > -1 ? this.config[index] : false;
8015 * Returns the index for a specified column id.
8016 * @param {String} id The column id
8017 * @return {Number} the index, or -1 if not found
8019 getIndexById : function(id){
8020 for(var i = 0, len = this.config.length; i < len; i++){
8021 if(this.config[i].id == id){
8029 * Returns the index for a specified column dataIndex.
8030 * @param {String} dataIndex The column dataIndex
8031 * @return {Number} the index, or -1 if not found
8034 findColumnIndex : function(dataIndex){
8035 for(var i = 0, len = this.config.length; i < len; i++){
8036 if(this.config[i].dataIndex == dataIndex){
8044 moveColumn : function(oldIndex, newIndex){
8045 var c = this.config[oldIndex];
8046 this.config.splice(oldIndex, 1);
8047 this.config.splice(newIndex, 0, c);
8048 this.dataMap = null;
8049 this.fireEvent("columnmoved", this, oldIndex, newIndex);
8052 isLocked : function(colIndex){
8053 return this.config[colIndex].locked === true;
8056 setLocked : function(colIndex, value, suppressEvent){
8057 if(this.isLocked(colIndex) == value){
8060 this.config[colIndex].locked = value;
8062 this.fireEvent("columnlockchange", this, colIndex, value);
8066 getTotalLockedWidth : function(){
8068 for(var i = 0; i < this.config.length; i++){
8069 if(this.isLocked(i) && !this.isHidden(i)){
8070 this.totalWidth += this.getColumnWidth(i);
8076 getLockedCount : function(){
8077 for(var i = 0, len = this.config.length; i < len; i++){
8078 if(!this.isLocked(i)){
8083 return this.config.length;
8087 * Returns the number of columns.
8090 getColumnCount : function(visibleOnly){
8091 if(visibleOnly === true){
8093 for(var i = 0, len = this.config.length; i < len; i++){
8094 if(!this.isHidden(i)){
8100 return this.config.length;
8104 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8105 * @param {Function} fn
8106 * @param {Object} scope (optional)
8107 * @return {Array} result
8109 getColumnsBy : function(fn, scope){
8111 for(var i = 0, len = this.config.length; i < len; i++){
8112 var c = this.config[i];
8113 if(fn.call(scope||this, c, i) === true){
8121 * Returns true if the specified column is sortable.
8122 * @param {Number} col The column index
8125 isSortable : function(col){
8126 if(typeof this.config[col].sortable == "undefined"){
8127 return this.defaultSortable;
8129 return this.config[col].sortable;
8133 * Returns the rendering (formatting) function defined for the column.
8134 * @param {Number} col The column index.
8135 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8137 getRenderer : function(col){
8138 if(!this.config[col].renderer){
8139 return Roo.grid.ColumnModel.defaultRenderer;
8141 return this.config[col].renderer;
8145 * Sets the rendering (formatting) function for a column.
8146 * @param {Number} col The column index
8147 * @param {Function} fn The function to use to process the cell's raw data
8148 * to return HTML markup for the grid view. The render function is called with
8149 * the following parameters:<ul>
8150 * <li>Data value.</li>
8151 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8152 * <li>css A CSS style string to apply to the table cell.</li>
8153 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8154 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8155 * <li>Row index</li>
8156 * <li>Column index</li>
8157 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8159 setRenderer : function(col, fn){
8160 this.config[col].renderer = fn;
8164 * Returns the width for the specified column.
8165 * @param {Number} col The column index
8168 getColumnWidth : function(col){
8169 return this.config[col].width * 1 || this.defaultWidth;
8173 * Sets the width for a column.
8174 * @param {Number} col The column index
8175 * @param {Number} width The new width
8177 setColumnWidth : function(col, width, suppressEvent){
8178 this.config[col].width = width;
8179 this.totalWidth = null;
8181 this.fireEvent("widthchange", this, col, width);
8186 * Returns the total width of all columns.
8187 * @param {Boolean} includeHidden True to include hidden column widths
8190 getTotalWidth : function(includeHidden){
8191 if(!this.totalWidth){
8192 this.totalWidth = 0;
8193 for(var i = 0, len = this.config.length; i < len; i++){
8194 if(includeHidden || !this.isHidden(i)){
8195 this.totalWidth += this.getColumnWidth(i);
8199 return this.totalWidth;
8203 * Returns the header for the specified column.
8204 * @param {Number} col The column index
8207 getColumnHeader : function(col){
8208 return this.config[col].header;
8212 * Sets the header for a column.
8213 * @param {Number} col The column index
8214 * @param {String} header The new header
8216 setColumnHeader : function(col, header){
8217 this.config[col].header = header;
8218 this.fireEvent("headerchange", this, col, header);
8222 * Returns the tooltip for the specified column.
8223 * @param {Number} col The column index
8226 getColumnTooltip : function(col){
8227 return this.config[col].tooltip;
8230 * Sets the tooltip for a column.
8231 * @param {Number} col The column index
8232 * @param {String} tooltip The new tooltip
8234 setColumnTooltip : function(col, tooltip){
8235 this.config[col].tooltip = tooltip;
8239 * Returns the dataIndex for the specified column.
8240 * @param {Number} col The column index
8243 getDataIndex : function(col){
8244 return this.config[col].dataIndex;
8248 * Sets the dataIndex for a column.
8249 * @param {Number} col The column index
8250 * @param {Number} dataIndex The new dataIndex
8252 setDataIndex : function(col, dataIndex){
8253 this.config[col].dataIndex = dataIndex;
8259 * Returns true if the cell is editable.
8260 * @param {Number} colIndex The column index
8261 * @param {Number} rowIndex The row index - this is nto actually used..?
8264 isCellEditable : function(colIndex, rowIndex){
8265 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8269 * Returns the editor defined for the cell/column.
8270 * return false or null to disable editing.
8271 * @param {Number} colIndex The column index
8272 * @param {Number} rowIndex The row index
8275 getCellEditor : function(colIndex, rowIndex){
8276 return this.config[colIndex].editor;
8280 * Sets if a column is editable.
8281 * @param {Number} col The column index
8282 * @param {Boolean} editable True if the column is editable
8284 setEditable : function(col, editable){
8285 this.config[col].editable = editable;
8290 * Returns true if the column is hidden.
8291 * @param {Number} colIndex The column index
8294 isHidden : function(colIndex){
8295 return this.config[colIndex].hidden;
8300 * Returns true if the column width cannot be changed
8302 isFixed : function(colIndex){
8303 return this.config[colIndex].fixed;
8307 * Returns true if the column can be resized
8310 isResizable : function(colIndex){
8311 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8314 * Sets if a column is hidden.
8315 * @param {Number} colIndex The column index
8316 * @param {Boolean} hidden True if the column is hidden
8318 setHidden : function(colIndex, hidden){
8319 this.config[colIndex].hidden = hidden;
8320 this.totalWidth = null;
8321 this.fireEvent("hiddenchange", this, colIndex, hidden);
8325 * Sets the editor for a column.
8326 * @param {Number} col The column index
8327 * @param {Object} editor The editor object
8329 setEditor : function(col, editor){
8330 this.config[col].editor = editor;
8333 * Add a column (experimental...) - defaults to adding to the end..
8334 * @param {Object} config
8336 addColumn : function(c)
8339 var i = this.config.length;
8342 if(typeof c.dataIndex == "undefined"){
8345 if(typeof c.renderer == "string"){
8346 c.renderer = Roo.util.Format[c.renderer];
8348 if(typeof c.id == "undefined"){
8351 if(c.editor && c.editor.xtype){
8352 c.editor = Roo.factory(c.editor, Roo.grid);
8354 if(c.editor && c.editor.isFormField){
8355 c.editor = new Roo.grid.GridEditor(c.editor);
8357 this.lookup[c.id] = c;
8362 Roo.grid.ColumnModel.defaultRenderer = function(value)
8364 if(typeof value == "object") {
8367 if(typeof value == "string" && value.length < 1){
8371 return String.format("{0}", value);
8374 // Alias for backwards compatibility
8375 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8378 * Ext JS Library 1.1.1
8379 * Copyright(c) 2006-2007, Ext JS, LLC.
8381 * Originally Released Under LGPL - original licence link has changed is not relivant.
8384 * <script type="text/javascript">
8388 * @class Roo.LoadMask
8389 * A simple utility class for generically masking elements while loading data. If the element being masked has
8390 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8391 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
8392 * element's UpdateManager load indicator and will be destroyed after the initial load.
8394 * Create a new LoadMask
8395 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8396 * @param {Object} config The config object
8398 Roo.LoadMask = function(el, config){
8399 this.el = Roo.get(el);
8400 Roo.apply(this, config);
8402 this.store.on('beforeload', this.onBeforeLoad, this);
8403 this.store.on('load', this.onLoad, this);
8404 this.store.on('loadexception', this.onLoadException, this);
8405 this.removeMask = false;
8407 var um = this.el.getUpdateManager();
8408 um.showLoadIndicator = false; // disable the default indicator
8409 um.on('beforeupdate', this.onBeforeLoad, this);
8410 um.on('update', this.onLoad, this);
8411 um.on('failure', this.onLoad, this);
8412 this.removeMask = true;
8416 Roo.LoadMask.prototype = {
8418 * @cfg {Boolean} removeMask
8419 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
8420 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
8424 * The text to display in a centered loading message box (defaults to 'Loading...')
8428 * @cfg {String} msgCls
8429 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
8431 msgCls : 'x-mask-loading',
8434 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
8440 * Disables the mask to prevent it from being displayed
8442 disable : function(){
8443 this.disabled = true;
8447 * Enables the mask so that it can be displayed
8449 enable : function(){
8450 this.disabled = false;
8453 onLoadException : function()
8457 if (typeof(arguments[3]) != 'undefined') {
8458 Roo.MessageBox.alert("Error loading",arguments[3]);
8462 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8463 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8470 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8475 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8479 onBeforeLoad : function(){
8481 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
8486 destroy : function(){
8488 this.store.un('beforeload', this.onBeforeLoad, this);
8489 this.store.un('load', this.onLoad, this);
8490 this.store.un('loadexception', this.onLoadException, this);
8492 var um = this.el.getUpdateManager();
8493 um.un('beforeupdate', this.onBeforeLoad, this);
8494 um.un('update', this.onLoad, this);
8495 um.un('failure', this.onLoad, this);
8499 * @class Roo.bootstrap.Table
8501 * @extends Roo.bootstrap.Component
8502 * Bootstrap Table class. This class represents the primary interface of a component based grid control.
8503 * Similar to Roo.grid.Grid
8505 var table = Roo.factory({
8507 xns : Roo.bootstrap,
8508 autoSizeColumns: true,
8515 sortInfo : { direction : 'ASC', field: 'name' },
8517 xtype : 'HttpProxy',
8520 url : 'https://example.com/some.data.url.json'
8523 xtype : 'JsonReader',
8525 fields : [ 'id', 'name', whatever' ],
8532 xtype : 'ColumnModel',
8536 dataIndex : 'is_in_group',
8539 renderer : function(v, x , r) {
8541 return String.format("{0}", v)
8547 xtype : 'RowSelectionModel',
8548 xns : Roo.bootstrap.Table
8549 // you can add listeners to catch selection change here....
8555 grid.render(Roo.get("some-div"));
8558 Currently the Table uses multiple headers to try and handle XL / Medium etc... styling
8563 * @cfg {Roo.grid.RowSelectionModel|Roo.grid.CellSelectionModel} sm The selection model to use (cell selection is not supported yet)
8564 * @cfg {Roo.data.Store|Roo.data.SimpleStore} store The data store to use
8565 * @cfg {Roo.grid.ColumnModel} cm[] A column for th grid.
8567 * @cfg {String} cls table class
8570 * @cfg {boolean} striped Should the rows be alternative striped
8571 * @cfg {boolean} bordered Add borders to the table
8572 * @cfg {boolean} hover Add hover highlighting
8573 * @cfg {boolean} condensed Format condensed
8574 * @cfg {boolean} responsive Format condensed
8575 * @cfg {Boolean} loadMask (true|false) default false
8576 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
8577 * @cfg {Boolean} headerShow (true|false) generate thead, default true
8578 * @cfg {Boolean} rowSelection (true|false) default false
8579 * @cfg {Boolean} cellSelection (true|false) default false
8580 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
8581 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
8582 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
8583 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
8587 * Create a new Table
8588 * @param {Object} config The config object
8591 Roo.bootstrap.Table = function(config)
8593 Roo.bootstrap.Table.superclass.constructor.call(this, config);
8596 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
8597 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
8598 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
8599 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
8601 this.sm = this.sm || {xtype: 'RowSelectionModel'};
8603 this.sm.grid = this;
8604 this.selModel = Roo.factory(this.sm, Roo.grid);
8605 this.sm = this.selModel;
8606 this.sm.xmodule = this.xmodule || false;
8609 if (this.cm && typeof(this.cm.config) == 'undefined') {
8610 this.colModel = new Roo.grid.ColumnModel(this.cm);
8611 this.cm = this.colModel;
8612 this.cm.xmodule = this.xmodule || false;
8615 this.store= Roo.factory(this.store, Roo.data);
8616 this.ds = this.store;
8617 this.ds.xmodule = this.xmodule || false;
8620 if (this.footer && this.store) {
8621 this.footer.dataSource = this.ds;
8622 this.footer = Roo.factory(this.footer);
8629 * Fires when a cell is clicked
8630 * @param {Roo.bootstrap.Table} this
8631 * @param {Roo.Element} el
8632 * @param {Number} rowIndex
8633 * @param {Number} columnIndex
8634 * @param {Roo.EventObject} e
8638 * @event celldblclick
8639 * Fires when a cell is double clicked
8640 * @param {Roo.bootstrap.Table} this
8641 * @param {Roo.Element} el
8642 * @param {Number} rowIndex
8643 * @param {Number} columnIndex
8644 * @param {Roo.EventObject} e
8646 "celldblclick" : true,
8649 * Fires when a row is clicked
8650 * @param {Roo.bootstrap.Table} this
8651 * @param {Roo.Element} el
8652 * @param {Number} rowIndex
8653 * @param {Roo.EventObject} e
8657 * @event rowdblclick
8658 * Fires when a row is double clicked
8659 * @param {Roo.bootstrap.Table} this
8660 * @param {Roo.Element} el
8661 * @param {Number} rowIndex
8662 * @param {Roo.EventObject} e
8664 "rowdblclick" : true,
8667 * Fires when a mouseover occur
8668 * @param {Roo.bootstrap.Table} this
8669 * @param {Roo.Element} el
8670 * @param {Number} rowIndex
8671 * @param {Number} columnIndex
8672 * @param {Roo.EventObject} e
8677 * Fires when a mouseout occur
8678 * @param {Roo.bootstrap.Table} this
8679 * @param {Roo.Element} el
8680 * @param {Number} rowIndex
8681 * @param {Number} columnIndex
8682 * @param {Roo.EventObject} e
8687 * Fires when a row is rendered, so you can change add a style to it.
8688 * @param {Roo.bootstrap.Table} this
8689 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
8693 * @event rowsrendered
8694 * Fires when all the rows have been rendered
8695 * @param {Roo.bootstrap.Table} this
8697 'rowsrendered' : true,
8699 * @event contextmenu
8700 * The raw contextmenu event for the entire grid.
8701 * @param {Roo.EventObject} e
8703 "contextmenu" : true,
8705 * @event rowcontextmenu
8706 * Fires when a row is right clicked
8707 * @param {Roo.bootstrap.Table} this
8708 * @param {Number} rowIndex
8709 * @param {Roo.EventObject} e
8711 "rowcontextmenu" : true,
8713 * @event cellcontextmenu
8714 * Fires when a cell is right clicked
8715 * @param {Roo.bootstrap.Table} this
8716 * @param {Number} rowIndex
8717 * @param {Number} cellIndex
8718 * @param {Roo.EventObject} e
8720 "cellcontextmenu" : true,
8722 * @event headercontextmenu
8723 * Fires when a header is right clicked
8724 * @param {Roo.bootstrap.Table} this
8725 * @param {Number} columnIndex
8726 * @param {Roo.EventObject} e
8728 "headercontextmenu" : true,
8731 * The raw mousedown event for the entire grid.
8732 * @param {Roo.EventObject} e
8739 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
8756 rowSelection : false,
8757 cellSelection : false,
8760 // Roo.Element - the tbody
8762 // Roo.Element - thead element
8765 container: false, // used by gridpanel...
8771 auto_hide_footer : false,
8773 getAutoCreate : function()
8775 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8782 // this get's auto added by panel.Grid
8783 if (this.scrollBody) {
8784 cfg.cls += ' table-body-fixed';
8787 cfg.cls += ' table-striped';
8791 cfg.cls += ' table-hover';
8793 if (this.bordered) {
8794 cfg.cls += ' table-bordered';
8796 if (this.condensed) {
8797 cfg.cls += ' table-condensed';
8800 if (this.responsive) {
8801 cfg.cls += ' table-responsive';
8805 cfg.cls+= ' ' +this.cls;
8811 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8814 if(this.store || this.cm){
8815 if(this.headerShow){
8816 cfg.cn.push(this.renderHeader());
8819 cfg.cn.push(this.renderBody());
8821 if(this.footerShow){
8822 cfg.cn.push(this.renderFooter());
8824 // where does this come from?
8825 //cfg.cls+= ' TableGrid';
8828 return { cn : [ cfg ] };
8831 initEvents : function()
8833 if(!this.store || !this.cm){
8836 if (this.selModel) {
8837 this.selModel.initEvents();
8841 //Roo.log('initEvents with ds!!!!');
8843 this.mainBody = this.el.select('tbody', true).first();
8844 this.mainHead = this.el.select('thead', true).first();
8845 this.mainFoot = this.el.select('tfoot', true).first();
8850 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8851 e.on('click', this.sort, this);
8855 // why is this done????? = it breaks dialogs??
8856 //this.parent().el.setStyle('position', 'relative');
8860 this.footer.parentId = this.id;
8861 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8864 this.el.select('tfoot tr td').first().addClass('hide');
8869 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8872 this.store.on('load', this.onLoad, this);
8873 this.store.on('beforeload', this.onBeforeLoad, this);
8874 this.store.on('update', this.onUpdate, this);
8875 this.store.on('add', this.onAdd, this);
8876 this.store.on("clear", this.clear, this);
8878 this.el.on("contextmenu", this.onContextMenu, this);
8881 this.cm.on("headerchange", this.onHeaderChange, this);
8882 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8885 this.mainBody.on("click", this.onClick, this);
8886 this.mainBody.on("dblclick", this.onDblClick, this);
8887 this.mainBody.on('scroll', this.onBodyScroll, this);
8889 // guessing mainbody will work - this relays usually caught by selmodel at present.
8890 this.relayEvents(this.mainBody, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
8897 onContextMenu : function(e, t)
8899 this.processEvent("contextmenu", e);
8902 processEvent : function(name, e)
8904 if (name != 'touchstart' ) {
8905 this.fireEvent(name, e);
8908 var t = e.getTarget();
8910 var cell = Roo.get(t);
8916 if(cell.findParent('tfoot', false, true)){
8920 if(cell.findParent('thead', false, true)){
8922 if(e.getTarget().nodeName.toLowerCase() != 'th'){
8923 cell = Roo.get(t).findParent('th', false, true);
8925 Roo.log("failed to find th in thead?");
8926 Roo.log(e.getTarget());
8931 var cellIndex = cell.dom.cellIndex;
8933 var ename = name == 'touchstart' ? 'click' : name;
8934 this.fireEvent("header" + ename, this, cellIndex, e);
8939 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8940 cell = Roo.get(t).findParent('td', false, true);
8942 Roo.log("failed to find th in tbody?");
8943 Roo.log(e.getTarget());
8948 var row = cell.findParent('tr', false, true);
8949 var cellIndex = cell.dom.cellIndex;
8950 var rowIndex = row.dom.rowIndex - 1;
8954 this.fireEvent("row" + name, this, rowIndex, e);
8958 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8964 onMouseover : function(e, el)
8966 var cell = Roo.get(el);
8972 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8973 cell = cell.findParent('td', false, true);
8976 var row = cell.findParent('tr', false, true);
8977 var cellIndex = cell.dom.cellIndex;
8978 var rowIndex = row.dom.rowIndex - 1; // start from 0
8980 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8984 onMouseout : function(e, el)
8986 var cell = Roo.get(el);
8992 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8993 cell = cell.findParent('td', false, true);
8996 var row = cell.findParent('tr', false, true);
8997 var cellIndex = cell.dom.cellIndex;
8998 var rowIndex = row.dom.rowIndex - 1; // start from 0
9000 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9004 onClick : function(e, el)
9006 var cell = Roo.get(el);
9008 if(!cell || (!this.cellSelection && !this.rowSelection)){
9012 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9013 cell = cell.findParent('td', false, true);
9016 if(!cell || typeof(cell) == 'undefined'){
9020 var row = cell.findParent('tr', false, true);
9022 if(!row || typeof(row) == 'undefined'){
9026 var cellIndex = cell.dom.cellIndex;
9027 var rowIndex = this.getRowIndex(row);
9029 // why??? - should these not be based on SelectionModel?
9030 //if(this.cellSelection){
9031 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9034 //if(this.rowSelection){
9035 this.fireEvent('rowclick', this, row, rowIndex, e);
9040 onDblClick : function(e,el)
9042 var cell = Roo.get(el);
9044 if(!cell || (!this.cellSelection && !this.rowSelection)){
9048 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9049 cell = cell.findParent('td', false, true);
9052 if(!cell || typeof(cell) == 'undefined'){
9056 var row = cell.findParent('tr', false, true);
9058 if(!row || typeof(row) == 'undefined'){
9062 var cellIndex = cell.dom.cellIndex;
9063 var rowIndex = this.getRowIndex(row);
9065 if(this.cellSelection){
9066 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9069 if(this.rowSelection){
9070 this.fireEvent('rowdblclick', this, row, rowIndex, e);
9073 findRowIndex : function(el)
9075 var cell = Roo.get(el);
9079 var row = cell.findParent('tr', false, true);
9081 if(!row || typeof(row) == 'undefined'){
9084 return this.getRowIndex(row);
9086 sort : function(e,el)
9088 var col = Roo.get(el);
9090 if(!col.hasClass('sortable')){
9094 var sort = col.attr('sort');
9097 if(col.select('i', true).first().hasClass('fa-arrow-up')){
9101 this.store.sortInfo = {field : sort, direction : dir};
9104 Roo.log("calling footer first");
9105 this.footer.onClick('first');
9108 this.store.load({ params : { start : 0 } });
9112 renderHeader : function()
9120 this.totalWidth = 0;
9122 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9124 var config = cm.config[i];
9128 cls : 'x-hcol-' + i,
9131 html: cm.getColumnHeader(i)
9134 var tooltip = cm.getColumnTooltip(i);
9136 c.tooltip = tooltip;
9142 if(typeof(config.sortable) != 'undefined' && config.sortable){
9144 c.html = '<i class="fa"></i>' + c.html;
9147 // could use BS4 hidden-..-down
9149 if(typeof(config.lgHeader) != 'undefined'){
9150 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9153 if(typeof(config.mdHeader) != 'undefined'){
9154 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9157 if(typeof(config.smHeader) != 'undefined'){
9158 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9161 if(typeof(config.xsHeader) != 'undefined'){
9162 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9169 if(typeof(config.tooltip) != 'undefined'){
9170 c.tooltip = config.tooltip;
9173 if(typeof(config.colspan) != 'undefined'){
9174 c.colspan = config.colspan;
9177 if(typeof(config.hidden) != 'undefined' && config.hidden){
9178 c.style += ' display:none;';
9181 if(typeof(config.dataIndex) != 'undefined'){
9182 c.sort = config.dataIndex;
9187 if(typeof(config.align) != 'undefined' && config.align.length){
9188 c.style += ' text-align:' + config.align + ';';
9191 if(typeof(config.width) != 'undefined'){
9192 c.style += ' width:' + config.width + 'px;';
9193 this.totalWidth += config.width;
9195 this.totalWidth += 100; // assume minimum of 100 per column?
9198 if(typeof(config.cls) != 'undefined'){
9199 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9202 ['xs','sm','md','lg'].map(function(size){
9204 if(typeof(config[size]) == 'undefined'){
9208 if (!config[size]) { // 0 = hidden
9209 // BS 4 '0' is treated as hide that column and below.
9210 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9214 c.cls += ' col-' + size + '-' + config[size] + (
9215 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9227 renderBody : function()
9237 colspan : this.cm.getColumnCount()
9247 renderFooter : function()
9257 colspan : this.cm.getColumnCount()
9271 // Roo.log('ds onload');
9276 var ds = this.store;
9278 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9279 e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9280 if (_this.store.sortInfo) {
9282 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9283 e.select('i', true).addClass(['fa-arrow-up']);
9286 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9287 e.select('i', true).addClass(['fa-arrow-down']);
9292 var tbody = this.mainBody;
9294 if(ds.getCount() > 0){
9295 ds.data.each(function(d,rowIndex){
9296 var row = this.renderRow(cm, ds, rowIndex);
9298 tbody.createChild(row);
9302 if(row.cellObjects.length){
9303 Roo.each(row.cellObjects, function(r){
9304 _this.renderCellObject(r);
9311 var tfoot = this.el.select('tfoot', true).first();
9313 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
9315 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
9317 var total = this.ds.getTotalCount();
9319 if(this.footer.pageSize < total){
9320 this.mainFoot.show();
9324 Roo.each(this.el.select('tbody td', true).elements, function(e){
9325 e.on('mouseover', _this.onMouseover, _this);
9328 Roo.each(this.el.select('tbody td', true).elements, function(e){
9329 e.on('mouseout', _this.onMouseout, _this);
9331 this.fireEvent('rowsrendered', this);
9337 onUpdate : function(ds,record)
9339 this.refreshRow(record);
9343 onRemove : function(ds, record, index, isUpdate){
9344 if(isUpdate !== true){
9345 this.fireEvent("beforerowremoved", this, index, record);
9347 var bt = this.mainBody.dom;
9349 var rows = this.el.select('tbody > tr', true).elements;
9351 if(typeof(rows[index]) != 'undefined'){
9352 bt.removeChild(rows[index].dom);
9355 // if(bt.rows[index]){
9356 // bt.removeChild(bt.rows[index]);
9359 if(isUpdate !== true){
9360 //this.stripeRows(index);
9361 //this.syncRowHeights(index, index);
9363 this.fireEvent("rowremoved", this, index, record);
9367 onAdd : function(ds, records, rowIndex)
9369 //Roo.log('on Add called');
9370 // - note this does not handle multiple adding very well..
9371 var bt = this.mainBody.dom;
9372 for (var i =0 ; i < records.length;i++) {
9373 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
9374 //Roo.log(records[i]);
9375 //Roo.log(this.store.getAt(rowIndex+i));
9376 this.insertRow(this.store, rowIndex + i, false);
9383 refreshRow : function(record){
9384 var ds = this.store, index;
9385 if(typeof record == 'number'){
9387 record = ds.getAt(index);
9389 index = ds.indexOf(record);
9391 return; // should not happen - but seems to
9394 this.insertRow(ds, index, true);
9396 this.onRemove(ds, record, index+1, true);
9398 //this.syncRowHeights(index, index);
9400 this.fireEvent("rowupdated", this, index, record);
9403 insertRow : function(dm, rowIndex, isUpdate){
9406 this.fireEvent("beforerowsinserted", this, rowIndex);
9408 //var s = this.getScrollState();
9409 var row = this.renderRow(this.cm, this.store, rowIndex);
9410 // insert before rowIndex..
9411 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
9415 if(row.cellObjects.length){
9416 Roo.each(row.cellObjects, function(r){
9417 _this.renderCellObject(r);
9422 this.fireEvent("rowsinserted", this, rowIndex);
9423 //this.syncRowHeights(firstRow, lastRow);
9424 //this.stripeRows(firstRow);
9431 getRowDom : function(rowIndex)
9433 var rows = this.el.select('tbody > tr', true).elements;
9435 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
9438 // returns the object tree for a tr..
9441 renderRow : function(cm, ds, rowIndex)
9443 var d = ds.getAt(rowIndex);
9447 cls : 'x-row-' + rowIndex,
9451 var cellObjects = [];
9453 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9454 var config = cm.config[i];
9456 var renderer = cm.getRenderer(i);
9460 if(typeof(renderer) !== 'undefined'){
9461 value = renderer(d.data[cm.getDataIndex(i)], false, d);
9463 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
9464 // and are rendered into the cells after the row is rendered - using the id for the element.
9466 if(typeof(value) === 'object'){
9476 rowIndex : rowIndex,
9481 this.fireEvent('rowclass', this, rowcfg);
9485 // this might end up displaying HTML?
9486 // this is too messy... - better to only do it on columsn you know are going to be too long
9487 //tooltip : (typeof(value) === 'object') ? '' : value,
9488 cls : rowcfg.rowClass + ' x-col-' + i,
9490 html: (typeof(value) === 'object') ? '' : value
9497 if(typeof(config.colspan) != 'undefined'){
9498 td.colspan = config.colspan;
9501 if(typeof(config.hidden) != 'undefined' && config.hidden){
9502 td.style += ' display:none;';
9505 if(typeof(config.align) != 'undefined' && config.align.length){
9506 td.style += ' text-align:' + config.align + ';';
9508 if(typeof(config.valign) != 'undefined' && config.valign.length){
9509 td.style += ' vertical-align:' + config.valign + ';';
9512 if(typeof(config.width) != 'undefined'){
9513 td.style += ' width:' + config.width + 'px;';
9516 if(typeof(config.cursor) != 'undefined'){
9517 td.style += ' cursor:' + config.cursor + ';';
9520 if(typeof(config.cls) != 'undefined'){
9521 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
9524 ['xs','sm','md','lg'].map(function(size){
9526 if(typeof(config[size]) == 'undefined'){
9532 if (!config[size]) { // 0 = hidden
9533 // BS 4 '0' is treated as hide that column and below.
9534 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
9538 td.cls += ' col-' + size + '-' + config[size] + (
9539 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9549 row.cellObjects = cellObjects;
9557 onBeforeLoad : function()
9566 this.el.select('tbody', true).first().dom.innerHTML = '';
9569 * Show or hide a row.
9570 * @param {Number} rowIndex to show or hide
9571 * @param {Boolean} state hide
9573 setRowVisibility : function(rowIndex, state)
9575 var bt = this.mainBody.dom;
9577 var rows = this.el.select('tbody > tr', true).elements;
9579 if(typeof(rows[rowIndex]) == 'undefined'){
9582 rows[rowIndex].dom.style.display = state ? '' : 'none';
9586 getSelectionModel : function(){
9588 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
9590 return this.selModel;
9593 * Render the Roo.bootstrap object from renderder
9595 renderCellObject : function(r)
9599 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
9601 var t = r.cfg.render(r.container);
9604 Roo.each(r.cfg.cn, function(c){
9606 container: t.getChildContainer(),
9609 _this.renderCellObject(child);
9614 getRowIndex : function(row)
9618 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
9629 * Returns the grid's underlying element = used by panel.Grid
9630 * @return {Element} The element
9632 getGridEl : function(){
9636 * Forces a resize - used by panel.Grid
9637 * @return {Element} The element
9639 autoSize : function()
9641 //var ctr = Roo.get(this.container.dom.parentElement);
9642 var ctr = Roo.get(this.el.dom);
9644 var thd = this.getGridEl().select('thead',true).first();
9645 var tbd = this.getGridEl().select('tbody', true).first();
9646 var tfd = this.getGridEl().select('tfoot', true).first();
9648 var cw = ctr.getWidth();
9649 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
9653 tbd.setWidth(ctr.getWidth());
9654 // if the body has a max height - and then scrolls - we should perhaps set up the height here
9655 // this needs fixing for various usage - currently only hydra job advers I think..
9657 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
9659 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
9662 cw = Math.max(cw, this.totalWidth);
9663 this.getGridEl().select('tbody tr',true).setWidth(cw);
9665 // resize 'expandable coloumn?
9667 return; // we doe not have a view in this design..
9670 onBodyScroll: function()
9672 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
9674 this.mainHead.setStyle({
9675 'position' : 'relative',
9676 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
9682 var scrollHeight = this.mainBody.dom.scrollHeight;
9684 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
9686 var height = this.mainBody.getHeight();
9688 if(scrollHeight - height == scrollTop) {
9690 var total = this.ds.getTotalCount();
9692 if(this.footer.cursor + this.footer.pageSize < total){
9694 this.footer.ds.load({
9696 start : this.footer.cursor + this.footer.pageSize,
9697 limit : this.footer.pageSize
9707 onHeaderChange : function()
9709 var header = this.renderHeader();
9710 var table = this.el.select('table', true).first();
9712 this.mainHead.remove();
9713 this.mainHead = table.createChild(header, this.mainBody, false);
9715 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9716 e.on('click', this.sort, this);
9722 onHiddenChange : function(colModel, colIndex, hidden)
9724 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
9725 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
9727 this.CSS.updateRule(thSelector, "display", "");
9728 this.CSS.updateRule(tdSelector, "display", "");
9731 this.CSS.updateRule(thSelector, "display", "none");
9732 this.CSS.updateRule(tdSelector, "display", "none");
9735 this.onHeaderChange();
9739 setColumnWidth: function(col_index, width)
9741 // width = "md-2 xs-2..."
9742 if(!this.colModel.config[col_index]) {
9746 var w = width.split(" ");
9748 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
9750 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
9753 for(var j = 0; j < w.length; j++) {
9759 var size_cls = w[j].split("-");
9761 if(!Number.isInteger(size_cls[1] * 1)) {
9765 if(!this.colModel.config[col_index][size_cls[0]]) {
9769 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9773 h_row[0].classList.replace(
9774 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9775 "col-"+size_cls[0]+"-"+size_cls[1]
9778 for(var i = 0; i < rows.length; i++) {
9780 var size_cls = w[j].split("-");
9782 if(!Number.isInteger(size_cls[1] * 1)) {
9786 if(!this.colModel.config[col_index][size_cls[0]]) {
9790 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9794 rows[i].classList.replace(
9795 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9796 "col-"+size_cls[0]+"-"+size_cls[1]
9800 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
9810 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
9811 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;/*
9819 * @class Roo.bootstrap.TableCell
9820 * @extends Roo.bootstrap.Component
9821 * Bootstrap TableCell class
9822 * @cfg {String} html cell contain text
9823 * @cfg {String} cls cell class
9824 * @cfg {String} tag cell tag (td|th) default td
9825 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
9826 * @cfg {String} align Aligns the content in a cell
9827 * @cfg {String} axis Categorizes cells
9828 * @cfg {String} bgcolor Specifies the background color of a cell
9829 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9830 * @cfg {Number} colspan Specifies the number of columns a cell should span
9831 * @cfg {String} headers Specifies one or more header cells a cell is related to
9832 * @cfg {Number} height Sets the height of a cell
9833 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
9834 * @cfg {Number} rowspan Sets the number of rows a cell should span
9835 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
9836 * @cfg {String} valign Vertical aligns the content in a cell
9837 * @cfg {Number} width Specifies the width of a cell
9840 * Create a new TableCell
9841 * @param {Object} config The config object
9844 Roo.bootstrap.TableCell = function(config){
9845 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
9848 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
9868 getAutoCreate : function(){
9869 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
9889 cfg.align=this.align
9895 cfg.bgcolor=this.bgcolor
9898 cfg.charoff=this.charoff
9901 cfg.colspan=this.colspan
9904 cfg.headers=this.headers
9907 cfg.height=this.height
9910 cfg.nowrap=this.nowrap
9913 cfg.rowspan=this.rowspan
9916 cfg.scope=this.scope
9919 cfg.valign=this.valign
9922 cfg.width=this.width
9941 * @class Roo.bootstrap.TableRow
9942 * @extends Roo.bootstrap.Component
9943 * Bootstrap TableRow class
9944 * @cfg {String} cls row class
9945 * @cfg {String} align Aligns the content in a table row
9946 * @cfg {String} bgcolor Specifies a background color for a table row
9947 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9948 * @cfg {String} valign Vertical aligns the content in a table row
9951 * Create a new TableRow
9952 * @param {Object} config The config object
9955 Roo.bootstrap.TableRow = function(config){
9956 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9959 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
9967 getAutoCreate : function(){
9968 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9978 cfg.align = this.align;
9981 cfg.bgcolor = this.bgcolor;
9984 cfg.charoff = this.charoff;
9987 cfg.valign = this.valign;
10005 * @class Roo.bootstrap.TableBody
10006 * @extends Roo.bootstrap.Component
10007 * Bootstrap TableBody class
10008 * @cfg {String} cls element class
10009 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10010 * @cfg {String} align Aligns the content inside the element
10011 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10012 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10015 * Create a new TableBody
10016 * @param {Object} config The config object
10019 Roo.bootstrap.TableBody = function(config){
10020 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10023 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
10031 getAutoCreate : function(){
10032 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10042 cfg.tag = this.tag;
10046 cfg.align = this.align;
10049 cfg.charoff = this.charoff;
10052 cfg.valign = this.valign;
10059 // initEvents : function()
10062 // if(!this.store){
10066 // this.store = Roo.factory(this.store, Roo.data);
10067 // this.store.on('load', this.onLoad, this);
10069 // this.store.load();
10073 // onLoad: function ()
10075 // this.fireEvent('load', this);
10085 * Ext JS Library 1.1.1
10086 * Copyright(c) 2006-2007, Ext JS, LLC.
10088 * Originally Released Under LGPL - original licence link has changed is not relivant.
10091 * <script type="text/javascript">
10094 // as we use this in bootstrap.
10095 Roo.namespace('Roo.form');
10097 * @class Roo.form.Action
10098 * Internal Class used to handle form actions
10100 * @param {Roo.form.BasicForm} el The form element or its id
10101 * @param {Object} config Configuration options
10106 // define the action interface
10107 Roo.form.Action = function(form, options){
10109 this.options = options || {};
10112 * Client Validation Failed
10115 Roo.form.Action.CLIENT_INVALID = 'client';
10117 * Server Validation Failed
10120 Roo.form.Action.SERVER_INVALID = 'server';
10122 * Connect to Server Failed
10125 Roo.form.Action.CONNECT_FAILURE = 'connect';
10127 * Reading Data from Server Failed
10130 Roo.form.Action.LOAD_FAILURE = 'load';
10132 Roo.form.Action.prototype = {
10134 failureType : undefined,
10135 response : undefined,
10136 result : undefined,
10138 // interface method
10139 run : function(options){
10143 // interface method
10144 success : function(response){
10148 // interface method
10149 handleResponse : function(response){
10153 // default connection failure
10154 failure : function(response){
10156 this.response = response;
10157 this.failureType = Roo.form.Action.CONNECT_FAILURE;
10158 this.form.afterAction(this, false);
10161 processResponse : function(response){
10162 this.response = response;
10163 if(!response.responseText){
10166 this.result = this.handleResponse(response);
10167 return this.result;
10170 // utility functions used internally
10171 getUrl : function(appendParams){
10172 var url = this.options.url || this.form.url || this.form.el.dom.action;
10174 var p = this.getParams();
10176 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
10182 getMethod : function(){
10183 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
10186 getParams : function(){
10187 var bp = this.form.baseParams;
10188 var p = this.options.params;
10190 if(typeof p == "object"){
10191 p = Roo.urlEncode(Roo.applyIf(p, bp));
10192 }else if(typeof p == 'string' && bp){
10193 p += '&' + Roo.urlEncode(bp);
10196 p = Roo.urlEncode(bp);
10201 createCallback : function(){
10203 success: this.success,
10204 failure: this.failure,
10206 timeout: (this.form.timeout*1000),
10207 upload: this.form.fileUpload ? this.success : undefined
10212 Roo.form.Action.Submit = function(form, options){
10213 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
10216 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
10219 haveProgress : false,
10220 uploadComplete : false,
10222 // uploadProgress indicator.
10223 uploadProgress : function()
10225 if (!this.form.progressUrl) {
10229 if (!this.haveProgress) {
10230 Roo.MessageBox.progress("Uploading", "Uploading");
10232 if (this.uploadComplete) {
10233 Roo.MessageBox.hide();
10237 this.haveProgress = true;
10239 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
10241 var c = new Roo.data.Connection();
10243 url : this.form.progressUrl,
10248 success : function(req){
10249 //console.log(data);
10253 rdata = Roo.decode(req.responseText)
10255 Roo.log("Invalid data from server..");
10259 if (!rdata || !rdata.success) {
10261 Roo.MessageBox.alert(Roo.encode(rdata));
10264 var data = rdata.data;
10266 if (this.uploadComplete) {
10267 Roo.MessageBox.hide();
10272 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
10273 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
10276 this.uploadProgress.defer(2000,this);
10279 failure: function(data) {
10280 Roo.log('progress url failed ');
10291 // run get Values on the form, so it syncs any secondary forms.
10292 this.form.getValues();
10294 var o = this.options;
10295 var method = this.getMethod();
10296 var isPost = method == 'POST';
10297 if(o.clientValidation === false || this.form.isValid()){
10299 if (this.form.progressUrl) {
10300 this.form.findField('UPLOAD_IDENTIFIER').setValue(
10301 (new Date() * 1) + '' + Math.random());
10306 Roo.Ajax.request(Roo.apply(this.createCallback(), {
10307 form:this.form.el.dom,
10308 url:this.getUrl(!isPost),
10310 params:isPost ? this.getParams() : null,
10311 isUpload: this.form.fileUpload,
10312 formData : this.form.formData
10315 this.uploadProgress();
10317 }else if (o.clientValidation !== false){ // client validation failed
10318 this.failureType = Roo.form.Action.CLIENT_INVALID;
10319 this.form.afterAction(this, false);
10323 success : function(response)
10325 this.uploadComplete= true;
10326 if (this.haveProgress) {
10327 Roo.MessageBox.hide();
10331 var result = this.processResponse(response);
10332 if(result === true || result.success){
10333 this.form.afterAction(this, true);
10337 this.form.markInvalid(result.errors);
10338 this.failureType = Roo.form.Action.SERVER_INVALID;
10340 this.form.afterAction(this, false);
10342 failure : function(response)
10344 this.uploadComplete= true;
10345 if (this.haveProgress) {
10346 Roo.MessageBox.hide();
10349 this.response = response;
10350 this.failureType = Roo.form.Action.CONNECT_FAILURE;
10351 this.form.afterAction(this, false);
10354 handleResponse : function(response){
10355 if(this.form.errorReader){
10356 var rs = this.form.errorReader.read(response);
10359 for(var i = 0, len = rs.records.length; i < len; i++) {
10360 var r = rs.records[i];
10361 errors[i] = r.data;
10364 if(errors.length < 1){
10368 success : rs.success,
10374 ret = Roo.decode(response.responseText);
10378 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
10388 Roo.form.Action.Load = function(form, options){
10389 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
10390 this.reader = this.form.reader;
10393 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
10398 Roo.Ajax.request(Roo.apply(
10399 this.createCallback(), {
10400 method:this.getMethod(),
10401 url:this.getUrl(false),
10402 params:this.getParams()
10406 success : function(response){
10408 var result = this.processResponse(response);
10409 if(result === true || !result.success || !result.data){
10410 this.failureType = Roo.form.Action.LOAD_FAILURE;
10411 this.form.afterAction(this, false);
10414 this.form.clearInvalid();
10415 this.form.setValues(result.data);
10416 this.form.afterAction(this, true);
10419 handleResponse : function(response){
10420 if(this.form.reader){
10421 var rs = this.form.reader.read(response);
10422 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
10424 success : rs.success,
10428 return Roo.decode(response.responseText);
10432 Roo.form.Action.ACTION_TYPES = {
10433 'load' : Roo.form.Action.Load,
10434 'submit' : Roo.form.Action.Submit
10443 * @class Roo.bootstrap.Form
10444 * @extends Roo.bootstrap.Component
10445 * Bootstrap Form class
10446 * @cfg {String} method GET | POST (default POST)
10447 * @cfg {String} labelAlign top | left (default top)
10448 * @cfg {String} align left | right - for navbars
10449 * @cfg {Boolean} loadMask load mask when submit (default true)
10453 * Create a new Form
10454 * @param {Object} config The config object
10458 Roo.bootstrap.Form = function(config){
10460 Roo.bootstrap.Form.superclass.constructor.call(this, config);
10462 Roo.bootstrap.Form.popover.apply();
10466 * @event clientvalidation
10467 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
10468 * @param {Form} this
10469 * @param {Boolean} valid true if the form has passed client-side validation
10471 clientvalidation: true,
10473 * @event beforeaction
10474 * Fires before any action is performed. Return false to cancel the action.
10475 * @param {Form} this
10476 * @param {Action} action The action to be performed
10478 beforeaction: true,
10480 * @event actionfailed
10481 * Fires when an action fails.
10482 * @param {Form} this
10483 * @param {Action} action The action that failed
10485 actionfailed : true,
10487 * @event actioncomplete
10488 * Fires when an action is completed.
10489 * @param {Form} this
10490 * @param {Action} action The action that completed
10492 actioncomplete : true
10496 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
10499 * @cfg {String} method
10500 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
10504 * @cfg {String} url
10505 * The URL to use for form actions if one isn't supplied in the action options.
10508 * @cfg {Boolean} fileUpload
10509 * Set to true if this form is a file upload.
10513 * @cfg {Object} baseParams
10514 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
10518 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
10522 * @cfg {Sting} align (left|right) for navbar forms
10527 activeAction : null,
10530 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
10531 * element by passing it or its id or mask the form itself by passing in true.
10534 waitMsgTarget : false,
10539 * @cfg {Boolean} errorMask (true|false) default false
10544 * @cfg {Number} maskOffset Default 100
10549 * @cfg {Boolean} maskBody
10553 getAutoCreate : function(){
10557 method : this.method || 'POST',
10558 id : this.id || Roo.id(),
10561 if (this.parent().xtype.match(/^Nav/)) {
10562 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
10566 if (this.labelAlign == 'left' ) {
10567 cfg.cls += ' form-horizontal';
10573 initEvents : function()
10575 this.el.on('submit', this.onSubmit, this);
10576 // this was added as random key presses on the form where triggering form submit.
10577 this.el.on('keypress', function(e) {
10578 if (e.getCharCode() != 13) {
10581 // we might need to allow it for textareas.. and some other items.
10582 // check e.getTarget().
10584 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
10588 Roo.log("keypress blocked");
10590 e.preventDefault();
10596 onSubmit : function(e){
10601 * Returns true if client-side validation on the form is successful.
10604 isValid : function(){
10605 var items = this.getItems();
10607 var target = false;
10609 items.each(function(f){
10615 Roo.log('invalid field: ' + f.name);
10619 if(!target && f.el.isVisible(true)){
10625 if(this.errorMask && !valid){
10626 Roo.bootstrap.Form.popover.mask(this, target);
10633 * Returns true if any fields in this form have changed since their original load.
10636 isDirty : function(){
10638 var items = this.getItems();
10639 items.each(function(f){
10649 * Performs a predefined action (submit or load) or custom actions you define on this form.
10650 * @param {String} actionName The name of the action type
10651 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
10652 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
10653 * accept other config options):
10655 Property Type Description
10656 ---------------- --------------- ----------------------------------------------------------------------------------
10657 url String The url for the action (defaults to the form's url)
10658 method String The form method to use (defaults to the form's method, or POST if not defined)
10659 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
10660 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
10661 validate the form on the client (defaults to false)
10663 * @return {BasicForm} this
10665 doAction : function(action, options){
10666 if(typeof action == 'string'){
10667 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
10669 if(this.fireEvent('beforeaction', this, action) !== false){
10670 this.beforeAction(action);
10671 action.run.defer(100, action);
10677 beforeAction : function(action){
10678 var o = action.options;
10683 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
10685 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10688 // not really supported yet.. ??
10690 //if(this.waitMsgTarget === true){
10691 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10692 //}else if(this.waitMsgTarget){
10693 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
10694 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
10696 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
10702 afterAction : function(action, success){
10703 this.activeAction = null;
10704 var o = action.options;
10709 Roo.get(document.body).unmask();
10715 //if(this.waitMsgTarget === true){
10716 // this.el.unmask();
10717 //}else if(this.waitMsgTarget){
10718 // this.waitMsgTarget.unmask();
10720 // Roo.MessageBox.updateProgress(1);
10721 // Roo.MessageBox.hide();
10728 Roo.callback(o.success, o.scope, [this, action]);
10729 this.fireEvent('actioncomplete', this, action);
10733 // failure condition..
10734 // we have a scenario where updates need confirming.
10735 // eg. if a locking scenario exists..
10736 // we look for { errors : { needs_confirm : true }} in the response.
10738 (typeof(action.result) != 'undefined') &&
10739 (typeof(action.result.errors) != 'undefined') &&
10740 (typeof(action.result.errors.needs_confirm) != 'undefined')
10743 Roo.log("not supported yet");
10746 Roo.MessageBox.confirm(
10747 "Change requires confirmation",
10748 action.result.errorMsg,
10753 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
10763 Roo.callback(o.failure, o.scope, [this, action]);
10764 // show an error message if no failed handler is set..
10765 if (!this.hasListener('actionfailed')) {
10766 Roo.log("need to add dialog support");
10768 Roo.MessageBox.alert("Error",
10769 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
10770 action.result.errorMsg :
10771 "Saving Failed, please check your entries or try again"
10776 this.fireEvent('actionfailed', this, action);
10781 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
10782 * @param {String} id The value to search for
10785 findField : function(id){
10786 var items = this.getItems();
10787 var field = items.get(id);
10789 items.each(function(f){
10790 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
10797 return field || null;
10800 * Mark fields in this form invalid in bulk.
10801 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
10802 * @return {BasicForm} this
10804 markInvalid : function(errors){
10805 if(errors instanceof Array){
10806 for(var i = 0, len = errors.length; i < len; i++){
10807 var fieldError = errors[i];
10808 var f = this.findField(fieldError.id);
10810 f.markInvalid(fieldError.msg);
10816 if(typeof errors[id] != 'function' && (field = this.findField(id))){
10817 field.markInvalid(errors[id]);
10821 //Roo.each(this.childForms || [], function (f) {
10822 // f.markInvalid(errors);
10829 * Set values for fields in this form in bulk.
10830 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
10831 * @return {BasicForm} this
10833 setValues : function(values){
10834 if(values instanceof Array){ // array of objects
10835 for(var i = 0, len = values.length; i < len; i++){
10837 var f = this.findField(v.id);
10839 f.setValue(v.value);
10840 if(this.trackResetOnLoad){
10841 f.originalValue = f.getValue();
10845 }else{ // object hash
10848 if(typeof values[id] != 'function' && (field = this.findField(id))){
10850 if (field.setFromData &&
10851 field.valueField &&
10852 field.displayField &&
10853 // combos' with local stores can
10854 // be queried via setValue()
10855 // to set their value..
10856 (field.store && !field.store.isLocal)
10860 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
10861 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
10862 field.setFromData(sd);
10864 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
10866 field.setFromData(values);
10869 field.setValue(values[id]);
10873 if(this.trackResetOnLoad){
10874 field.originalValue = field.getValue();
10880 //Roo.each(this.childForms || [], function (f) {
10881 // f.setValues(values);
10888 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
10889 * they are returned as an array.
10890 * @param {Boolean} asString
10893 getValues : function(asString){
10894 //if (this.childForms) {
10895 // copy values from the child forms
10896 // Roo.each(this.childForms, function (f) {
10897 // this.setValues(f.getValues());
10903 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10904 if(asString === true){
10907 return Roo.urlDecode(fs);
10911 * Returns the fields in this form as an object with key/value pairs.
10912 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10915 getFieldValues : function(with_hidden)
10917 var items = this.getItems();
10919 items.each(function(f){
10921 if (!f.getName()) {
10925 var v = f.getValue();
10927 if (f.inputType =='radio') {
10928 if (typeof(ret[f.getName()]) == 'undefined') {
10929 ret[f.getName()] = ''; // empty..
10932 if (!f.el.dom.checked) {
10936 v = f.el.dom.value;
10940 if(f.xtype == 'MoneyField'){
10941 ret[f.currencyName] = f.getCurrency();
10944 // not sure if this supported any more..
10945 if ((typeof(v) == 'object') && f.getRawValue) {
10946 v = f.getRawValue() ; // dates..
10948 // combo boxes where name != hiddenName...
10949 if (f.name !== false && f.name != '' && f.name != f.getName()) {
10950 ret[f.name] = f.getRawValue();
10952 ret[f.getName()] = v;
10959 * Clears all invalid messages in this form.
10960 * @return {BasicForm} this
10962 clearInvalid : function(){
10963 var items = this.getItems();
10965 items.each(function(f){
10973 * Resets this form.
10974 * @return {BasicForm} this
10976 reset : function(){
10977 var items = this.getItems();
10978 items.each(function(f){
10982 Roo.each(this.childForms || [], function (f) {
10990 getItems : function()
10992 var r=new Roo.util.MixedCollection(false, function(o){
10993 return o.id || (o.id = Roo.id());
10995 var iter = function(el) {
11002 Roo.each(el.items,function(e) {
11011 hideFields : function(items)
11013 Roo.each(items, function(i){
11015 var f = this.findField(i);
11026 showFields : function(items)
11028 Roo.each(items, function(i){
11030 var f = this.findField(i);
11043 Roo.apply(Roo.bootstrap.Form, {
11059 intervalID : false,
11065 if(this.isApplied){
11070 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11071 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11072 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11073 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11076 this.maskEl.top.enableDisplayMode("block");
11077 this.maskEl.left.enableDisplayMode("block");
11078 this.maskEl.bottom.enableDisplayMode("block");
11079 this.maskEl.right.enableDisplayMode("block");
11081 this.toolTip = new Roo.bootstrap.Tooltip({
11082 cls : 'roo-form-error-popover',
11084 'left' : ['r-l', [-2,0], 'right'],
11085 'right' : ['l-r', [2,0], 'left'],
11086 'bottom' : ['tl-bl', [0,2], 'top'],
11087 'top' : [ 'bl-tl', [0,-2], 'bottom']
11091 this.toolTip.render(Roo.get(document.body));
11093 this.toolTip.el.enableDisplayMode("block");
11095 Roo.get(document.body).on('click', function(){
11099 Roo.get(document.body).on('touchstart', function(){
11103 this.isApplied = true
11106 mask : function(form, target)
11110 this.target = target;
11112 if(!this.form.errorMask || !target.el){
11116 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
11118 Roo.log(scrollable);
11120 var ot = this.target.el.calcOffsetsTo(scrollable);
11122 var scrollTo = ot[1] - this.form.maskOffset;
11124 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
11126 scrollable.scrollTo('top', scrollTo);
11128 var box = this.target.el.getBox();
11130 var zIndex = Roo.bootstrap.Modal.zIndex++;
11133 this.maskEl.top.setStyle('position', 'absolute');
11134 this.maskEl.top.setStyle('z-index', zIndex);
11135 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
11136 this.maskEl.top.setLeft(0);
11137 this.maskEl.top.setTop(0);
11138 this.maskEl.top.show();
11140 this.maskEl.left.setStyle('position', 'absolute');
11141 this.maskEl.left.setStyle('z-index', zIndex);
11142 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
11143 this.maskEl.left.setLeft(0);
11144 this.maskEl.left.setTop(box.y - this.padding);
11145 this.maskEl.left.show();
11147 this.maskEl.bottom.setStyle('position', 'absolute');
11148 this.maskEl.bottom.setStyle('z-index', zIndex);
11149 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
11150 this.maskEl.bottom.setLeft(0);
11151 this.maskEl.bottom.setTop(box.bottom + this.padding);
11152 this.maskEl.bottom.show();
11154 this.maskEl.right.setStyle('position', 'absolute');
11155 this.maskEl.right.setStyle('z-index', zIndex);
11156 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
11157 this.maskEl.right.setLeft(box.right + this.padding);
11158 this.maskEl.right.setTop(box.y - this.padding);
11159 this.maskEl.right.show();
11161 this.toolTip.bindEl = this.target.el;
11163 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
11165 var tip = this.target.blankText;
11167 if(this.target.getValue() !== '' ) {
11169 if (this.target.invalidText.length) {
11170 tip = this.target.invalidText;
11171 } else if (this.target.regexText.length){
11172 tip = this.target.regexText;
11176 this.toolTip.show(tip);
11178 this.intervalID = window.setInterval(function() {
11179 Roo.bootstrap.Form.popover.unmask();
11182 window.onwheel = function(){ return false;};
11184 (function(){ this.isMasked = true; }).defer(500, this);
11188 unmask : function()
11190 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
11194 this.maskEl.top.setStyle('position', 'absolute');
11195 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
11196 this.maskEl.top.hide();
11198 this.maskEl.left.setStyle('position', 'absolute');
11199 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
11200 this.maskEl.left.hide();
11202 this.maskEl.bottom.setStyle('position', 'absolute');
11203 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
11204 this.maskEl.bottom.hide();
11206 this.maskEl.right.setStyle('position', 'absolute');
11207 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
11208 this.maskEl.right.hide();
11210 this.toolTip.hide();
11212 this.toolTip.el.hide();
11214 window.onwheel = function(){ return true;};
11216 if(this.intervalID){
11217 window.clearInterval(this.intervalID);
11218 this.intervalID = false;
11221 this.isMasked = false;
11231 * Ext JS Library 1.1.1
11232 * Copyright(c) 2006-2007, Ext JS, LLC.
11234 * Originally Released Under LGPL - original licence link has changed is not relivant.
11237 * <script type="text/javascript">
11240 * @class Roo.form.VTypes
11241 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
11244 Roo.form.VTypes = function(){
11245 // closure these in so they are only created once.
11246 var alpha = /^[a-zA-Z_]+$/;
11247 var alphanum = /^[a-zA-Z0-9_]+$/;
11248 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
11249 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
11251 // All these messages and functions are configurable
11254 * The function used to validate email addresses
11255 * @param {String} value The email address
11257 'email' : function(v){
11258 return email.test(v);
11261 * The error text to display when the email validation function returns false
11264 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
11266 * The keystroke filter mask to be applied on email input
11269 'emailMask' : /[a-z0-9_\.\-@]/i,
11272 * The function used to validate URLs
11273 * @param {String} value The URL
11275 'url' : function(v){
11276 return url.test(v);
11279 * The error text to display when the url validation function returns false
11282 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
11285 * The function used to validate alpha values
11286 * @param {String} value The value
11288 'alpha' : function(v){
11289 return alpha.test(v);
11292 * The error text to display when the alpha validation function returns false
11295 'alphaText' : 'This field should only contain letters and _',
11297 * The keystroke filter mask to be applied on alpha input
11300 'alphaMask' : /[a-z_]/i,
11303 * The function used to validate alphanumeric values
11304 * @param {String} value The value
11306 'alphanum' : function(v){
11307 return alphanum.test(v);
11310 * The error text to display when the alphanumeric validation function returns false
11313 'alphanumText' : 'This field should only contain letters, numbers and _',
11315 * The keystroke filter mask to be applied on alphanumeric input
11318 'alphanumMask' : /[a-z0-9_]/i
11328 * @class Roo.bootstrap.Input
11329 * @extends Roo.bootstrap.Component
11330 * Bootstrap Input class
11331 * @cfg {Boolean} disabled is it disabled
11332 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
11333 * @cfg {String} name name of the input
11334 * @cfg {string} fieldLabel - the label associated
11335 * @cfg {string} placeholder - placeholder to put in text.
11336 * @cfg {string} before - input group add on before
11337 * @cfg {string} after - input group add on after
11338 * @cfg {string} size - (lg|sm) or leave empty..
11339 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
11340 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
11341 * @cfg {Number} md colspan out of 12 for computer-sized screens
11342 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
11343 * @cfg {string} value default value of the input
11344 * @cfg {Number} labelWidth set the width of label
11345 * @cfg {Number} labellg set the width of label (1-12)
11346 * @cfg {Number} labelmd set the width of label (1-12)
11347 * @cfg {Number} labelsm set the width of label (1-12)
11348 * @cfg {Number} labelxs set the width of label (1-12)
11349 * @cfg {String} labelAlign (top|left)
11350 * @cfg {Boolean} readOnly Specifies that the field should be read-only
11351 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
11352 * @cfg {String} indicatorpos (left|right) default left
11353 * @cfg {String} capture (user|camera) use for file input only. (default empty)
11354 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
11355 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
11357 * @cfg {String} align (left|center|right) Default left
11358 * @cfg {Boolean} forceFeedback (true|false) Default false
11361 * Create a new Input
11362 * @param {Object} config The config object
11365 Roo.bootstrap.Input = function(config){
11367 Roo.bootstrap.Input.superclass.constructor.call(this, config);
11372 * Fires when this field receives input focus.
11373 * @param {Roo.form.Field} this
11378 * Fires when this field loses input focus.
11379 * @param {Roo.form.Field} this
11383 * @event specialkey
11384 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
11385 * {@link Roo.EventObject#getKey} to determine which key was pressed.
11386 * @param {Roo.form.Field} this
11387 * @param {Roo.EventObject} e The event object
11392 * Fires just before the field blurs if the field value has changed.
11393 * @param {Roo.form.Field} this
11394 * @param {Mixed} newValue The new value
11395 * @param {Mixed} oldValue The original value
11400 * Fires after the field has been marked as invalid.
11401 * @param {Roo.form.Field} this
11402 * @param {String} msg The validation message
11407 * Fires after the field has been validated with no errors.
11408 * @param {Roo.form.Field} this
11413 * Fires after the key up
11414 * @param {Roo.form.Field} this
11415 * @param {Roo.EventObject} e The event Object
11420 * Fires after the user pastes into input
11421 * @param {Roo.form.Field} this
11422 * @param {Roo.EventObject} e The event Object
11428 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
11430 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
11431 automatic validation (defaults to "keyup").
11433 validationEvent : "keyup",
11435 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
11437 validateOnBlur : true,
11439 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
11441 validationDelay : 250,
11443 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
11445 focusClass : "x-form-focus", // not needed???
11449 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11451 invalidClass : "has-warning",
11454 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11456 validClass : "has-success",
11459 * @cfg {Boolean} hasFeedback (true|false) default true
11461 hasFeedback : true,
11464 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11466 invalidFeedbackClass : "glyphicon-warning-sign",
11469 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11471 validFeedbackClass : "glyphicon-ok",
11474 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
11476 selectOnFocus : false,
11479 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
11483 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
11488 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
11490 disableKeyFilter : false,
11493 * @cfg {Boolean} disabled True to disable the field (defaults to false).
11497 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
11501 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
11503 blankText : "Please complete this mandatory field",
11506 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
11510 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
11512 maxLength : Number.MAX_VALUE,
11514 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
11516 minLengthText : "The minimum length for this field is {0}",
11518 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
11520 maxLengthText : "The maximum length for this field is {0}",
11524 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
11525 * If available, this function will be called only after the basic validators all return true, and will be passed the
11526 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
11530 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
11531 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
11532 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
11536 * @cfg {String} regexText -- Depricated - use Invalid Text
11541 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
11547 autocomplete: false,
11551 inputType : 'text',
11554 placeholder: false,
11559 preventMark: false,
11560 isFormField : true,
11563 labelAlign : false,
11566 formatedValue : false,
11567 forceFeedback : false,
11569 indicatorpos : 'left',
11579 parentLabelAlign : function()
11582 while (parent.parent()) {
11583 parent = parent.parent();
11584 if (typeof(parent.labelAlign) !='undefined') {
11585 return parent.labelAlign;
11592 getAutoCreate : function()
11594 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11600 if(this.inputType != 'hidden'){
11601 cfg.cls = 'form-group' //input-group
11607 type : this.inputType,
11608 value : this.value,
11609 cls : 'form-control',
11610 placeholder : this.placeholder || '',
11611 autocomplete : this.autocomplete || 'new-password'
11613 if (this.inputType == 'file') {
11614 input.style = 'overflow:hidden'; // why not in CSS?
11617 if(this.capture.length){
11618 input.capture = this.capture;
11621 if(this.accept.length){
11622 input.accept = this.accept + "/*";
11626 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
11629 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11630 input.maxLength = this.maxLength;
11633 if (this.disabled) {
11634 input.disabled=true;
11637 if (this.readOnly) {
11638 input.readonly=true;
11642 input.name = this.name;
11646 input.cls += ' input-' + this.size;
11650 ['xs','sm','md','lg'].map(function(size){
11651 if (settings[size]) {
11652 cfg.cls += ' col-' + size + '-' + settings[size];
11656 var inputblock = input;
11660 cls: 'glyphicon form-control-feedback'
11663 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11666 cls : 'has-feedback',
11674 if (this.before || this.after) {
11677 cls : 'input-group',
11681 if (this.before && typeof(this.before) == 'string') {
11683 inputblock.cn.push({
11685 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
11689 if (this.before && typeof(this.before) == 'object') {
11690 this.before = Roo.factory(this.before);
11692 inputblock.cn.push({
11694 cls : 'roo-input-before input-group-prepend input-group-' +
11695 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
11699 inputblock.cn.push(input);
11701 if (this.after && typeof(this.after) == 'string') {
11702 inputblock.cn.push({
11704 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
11708 if (this.after && typeof(this.after) == 'object') {
11709 this.after = Roo.factory(this.after);
11711 inputblock.cn.push({
11713 cls : 'roo-input-after input-group-append input-group-' +
11714 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
11718 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11719 inputblock.cls += ' has-feedback';
11720 inputblock.cn.push(feedback);
11725 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11726 tooltip : 'This field is required'
11728 if (this.allowBlank ) {
11729 indicator.style = this.allowBlank ? ' display:none' : '';
11731 if (align ==='left' && this.fieldLabel.length) {
11733 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
11740 cls : 'control-label col-form-label',
11741 html : this.fieldLabel
11752 var labelCfg = cfg.cn[1];
11753 var contentCfg = cfg.cn[2];
11755 if(this.indicatorpos == 'right'){
11760 cls : 'control-label col-form-label',
11764 html : this.fieldLabel
11778 labelCfg = cfg.cn[0];
11779 contentCfg = cfg.cn[1];
11783 if(this.labelWidth > 12){
11784 labelCfg.style = "width: " + this.labelWidth + 'px';
11787 if(this.labelWidth < 13 && this.labelmd == 0){
11788 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
11791 if(this.labellg > 0){
11792 labelCfg.cls += ' col-lg-' + this.labellg;
11793 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11796 if(this.labelmd > 0){
11797 labelCfg.cls += ' col-md-' + this.labelmd;
11798 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11801 if(this.labelsm > 0){
11802 labelCfg.cls += ' col-sm-' + this.labelsm;
11803 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11806 if(this.labelxs > 0){
11807 labelCfg.cls += ' col-xs-' + this.labelxs;
11808 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11812 } else if ( this.fieldLabel.length) {
11819 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
11820 tooltip : 'This field is required',
11821 style : this.allowBlank ? ' display:none' : ''
11825 //cls : 'input-group-addon',
11826 html : this.fieldLabel
11834 if(this.indicatorpos == 'right'){
11839 //cls : 'input-group-addon',
11840 html : this.fieldLabel
11845 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
11846 tooltip : 'This field is required',
11847 style : this.allowBlank ? ' display:none' : ''
11867 if (this.parentType === 'Navbar' && this.parent().bar) {
11868 cfg.cls += ' navbar-form';
11871 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
11872 // on BS4 we do this only if not form
11873 cfg.cls += ' navbar-form';
11881 * return the real input element.
11883 inputEl: function ()
11885 return this.el.select('input.form-control',true).first();
11888 tooltipEl : function()
11890 return this.inputEl();
11893 indicatorEl : function()
11895 if (Roo.bootstrap.version == 4) {
11896 return false; // not enabled in v4 yet.
11899 var indicator = this.el.select('i.roo-required-indicator',true).first();
11909 setDisabled : function(v)
11911 var i = this.inputEl().dom;
11913 i.removeAttribute('disabled');
11917 i.setAttribute('disabled','true');
11919 initEvents : function()
11922 this.inputEl().on("keydown" , this.fireKey, this);
11923 this.inputEl().on("focus", this.onFocus, this);
11924 this.inputEl().on("blur", this.onBlur, this);
11926 this.inputEl().relayEvent('keyup', this);
11927 this.inputEl().relayEvent('paste', this);
11929 this.indicator = this.indicatorEl();
11931 if(this.indicator){
11932 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
11935 // reference to original value for reset
11936 this.originalValue = this.getValue();
11937 //Roo.form.TextField.superclass.initEvents.call(this);
11938 if(this.validationEvent == 'keyup'){
11939 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11940 this.inputEl().on('keyup', this.filterValidation, this);
11942 else if(this.validationEvent !== false){
11943 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11946 if(this.selectOnFocus){
11947 this.on("focus", this.preFocus, this);
11950 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11951 this.inputEl().on("keypress", this.filterKeys, this);
11953 this.inputEl().relayEvent('keypress', this);
11956 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
11957 this.el.on("click", this.autoSize, this);
11960 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11961 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11964 if (typeof(this.before) == 'object') {
11965 this.before.render(this.el.select('.roo-input-before',true).first());
11967 if (typeof(this.after) == 'object') {
11968 this.after.render(this.el.select('.roo-input-after',true).first());
11971 this.inputEl().on('change', this.onChange, this);
11974 filterValidation : function(e){
11975 if(!e.isNavKeyPress()){
11976 this.validationTask.delay(this.validationDelay);
11980 * Validates the field value
11981 * @return {Boolean} True if the value is valid, else false
11983 validate : function(){
11984 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11985 if(this.disabled || this.validateValue(this.getRawValue())){
11990 this.markInvalid();
11996 * Validates a value according to the field's validation rules and marks the field as invalid
11997 * if the validation fails
11998 * @param {Mixed} value The value to validate
11999 * @return {Boolean} True if the value is valid, else false
12001 validateValue : function(value)
12003 if(this.getVisibilityEl().hasClass('hidden')){
12007 if(value.length < 1) { // if it's blank
12008 if(this.allowBlank){
12014 if(value.length < this.minLength){
12017 if(value.length > this.maxLength){
12021 var vt = Roo.form.VTypes;
12022 if(!vt[this.vtype](value, this)){
12026 if(typeof this.validator == "function"){
12027 var msg = this.validator(value);
12031 if (typeof(msg) == 'string') {
12032 this.invalidText = msg;
12036 if(this.regex && !this.regex.test(value)){
12044 fireKey : function(e){
12045 //Roo.log('field ' + e.getKey());
12046 if(e.isNavKeyPress()){
12047 this.fireEvent("specialkey", this, e);
12050 focus : function (selectText){
12052 this.inputEl().focus();
12053 if(selectText === true){
12054 this.inputEl().dom.select();
12060 onFocus : function(){
12061 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12062 // this.el.addClass(this.focusClass);
12064 if(!this.hasFocus){
12065 this.hasFocus = true;
12066 this.startValue = this.getValue();
12067 this.fireEvent("focus", this);
12071 beforeBlur : Roo.emptyFn,
12075 onBlur : function(){
12077 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12078 //this.el.removeClass(this.focusClass);
12080 this.hasFocus = false;
12081 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12084 var v = this.getValue();
12085 if(String(v) !== String(this.startValue)){
12086 this.fireEvent('change', this, v, this.startValue);
12088 this.fireEvent("blur", this);
12091 onChange : function(e)
12093 var v = this.getValue();
12094 if(String(v) !== String(this.startValue)){
12095 this.fireEvent('change', this, v, this.startValue);
12101 * Resets the current field value to the originally loaded value and clears any validation messages
12103 reset : function(){
12104 this.setValue(this.originalValue);
12108 * Returns the name of the field
12109 * @return {Mixed} name The name field
12111 getName: function(){
12115 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
12116 * @return {Mixed} value The field value
12118 getValue : function(){
12120 var v = this.inputEl().getValue();
12125 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
12126 * @return {Mixed} value The field value
12128 getRawValue : function(){
12129 var v = this.inputEl().getValue();
12135 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
12136 * @param {Mixed} value The value to set
12138 setRawValue : function(v){
12139 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12142 selectText : function(start, end){
12143 var v = this.getRawValue();
12145 start = start === undefined ? 0 : start;
12146 end = end === undefined ? v.length : end;
12147 var d = this.inputEl().dom;
12148 if(d.setSelectionRange){
12149 d.setSelectionRange(start, end);
12150 }else if(d.createTextRange){
12151 var range = d.createTextRange();
12152 range.moveStart("character", start);
12153 range.moveEnd("character", v.length-end);
12160 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
12161 * @param {Mixed} value The value to set
12163 setValue : function(v){
12166 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12172 processValue : function(value){
12173 if(this.stripCharsRe){
12174 var newValue = value.replace(this.stripCharsRe, '');
12175 if(newValue !== value){
12176 this.setRawValue(newValue);
12183 preFocus : function(){
12185 if(this.selectOnFocus){
12186 this.inputEl().dom.select();
12189 filterKeys : function(e){
12190 var k = e.getKey();
12191 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
12194 var c = e.getCharCode(), cc = String.fromCharCode(c);
12195 if(Roo.isIE && (e.isSpecialKey() || !cc)){
12198 if(!this.maskRe.test(cc)){
12203 * Clear any invalid styles/messages for this field
12205 clearInvalid : function(){
12207 if(!this.el || this.preventMark){ // not rendered
12212 this.el.removeClass([this.invalidClass, 'is-invalid']);
12214 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12216 var feedback = this.el.select('.form-control-feedback', true).first();
12219 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12224 if(this.indicator){
12225 this.indicator.removeClass('visible');
12226 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12229 this.fireEvent('valid', this);
12233 * Mark this field as valid
12235 markValid : function()
12237 if(!this.el || this.preventMark){ // not rendered...
12241 this.el.removeClass([this.invalidClass, this.validClass]);
12242 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12244 var feedback = this.el.select('.form-control-feedback', true).first();
12247 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12250 if(this.indicator){
12251 this.indicator.removeClass('visible');
12252 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12260 if(this.allowBlank && !this.getRawValue().length){
12263 if (Roo.bootstrap.version == 3) {
12264 this.el.addClass(this.validClass);
12266 this.inputEl().addClass('is-valid');
12269 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12271 var feedback = this.el.select('.form-control-feedback', true).first();
12274 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12275 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12280 this.fireEvent('valid', this);
12284 * Mark this field as invalid
12285 * @param {String} msg The validation message
12287 markInvalid : function(msg)
12289 if(!this.el || this.preventMark){ // not rendered
12293 this.el.removeClass([this.invalidClass, this.validClass]);
12294 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12296 var feedback = this.el.select('.form-control-feedback', true).first();
12299 this.el.select('.form-control-feedback', true).first().removeClass(
12300 [this.invalidFeedbackClass, this.validFeedbackClass]);
12307 if(this.allowBlank && !this.getRawValue().length){
12311 if(this.indicator){
12312 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12313 this.indicator.addClass('visible');
12315 if (Roo.bootstrap.version == 3) {
12316 this.el.addClass(this.invalidClass);
12318 this.inputEl().addClass('is-invalid');
12323 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12325 var feedback = this.el.select('.form-control-feedback', true).first();
12328 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12330 if(this.getValue().length || this.forceFeedback){
12331 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12338 this.fireEvent('invalid', this, msg);
12341 SafariOnKeyDown : function(event)
12343 // this is a workaround for a password hang bug on chrome/ webkit.
12344 if (this.inputEl().dom.type != 'password') {
12348 var isSelectAll = false;
12350 if(this.inputEl().dom.selectionEnd > 0){
12351 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
12353 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
12354 event.preventDefault();
12359 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
12361 event.preventDefault();
12362 // this is very hacky as keydown always get's upper case.
12364 var cc = String.fromCharCode(event.getCharCode());
12365 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
12369 adjustWidth : function(tag, w){
12370 tag = tag.toLowerCase();
12371 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
12372 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
12373 if(tag == 'input'){
12376 if(tag == 'textarea'){
12379 }else if(Roo.isOpera){
12380 if(tag == 'input'){
12383 if(tag == 'textarea'){
12391 setFieldLabel : function(v)
12393 if(!this.rendered){
12397 if(this.indicatorEl()){
12398 var ar = this.el.select('label > span',true);
12400 if (ar.elements.length) {
12401 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12402 this.fieldLabel = v;
12406 var br = this.el.select('label',true);
12408 if(br.elements.length) {
12409 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12410 this.fieldLabel = v;
12414 Roo.log('Cannot Found any of label > span || label in input');
12418 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12419 this.fieldLabel = v;
12434 * @class Roo.bootstrap.TextArea
12435 * @extends Roo.bootstrap.Input
12436 * Bootstrap TextArea class
12437 * @cfg {Number} cols Specifies the visible width of a text area
12438 * @cfg {Number} rows Specifies the visible number of lines in a text area
12439 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
12440 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
12441 * @cfg {string} html text
12444 * Create a new TextArea
12445 * @param {Object} config The config object
12448 Roo.bootstrap.TextArea = function(config){
12449 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
12453 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
12463 getAutoCreate : function(){
12465 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12471 if(this.inputType != 'hidden'){
12472 cfg.cls = 'form-group' //input-group
12480 value : this.value || '',
12481 html: this.html || '',
12482 cls : 'form-control',
12483 placeholder : this.placeholder || ''
12487 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12488 input.maxLength = this.maxLength;
12492 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
12496 input.cols = this.cols;
12499 if (this.readOnly) {
12500 input.readonly = true;
12504 input.name = this.name;
12508 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
12512 ['xs','sm','md','lg'].map(function(size){
12513 if (settings[size]) {
12514 cfg.cls += ' col-' + size + '-' + settings[size];
12518 var inputblock = input;
12520 if(this.hasFeedback && !this.allowBlank){
12524 cls: 'glyphicon form-control-feedback'
12528 cls : 'has-feedback',
12537 if (this.before || this.after) {
12540 cls : 'input-group',
12544 inputblock.cn.push({
12546 cls : 'input-group-addon',
12551 inputblock.cn.push(input);
12553 if(this.hasFeedback && !this.allowBlank){
12554 inputblock.cls += ' has-feedback';
12555 inputblock.cn.push(feedback);
12559 inputblock.cn.push({
12561 cls : 'input-group-addon',
12568 if (align ==='left' && this.fieldLabel.length) {
12573 cls : 'control-label',
12574 html : this.fieldLabel
12585 if(this.labelWidth > 12){
12586 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
12589 if(this.labelWidth < 13 && this.labelmd == 0){
12590 this.labelmd = this.labelWidth;
12593 if(this.labellg > 0){
12594 cfg.cn[0].cls += ' col-lg-' + this.labellg;
12595 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
12598 if(this.labelmd > 0){
12599 cfg.cn[0].cls += ' col-md-' + this.labelmd;
12600 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
12603 if(this.labelsm > 0){
12604 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
12605 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
12608 if(this.labelxs > 0){
12609 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
12610 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
12613 } else if ( this.fieldLabel.length) {
12618 //cls : 'input-group-addon',
12619 html : this.fieldLabel
12637 if (this.disabled) {
12638 input.disabled=true;
12645 * return the real textarea element.
12647 inputEl: function ()
12649 return this.el.select('textarea.form-control',true).first();
12653 * Clear any invalid styles/messages for this field
12655 clearInvalid : function()
12658 if(!this.el || this.preventMark){ // not rendered
12662 var label = this.el.select('label', true).first();
12663 var icon = this.el.select('i.fa-star', true).first();
12668 this.el.removeClass( this.validClass);
12669 this.inputEl().removeClass('is-invalid');
12671 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12673 var feedback = this.el.select('.form-control-feedback', true).first();
12676 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12681 this.fireEvent('valid', this);
12685 * Mark this field as valid
12687 markValid : function()
12689 if(!this.el || this.preventMark){ // not rendered
12693 this.el.removeClass([this.invalidClass, this.validClass]);
12694 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12696 var feedback = this.el.select('.form-control-feedback', true).first();
12699 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12702 if(this.disabled || this.allowBlank){
12706 var label = this.el.select('label', true).first();
12707 var icon = this.el.select('i.fa-star', true).first();
12712 if (Roo.bootstrap.version == 3) {
12713 this.el.addClass(this.validClass);
12715 this.inputEl().addClass('is-valid');
12719 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12721 var feedback = this.el.select('.form-control-feedback', true).first();
12724 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12725 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12730 this.fireEvent('valid', this);
12734 * Mark this field as invalid
12735 * @param {String} msg The validation message
12737 markInvalid : function(msg)
12739 if(!this.el || this.preventMark){ // not rendered
12743 this.el.removeClass([this.invalidClass, this.validClass]);
12744 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12746 var feedback = this.el.select('.form-control-feedback', true).first();
12749 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12752 if(this.disabled || this.allowBlank){
12756 var label = this.el.select('label', true).first();
12757 var icon = this.el.select('i.fa-star', true).first();
12759 if(!this.getValue().length && label && !icon){
12760 this.el.createChild({
12762 cls : 'text-danger fa fa-lg fa-star',
12763 tooltip : 'This field is required',
12764 style : 'margin-right:5px;'
12768 if (Roo.bootstrap.version == 3) {
12769 this.el.addClass(this.invalidClass);
12771 this.inputEl().addClass('is-invalid');
12774 // fixme ... this may be depricated need to test..
12775 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12777 var feedback = this.el.select('.form-control-feedback', true).first();
12780 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12782 if(this.getValue().length || this.forceFeedback){
12783 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12790 this.fireEvent('invalid', this, msg);
12798 * trigger field - base class for combo..
12803 * @class Roo.bootstrap.TriggerField
12804 * @extends Roo.bootstrap.Input
12805 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
12806 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
12807 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
12808 * for which you can provide a custom implementation. For example:
12810 var trigger = new Roo.bootstrap.TriggerField();
12811 trigger.onTriggerClick = myTriggerFn;
12812 trigger.applyTo('my-field');
12815 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
12816 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
12817 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
12818 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
12819 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
12822 * Create a new TriggerField.
12823 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
12824 * to the base TextField)
12826 Roo.bootstrap.TriggerField = function(config){
12827 this.mimicing = false;
12828 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
12831 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
12833 * @cfg {String} triggerClass A CSS class to apply to the trigger
12836 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
12841 * @cfg {Boolean} removable (true|false) special filter default false
12845 /** @cfg {Boolean} grow @hide */
12846 /** @cfg {Number} growMin @hide */
12847 /** @cfg {Number} growMax @hide */
12853 autoSize: Roo.emptyFn,
12857 deferHeight : true,
12860 actionMode : 'wrap',
12865 getAutoCreate : function(){
12867 var align = this.labelAlign || this.parentLabelAlign();
12872 cls: 'form-group' //input-group
12879 type : this.inputType,
12880 cls : 'form-control',
12881 autocomplete: 'new-password',
12882 placeholder : this.placeholder || ''
12886 input.name = this.name;
12889 input.cls += ' input-' + this.size;
12892 if (this.disabled) {
12893 input.disabled=true;
12896 var inputblock = input;
12898 if(this.hasFeedback && !this.allowBlank){
12902 cls: 'glyphicon form-control-feedback'
12905 if(this.removable && !this.editable ){
12907 cls : 'has-feedback',
12913 cls : 'roo-combo-removable-btn close'
12920 cls : 'has-feedback',
12929 if(this.removable && !this.editable ){
12931 cls : 'roo-removable',
12937 cls : 'roo-combo-removable-btn close'
12944 if (this.before || this.after) {
12947 cls : 'input-group',
12951 inputblock.cn.push({
12953 cls : 'input-group-addon input-group-prepend input-group-text',
12958 inputblock.cn.push(input);
12960 if(this.hasFeedback && !this.allowBlank){
12961 inputblock.cls += ' has-feedback';
12962 inputblock.cn.push(feedback);
12966 inputblock.cn.push({
12968 cls : 'input-group-addon input-group-append input-group-text',
12977 var ibwrap = inputblock;
12982 cls: 'roo-select2-choices',
12986 cls: 'roo-select2-search-field',
12998 cls: 'roo-select2-container input-group',
13003 cls: 'form-hidden-field'
13009 if(!this.multiple && this.showToggleBtn){
13015 if (this.caret != false) {
13018 cls: 'fa fa-' + this.caret
13025 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13027 Roo.bootstrap.version == 3 ? caret : '',
13030 cls: 'combobox-clear',
13044 combobox.cls += ' roo-select2-container-multi';
13048 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13049 tooltip : 'This field is required'
13051 if (Roo.bootstrap.version == 4) {
13054 style : 'display:none'
13059 if (align ==='left' && this.fieldLabel.length) {
13061 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
13068 cls : 'control-label',
13069 html : this.fieldLabel
13081 var labelCfg = cfg.cn[1];
13082 var contentCfg = cfg.cn[2];
13084 if(this.indicatorpos == 'right'){
13089 cls : 'control-label',
13093 html : this.fieldLabel
13107 labelCfg = cfg.cn[0];
13108 contentCfg = cfg.cn[1];
13111 if(this.labelWidth > 12){
13112 labelCfg.style = "width: " + this.labelWidth + 'px';
13115 if(this.labelWidth < 13 && this.labelmd == 0){
13116 this.labelmd = this.labelWidth;
13119 if(this.labellg > 0){
13120 labelCfg.cls += ' col-lg-' + this.labellg;
13121 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13124 if(this.labelmd > 0){
13125 labelCfg.cls += ' col-md-' + this.labelmd;
13126 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13129 if(this.labelsm > 0){
13130 labelCfg.cls += ' col-sm-' + this.labelsm;
13131 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13134 if(this.labelxs > 0){
13135 labelCfg.cls += ' col-xs-' + this.labelxs;
13136 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13139 } else if ( this.fieldLabel.length) {
13140 // Roo.log(" label");
13145 //cls : 'input-group-addon',
13146 html : this.fieldLabel
13154 if(this.indicatorpos == 'right'){
13162 html : this.fieldLabel
13176 // Roo.log(" no label && no align");
13183 ['xs','sm','md','lg'].map(function(size){
13184 if (settings[size]) {
13185 cfg.cls += ' col-' + size + '-' + settings[size];
13196 onResize : function(w, h){
13197 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
13198 // if(typeof w == 'number'){
13199 // var x = w - this.trigger.getWidth();
13200 // this.inputEl().setWidth(this.adjustWidth('input', x));
13201 // this.trigger.setStyle('left', x+'px');
13206 adjustSize : Roo.BoxComponent.prototype.adjustSize,
13209 getResizeEl : function(){
13210 return this.inputEl();
13214 getPositionEl : function(){
13215 return this.inputEl();
13219 alignErrorIcon : function(){
13220 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
13224 initEvents : function(){
13228 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
13229 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
13230 if(!this.multiple && this.showToggleBtn){
13231 this.trigger = this.el.select('span.dropdown-toggle',true).first();
13232 if(this.hideTrigger){
13233 this.trigger.setDisplayed(false);
13235 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
13239 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
13242 if(this.removable && !this.editable && !this.tickable){
13243 var close = this.closeTriggerEl();
13246 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13247 close.on('click', this.removeBtnClick, this, close);
13251 //this.trigger.addClassOnOver('x-form-trigger-over');
13252 //this.trigger.addClassOnClick('x-form-trigger-click');
13255 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
13259 closeTriggerEl : function()
13261 var close = this.el.select('.roo-combo-removable-btn', true).first();
13262 return close ? close : false;
13265 removeBtnClick : function(e, h, el)
13267 e.preventDefault();
13269 if(this.fireEvent("remove", this) !== false){
13271 this.fireEvent("afterremove", this)
13275 createList : function()
13277 this.list = Roo.get(document.body).createChild({
13278 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
13279 cls: 'typeahead typeahead-long dropdown-menu shadow',
13280 style: 'display:none'
13283 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
13288 initTrigger : function(){
13293 onDestroy : function(){
13295 this.trigger.removeAllListeners();
13296 // this.trigger.remove();
13299 // this.wrap.remove();
13301 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
13305 onFocus : function(){
13306 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
13308 if(!this.mimicing){
13309 this.wrap.addClass('x-trigger-wrap-focus');
13310 this.mimicing = true;
13311 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
13312 if(this.monitorTab){
13313 this.el.on("keydown", this.checkTab, this);
13320 checkTab : function(e){
13321 if(e.getKey() == e.TAB){
13322 this.triggerBlur();
13327 onBlur : function(){
13332 mimicBlur : function(e, t){
13334 if(!this.wrap.contains(t) && this.validateBlur()){
13335 this.triggerBlur();
13341 triggerBlur : function(){
13342 this.mimicing = false;
13343 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
13344 if(this.monitorTab){
13345 this.el.un("keydown", this.checkTab, this);
13347 //this.wrap.removeClass('x-trigger-wrap-focus');
13348 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
13352 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
13353 validateBlur : function(e, t){
13358 onDisable : function(){
13359 this.inputEl().dom.disabled = true;
13360 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
13362 // this.wrap.addClass('x-item-disabled');
13367 onEnable : function(){
13368 this.inputEl().dom.disabled = false;
13369 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
13371 // this.el.removeClass('x-item-disabled');
13376 onShow : function(){
13377 var ae = this.getActionEl();
13380 ae.dom.style.display = '';
13381 ae.dom.style.visibility = 'visible';
13387 onHide : function(){
13388 var ae = this.getActionEl();
13389 ae.dom.style.display = 'none';
13393 * The function that should handle the trigger's click event. This method does nothing by default until overridden
13394 * by an implementing function.
13396 * @param {EventObject} e
13398 onTriggerClick : Roo.emptyFn
13406 * @class Roo.bootstrap.CardUploader
13407 * @extends Roo.bootstrap.Button
13408 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
13409 * @cfg {Number} errorTimeout default 3000
13410 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
13411 * @cfg {Array} html The button text.
13415 * Create a new CardUploader
13416 * @param {Object} config The config object
13419 Roo.bootstrap.CardUploader = function(config){
13423 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
13426 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
13434 * When a image is clicked on - and needs to display a slideshow or similar..
13435 * @param {Roo.bootstrap.Card} this
13436 * @param {Object} The image information data
13442 * When a the download link is clicked
13443 * @param {Roo.bootstrap.Card} this
13444 * @param {Object} The image information data contains
13451 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
13454 errorTimeout : 3000,
13458 fileCollection : false,
13461 getAutoCreate : function()
13465 cls :'form-group' ,
13470 //cls : 'input-group-addon',
13471 html : this.fieldLabel
13479 value : this.value,
13480 cls : 'd-none form-control'
13485 multiple : 'multiple',
13487 cls : 'd-none roo-card-upload-selector'
13491 cls : 'roo-card-uploader-button-container w-100 mb-2'
13494 cls : 'card-columns roo-card-uploader-container'
13504 getChildContainer : function() /// what children are added to.
13506 return this.containerEl;
13509 getButtonContainer : function() /// what children are added to.
13511 return this.el.select(".roo-card-uploader-button-container").first();
13514 initEvents : function()
13517 Roo.bootstrap.Input.prototype.initEvents.call(this);
13521 xns: Roo.bootstrap,
13524 container_method : 'getButtonContainer' ,
13525 html : this.html, // fix changable?
13528 'click' : function(btn, e) {
13537 this.urlAPI = (window.createObjectURL && window) ||
13538 (window.URL && URL.revokeObjectURL && URL) ||
13539 (window.webkitURL && webkitURL);
13544 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
13546 this.selectorEl.on('change', this.onFileSelected, this);
13549 this.images.forEach(function(img) {
13552 this.images = false;
13554 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
13560 onClick : function(e)
13562 e.preventDefault();
13564 this.selectorEl.dom.click();
13568 onFileSelected : function(e)
13570 e.preventDefault();
13572 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
13576 Roo.each(this.selectorEl.dom.files, function(file){
13577 this.addFile(file);
13586 addFile : function(file)
13589 if(typeof(file) === 'string'){
13590 throw "Add file by name?"; // should not happen
13594 if(!file || !this.urlAPI){
13604 var url = _this.urlAPI.createObjectURL( file);
13607 id : Roo.bootstrap.CardUploader.ID--,
13608 is_uploaded : false,
13612 mimetype : file.type,
13620 * addCard - add an Attachment to the uploader
13621 * @param data - the data about the image to upload
13625 title : "Title of file",
13626 is_uploaded : false,
13627 src : "http://.....",
13628 srcfile : { the File upload object },
13629 mimetype : file.type,
13632 .. any other data...
13638 addCard : function (data)
13640 // hidden input element?
13641 // if the file is not an image...
13642 //then we need to use something other that and header_image
13647 xns : Roo.bootstrap,
13648 xtype : 'CardFooter',
13651 xns : Roo.bootstrap,
13657 xns : Roo.bootstrap,
13659 html : String.format("<small>{0}</small>", data.title),
13660 cls : 'col-10 text-left',
13665 click : function() {
13667 t.fireEvent( "download", t, data );
13673 xns : Roo.bootstrap,
13675 style: 'max-height: 28px; ',
13681 click : function() {
13682 t.removeCard(data.id)
13694 var cn = this.addxtype(
13697 xns : Roo.bootstrap,
13700 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
13701 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
13702 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
13707 initEvents : function() {
13708 Roo.bootstrap.Card.prototype.initEvents.call(this);
13710 this.imgEl = this.el.select('.card-img-top').first();
13712 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
13713 this.imgEl.set({ 'pointer' : 'cursor' });
13716 this.getCardFooter().addClass('p-1');
13723 // dont' really need ot update items.
13724 // this.items.push(cn);
13725 this.fileCollection.add(cn);
13727 if (!data.srcfile) {
13728 this.updateInput();
13733 var reader = new FileReader();
13734 reader.addEventListener("load", function() {
13735 data.srcdata = reader.result;
13738 reader.readAsDataURL(data.srcfile);
13743 removeCard : function(id)
13746 var card = this.fileCollection.get(id);
13747 card.data.is_deleted = 1;
13748 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
13749 //this.fileCollection.remove(card);
13750 //this.items = this.items.filter(function(e) { return e != card });
13751 // dont' really need ot update items.
13752 card.el.dom.parentNode.removeChild(card.el.dom);
13753 this.updateInput();
13759 this.fileCollection.each(function(card) {
13760 if (card.el.dom && card.el.dom.parentNode) {
13761 card.el.dom.parentNode.removeChild(card.el.dom);
13764 this.fileCollection.clear();
13765 this.updateInput();
13768 updateInput : function()
13771 this.fileCollection.each(function(e) {
13775 this.inputEl().dom.value = JSON.stringify(data);
13785 Roo.bootstrap.CardUploader.ID = -1;/*
13787 * Ext JS Library 1.1.1
13788 * Copyright(c) 2006-2007, Ext JS, LLC.
13790 * Originally Released Under LGPL - original licence link has changed is not relivant.
13793 * <script type="text/javascript">
13798 * @class Roo.data.SortTypes
13800 * Defines the default sorting (casting?) comparison functions used when sorting data.
13802 Roo.data.SortTypes = {
13804 * Default sort that does nothing
13805 * @param {Mixed} s The value being converted
13806 * @return {Mixed} The comparison value
13808 none : function(s){
13813 * The regular expression used to strip tags
13817 stripTagsRE : /<\/?[^>]+>/gi,
13820 * Strips all HTML tags to sort on text only
13821 * @param {Mixed} s The value being converted
13822 * @return {String} The comparison value
13824 asText : function(s){
13825 return String(s).replace(this.stripTagsRE, "");
13829 * Strips all HTML tags to sort on text only - Case insensitive
13830 * @param {Mixed} s The value being converted
13831 * @return {String} The comparison value
13833 asUCText : function(s){
13834 return String(s).toUpperCase().replace(this.stripTagsRE, "");
13838 * Case insensitive string
13839 * @param {Mixed} s The value being converted
13840 * @return {String} The comparison value
13842 asUCString : function(s) {
13843 return String(s).toUpperCase();
13848 * @param {Mixed} s The value being converted
13849 * @return {Number} The comparison value
13851 asDate : function(s) {
13855 if(s instanceof Date){
13856 return s.getTime();
13858 return Date.parse(String(s));
13863 * @param {Mixed} s The value being converted
13864 * @return {Float} The comparison value
13866 asFloat : function(s) {
13867 var val = parseFloat(String(s).replace(/,/g, ""));
13876 * @param {Mixed} s The value being converted
13877 * @return {Number} The comparison value
13879 asInt : function(s) {
13880 var val = parseInt(String(s).replace(/,/g, ""));
13888 * Ext JS Library 1.1.1
13889 * Copyright(c) 2006-2007, Ext JS, LLC.
13891 * Originally Released Under LGPL - original licence link has changed is not relivant.
13894 * <script type="text/javascript">
13898 * @class Roo.data.Record
13899 * Instances of this class encapsulate both record <em>definition</em> information, and record
13900 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13901 * to access Records cached in an {@link Roo.data.Store} object.<br>
13903 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13904 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13907 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13909 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13910 * {@link #create}. The parameters are the same.
13911 * @param {Array} data An associative Array of data values keyed by the field name.
13912 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13913 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13914 * not specified an integer id is generated.
13916 Roo.data.Record = function(data, id){
13917 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13922 * Generate a constructor for a specific record layout.
13923 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13924 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13925 * Each field definition object may contain the following properties: <ul>
13926 * <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,
13927 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13928 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13929 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13930 * is being used, then this is a string containing the javascript expression to reference the data relative to
13931 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13932 * to the data item relative to the record element. If the mapping expression is the same as the field name,
13933 * this may be omitted.</p></li>
13934 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13935 * <ul><li>auto (Default, implies no conversion)</li>
13940 * <li>date</li></ul></p></li>
13941 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13942 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13943 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13944 * by the Reader into an object that will be stored in the Record. It is passed the
13945 * following parameters:<ul>
13946 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13948 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13950 * <br>usage:<br><pre><code>
13951 var TopicRecord = Roo.data.Record.create(
13952 {name: 'title', mapping: 'topic_title'},
13953 {name: 'author', mapping: 'username'},
13954 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13955 {name: 'lastPost', mapping: 'post_time', type: 'date'},
13956 {name: 'lastPoster', mapping: 'user2'},
13957 {name: 'excerpt', mapping: 'post_text'}
13960 var myNewRecord = new TopicRecord({
13961 title: 'Do my job please',
13964 lastPost: new Date(),
13965 lastPoster: 'Animal',
13966 excerpt: 'No way dude!'
13968 myStore.add(myNewRecord);
13973 Roo.data.Record.create = function(o){
13974 var f = function(){
13975 f.superclass.constructor.apply(this, arguments);
13977 Roo.extend(f, Roo.data.Record);
13978 var p = f.prototype;
13979 p.fields = new Roo.util.MixedCollection(false, function(field){
13982 for(var i = 0, len = o.length; i < len; i++){
13983 p.fields.add(new Roo.data.Field(o[i]));
13985 f.getField = function(name){
13986 return p.fields.get(name);
13991 Roo.data.Record.AUTO_ID = 1000;
13992 Roo.data.Record.EDIT = 'edit';
13993 Roo.data.Record.REJECT = 'reject';
13994 Roo.data.Record.COMMIT = 'commit';
13996 Roo.data.Record.prototype = {
13998 * Readonly flag - true if this record has been modified.
14007 join : function(store){
14008 this.store = store;
14012 * Set the named field to the specified value.
14013 * @param {String} name The name of the field to set.
14014 * @param {Object} value The value to set the field to.
14016 set : function(name, value){
14017 if(this.data[name] == value){
14021 if(!this.modified){
14022 this.modified = {};
14024 if(typeof this.modified[name] == 'undefined'){
14025 this.modified[name] = this.data[name];
14027 this.data[name] = value;
14028 if(!this.editing && this.store){
14029 this.store.afterEdit(this);
14034 * Get the value of the named field.
14035 * @param {String} name The name of the field to get the value of.
14036 * @return {Object} The value of the field.
14038 get : function(name){
14039 return this.data[name];
14043 beginEdit : function(){
14044 this.editing = true;
14045 this.modified = {};
14049 cancelEdit : function(){
14050 this.editing = false;
14051 delete this.modified;
14055 endEdit : function(){
14056 this.editing = false;
14057 if(this.dirty && this.store){
14058 this.store.afterEdit(this);
14063 * Usually called by the {@link Roo.data.Store} which owns the Record.
14064 * Rejects all changes made to the Record since either creation, or the last commit operation.
14065 * Modified fields are reverted to their original values.
14067 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14068 * of reject operations.
14070 reject : function(){
14071 var m = this.modified;
14073 if(typeof m[n] != "function"){
14074 this.data[n] = m[n];
14077 this.dirty = false;
14078 delete this.modified;
14079 this.editing = false;
14081 this.store.afterReject(this);
14086 * Usually called by the {@link Roo.data.Store} which owns the Record.
14087 * Commits all changes made to the Record since either creation, or the last commit operation.
14089 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14090 * of commit operations.
14092 commit : function(){
14093 this.dirty = false;
14094 delete this.modified;
14095 this.editing = false;
14097 this.store.afterCommit(this);
14102 hasError : function(){
14103 return this.error != null;
14107 clearError : function(){
14112 * Creates a copy of this record.
14113 * @param {String} id (optional) A new record id if you don't want to use this record's id
14116 copy : function(newId) {
14117 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
14121 * Ext JS Library 1.1.1
14122 * Copyright(c) 2006-2007, Ext JS, LLC.
14124 * Originally Released Under LGPL - original licence link has changed is not relivant.
14127 * <script type="text/javascript">
14133 * @class Roo.data.Store
14134 * @extends Roo.util.Observable
14135 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
14136 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
14138 * 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
14139 * has no knowledge of the format of the data returned by the Proxy.<br>
14141 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
14142 * instances from the data object. These records are cached and made available through accessor functions.
14144 * Creates a new Store.
14145 * @param {Object} config A config object containing the objects needed for the Store to access data,
14146 * and read the data into Records.
14148 Roo.data.Store = function(config){
14149 this.data = new Roo.util.MixedCollection(false);
14150 this.data.getKey = function(o){
14153 this.baseParams = {};
14155 this.paramNames = {
14160 "multisort" : "_multisort"
14163 if(config && config.data){
14164 this.inlineData = config.data;
14165 delete config.data;
14168 Roo.apply(this, config);
14170 if(this.reader){ // reader passed
14171 this.reader = Roo.factory(this.reader, Roo.data);
14172 this.reader.xmodule = this.xmodule || false;
14173 if(!this.recordType){
14174 this.recordType = this.reader.recordType;
14176 if(this.reader.onMetaChange){
14177 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
14181 if(this.recordType){
14182 this.fields = this.recordType.prototype.fields;
14184 this.modified = [];
14188 * @event datachanged
14189 * Fires when the data cache has changed, and a widget which is using this Store
14190 * as a Record cache should refresh its view.
14191 * @param {Store} this
14193 datachanged : true,
14195 * @event metachange
14196 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
14197 * @param {Store} this
14198 * @param {Object} meta The JSON metadata
14203 * Fires when Records have been added to the Store
14204 * @param {Store} this
14205 * @param {Roo.data.Record[]} records The array of Records added
14206 * @param {Number} index The index at which the record(s) were added
14211 * Fires when a Record has been removed from the Store
14212 * @param {Store} this
14213 * @param {Roo.data.Record} record The Record that was removed
14214 * @param {Number} index The index at which the record was removed
14219 * Fires when a Record has been updated
14220 * @param {Store} this
14221 * @param {Roo.data.Record} record The Record that was updated
14222 * @param {String} operation The update operation being performed. Value may be one of:
14224 Roo.data.Record.EDIT
14225 Roo.data.Record.REJECT
14226 Roo.data.Record.COMMIT
14232 * Fires when the data cache has been cleared.
14233 * @param {Store} this
14237 * @event beforeload
14238 * Fires before a request is made for a new data object. If the beforeload handler returns false
14239 * the load action will be canceled.
14240 * @param {Store} this
14241 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14245 * @event beforeloadadd
14246 * Fires after a new set of Records has been loaded.
14247 * @param {Store} this
14248 * @param {Roo.data.Record[]} records The Records that were loaded
14249 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14251 beforeloadadd : true,
14254 * Fires after a new set of Records has been loaded, before they are added to the store.
14255 * @param {Store} this
14256 * @param {Roo.data.Record[]} records The Records that were loaded
14257 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14258 * @params {Object} return from reader
14262 * @event loadexception
14263 * Fires if an exception occurs in the Proxy during loading.
14264 * Called with the signature of the Proxy's "loadexception" event.
14265 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
14268 * @param {Object} return from JsonData.reader() - success, totalRecords, records
14269 * @param {Object} load options
14270 * @param {Object} jsonData from your request (normally this contains the Exception)
14272 loadexception : true
14276 this.proxy = Roo.factory(this.proxy, Roo.data);
14277 this.proxy.xmodule = this.xmodule || false;
14278 this.relayEvents(this.proxy, ["loadexception"]);
14280 this.sortToggle = {};
14281 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
14283 Roo.data.Store.superclass.constructor.call(this);
14285 if(this.inlineData){
14286 this.loadData(this.inlineData);
14287 delete this.inlineData;
14291 Roo.extend(Roo.data.Store, Roo.util.Observable, {
14293 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
14294 * without a remote query - used by combo/forms at present.
14298 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
14301 * @cfg {Array} data Inline data to be loaded when the store is initialized.
14304 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
14305 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
14308 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
14309 * on any HTTP request
14312 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
14315 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
14319 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
14320 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
14322 remoteSort : false,
14325 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
14326 * loaded or when a record is removed. (defaults to false).
14328 pruneModifiedRecords : false,
14331 lastOptions : null,
14334 * Add Records to the Store and fires the add event.
14335 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14337 add : function(records){
14338 records = [].concat(records);
14339 for(var i = 0, len = records.length; i < len; i++){
14340 records[i].join(this);
14342 var index = this.data.length;
14343 this.data.addAll(records);
14344 this.fireEvent("add", this, records, index);
14348 * Remove a Record from the Store and fires the remove event.
14349 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
14351 remove : function(record){
14352 var index = this.data.indexOf(record);
14353 this.data.removeAt(index);
14355 if(this.pruneModifiedRecords){
14356 this.modified.remove(record);
14358 this.fireEvent("remove", this, record, index);
14362 * Remove all Records from the Store and fires the clear event.
14364 removeAll : function(){
14366 if(this.pruneModifiedRecords){
14367 this.modified = [];
14369 this.fireEvent("clear", this);
14373 * Inserts Records to the Store at the given index and fires the add event.
14374 * @param {Number} index The start index at which to insert the passed Records.
14375 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14377 insert : function(index, records){
14378 records = [].concat(records);
14379 for(var i = 0, len = records.length; i < len; i++){
14380 this.data.insert(index, records[i]);
14381 records[i].join(this);
14383 this.fireEvent("add", this, records, index);
14387 * Get the index within the cache of the passed Record.
14388 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
14389 * @return {Number} The index of the passed Record. Returns -1 if not found.
14391 indexOf : function(record){
14392 return this.data.indexOf(record);
14396 * Get the index within the cache of the Record with the passed id.
14397 * @param {String} id The id of the Record to find.
14398 * @return {Number} The index of the Record. Returns -1 if not found.
14400 indexOfId : function(id){
14401 return this.data.indexOfKey(id);
14405 * Get the Record with the specified id.
14406 * @param {String} id The id of the Record to find.
14407 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
14409 getById : function(id){
14410 return this.data.key(id);
14414 * Get the Record at the specified index.
14415 * @param {Number} index The index of the Record to find.
14416 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
14418 getAt : function(index){
14419 return this.data.itemAt(index);
14423 * Returns a range of Records between specified indices.
14424 * @param {Number} startIndex (optional) The starting index (defaults to 0)
14425 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
14426 * @return {Roo.data.Record[]} An array of Records
14428 getRange : function(start, end){
14429 return this.data.getRange(start, end);
14433 storeOptions : function(o){
14434 o = Roo.apply({}, o);
14437 this.lastOptions = o;
14441 * Loads the Record cache from the configured Proxy using the configured Reader.
14443 * If using remote paging, then the first load call must specify the <em>start</em>
14444 * and <em>limit</em> properties in the options.params property to establish the initial
14445 * position within the dataset, and the number of Records to cache on each read from the Proxy.
14447 * <strong>It is important to note that for remote data sources, loading is asynchronous,
14448 * and this call will return before the new data has been loaded. Perform any post-processing
14449 * in a callback function, or in a "load" event handler.</strong>
14451 * @param {Object} options An object containing properties which control loading options:<ul>
14452 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
14453 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
14454 * passed the following arguments:<ul>
14455 * <li>r : Roo.data.Record[]</li>
14456 * <li>options: Options object from the load call</li>
14457 * <li>success: Boolean success indicator</li></ul></li>
14458 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
14459 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
14462 load : function(options){
14463 options = options || {};
14464 if(this.fireEvent("beforeload", this, options) !== false){
14465 this.storeOptions(options);
14466 var p = Roo.apply(options.params || {}, this.baseParams);
14467 // if meta was not loaded from remote source.. try requesting it.
14468 if (!this.reader.metaFromRemote) {
14469 p._requestMeta = 1;
14471 if(this.sortInfo && this.remoteSort){
14472 var pn = this.paramNames;
14473 p[pn["sort"]] = this.sortInfo.field;
14474 p[pn["dir"]] = this.sortInfo.direction;
14476 if (this.multiSort) {
14477 var pn = this.paramNames;
14478 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
14481 this.proxy.load(p, this.reader, this.loadRecords, this, options);
14486 * Reloads the Record cache from the configured Proxy using the configured Reader and
14487 * the options from the last load operation performed.
14488 * @param {Object} options (optional) An object containing properties which may override the options
14489 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
14490 * the most recently used options are reused).
14492 reload : function(options){
14493 this.load(Roo.applyIf(options||{}, this.lastOptions));
14497 // Called as a callback by the Reader during a load operation.
14498 loadRecords : function(o, options, success){
14499 if(!o || success === false){
14500 if(success !== false){
14501 this.fireEvent("load", this, [], options, o);
14503 if(options.callback){
14504 options.callback.call(options.scope || this, [], options, false);
14508 // if data returned failure - throw an exception.
14509 if (o.success === false) {
14510 // show a message if no listener is registered.
14511 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
14512 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
14514 // loadmask wil be hooked into this..
14515 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
14518 var r = o.records, t = o.totalRecords || r.length;
14520 this.fireEvent("beforeloadadd", this, r, options, o);
14522 if(!options || options.add !== true){
14523 if(this.pruneModifiedRecords){
14524 this.modified = [];
14526 for(var i = 0, len = r.length; i < len; i++){
14530 this.data = this.snapshot;
14531 delete this.snapshot;
14534 this.data.addAll(r);
14535 this.totalLength = t;
14537 this.fireEvent("datachanged", this);
14539 this.totalLength = Math.max(t, this.data.length+r.length);
14543 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
14545 var e = new Roo.data.Record({});
14547 e.set(this.parent.displayField, this.parent.emptyTitle);
14548 e.set(this.parent.valueField, '');
14553 this.fireEvent("load", this, r, options, o);
14554 if(options.callback){
14555 options.callback.call(options.scope || this, r, options, true);
14561 * Loads data from a passed data block. A Reader which understands the format of the data
14562 * must have been configured in the constructor.
14563 * @param {Object} data The data block from which to read the Records. The format of the data expected
14564 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
14565 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
14567 loadData : function(o, append){
14568 var r = this.reader.readRecords(o);
14569 this.loadRecords(r, {add: append}, true);
14573 * using 'cn' the nested child reader read the child array into it's child stores.
14574 * @param {Object} rec The record with a 'children array
14576 loadDataFromChildren : function(rec)
14578 this.loadData(this.reader.toLoadData(rec));
14583 * Gets the number of cached records.
14585 * <em>If using paging, this may not be the total size of the dataset. If the data object
14586 * used by the Reader contains the dataset size, then the getTotalCount() function returns
14587 * the data set size</em>
14589 getCount : function(){
14590 return this.data.length || 0;
14594 * Gets the total number of records in the dataset as returned by the server.
14596 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
14597 * the dataset size</em>
14599 getTotalCount : function(){
14600 return this.totalLength || 0;
14604 * Returns the sort state of the Store as an object with two properties:
14606 field {String} The name of the field by which the Records are sorted
14607 direction {String} The sort order, "ASC" or "DESC"
14610 getSortState : function(){
14611 return this.sortInfo;
14615 applySort : function(){
14616 if(this.sortInfo && !this.remoteSort){
14617 var s = this.sortInfo, f = s.field;
14618 var st = this.fields.get(f).sortType;
14619 var fn = function(r1, r2){
14620 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
14621 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
14623 this.data.sort(s.direction, fn);
14624 if(this.snapshot && this.snapshot != this.data){
14625 this.snapshot.sort(s.direction, fn);
14631 * Sets the default sort column and order to be used by the next load operation.
14632 * @param {String} fieldName The name of the field to sort by.
14633 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14635 setDefaultSort : function(field, dir){
14636 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
14640 * Sort the Records.
14641 * If remote sorting is used, the sort is performed on the server, and the cache is
14642 * reloaded. If local sorting is used, the cache is sorted internally.
14643 * @param {String} fieldName The name of the field to sort by.
14644 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14646 sort : function(fieldName, dir){
14647 var f = this.fields.get(fieldName);
14649 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
14651 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
14652 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
14657 this.sortToggle[f.name] = dir;
14658 this.sortInfo = {field: f.name, direction: dir};
14659 if(!this.remoteSort){
14661 this.fireEvent("datachanged", this);
14663 this.load(this.lastOptions);
14668 * Calls the specified function for each of the Records in the cache.
14669 * @param {Function} fn The function to call. The Record is passed as the first parameter.
14670 * Returning <em>false</em> aborts and exits the iteration.
14671 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
14673 each : function(fn, scope){
14674 this.data.each(fn, scope);
14678 * Gets all records modified since the last commit. Modified records are persisted across load operations
14679 * (e.g., during paging).
14680 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
14682 getModifiedRecords : function(){
14683 return this.modified;
14687 createFilterFn : function(property, value, anyMatch){
14688 if(!value.exec){ // not a regex
14689 value = String(value);
14690 if(value.length == 0){
14693 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
14695 return function(r){
14696 return value.test(r.data[property]);
14701 * Sums the value of <i>property</i> for each record between start and end and returns the result.
14702 * @param {String} property A field on your records
14703 * @param {Number} start The record index to start at (defaults to 0)
14704 * @param {Number} end The last record index to include (defaults to length - 1)
14705 * @return {Number} The sum
14707 sum : function(property, start, end){
14708 var rs = this.data.items, v = 0;
14709 start = start || 0;
14710 end = (end || end === 0) ? end : rs.length-1;
14712 for(var i = start; i <= end; i++){
14713 v += (rs[i].data[property] || 0);
14719 * Filter the records by a specified property.
14720 * @param {String} field A field on your records
14721 * @param {String/RegExp} value Either a string that the field
14722 * should start with or a RegExp to test against the field
14723 * @param {Boolean} anyMatch True to match any part not just the beginning
14725 filter : function(property, value, anyMatch){
14726 var fn = this.createFilterFn(property, value, anyMatch);
14727 return fn ? this.filterBy(fn) : this.clearFilter();
14731 * Filter by a function. The specified function will be called with each
14732 * record in this data source. If the function returns true the record is included,
14733 * otherwise it is filtered.
14734 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14735 * @param {Object} scope (optional) The scope of the function (defaults to this)
14737 filterBy : function(fn, scope){
14738 this.snapshot = this.snapshot || this.data;
14739 this.data = this.queryBy(fn, scope||this);
14740 this.fireEvent("datachanged", this);
14744 * Query the records by a specified property.
14745 * @param {String} field A field on your records
14746 * @param {String/RegExp} value Either a string that the field
14747 * should start with or a RegExp to test against the field
14748 * @param {Boolean} anyMatch True to match any part not just the beginning
14749 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14751 query : function(property, value, anyMatch){
14752 var fn = this.createFilterFn(property, value, anyMatch);
14753 return fn ? this.queryBy(fn) : this.data.clone();
14757 * Query by a function. The specified function will be called with each
14758 * record in this data source. If the function returns true the record is included
14760 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14761 * @param {Object} scope (optional) The scope of the function (defaults to this)
14762 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14764 queryBy : function(fn, scope){
14765 var data = this.snapshot || this.data;
14766 return data.filterBy(fn, scope||this);
14770 * Collects unique values for a particular dataIndex from this store.
14771 * @param {String} dataIndex The property to collect
14772 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
14773 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
14774 * @return {Array} An array of the unique values
14776 collect : function(dataIndex, allowNull, bypassFilter){
14777 var d = (bypassFilter === true && this.snapshot) ?
14778 this.snapshot.items : this.data.items;
14779 var v, sv, r = [], l = {};
14780 for(var i = 0, len = d.length; i < len; i++){
14781 v = d[i].data[dataIndex];
14783 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
14792 * Revert to a view of the Record cache with no filtering applied.
14793 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
14795 clearFilter : function(suppressEvent){
14796 if(this.snapshot && this.snapshot != this.data){
14797 this.data = this.snapshot;
14798 delete this.snapshot;
14799 if(suppressEvent !== true){
14800 this.fireEvent("datachanged", this);
14806 afterEdit : function(record){
14807 if(this.modified.indexOf(record) == -1){
14808 this.modified.push(record);
14810 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
14814 afterReject : function(record){
14815 this.modified.remove(record);
14816 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
14820 afterCommit : function(record){
14821 this.modified.remove(record);
14822 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
14826 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
14827 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
14829 commitChanges : function(){
14830 var m = this.modified.slice(0);
14831 this.modified = [];
14832 for(var i = 0, len = m.length; i < len; i++){
14838 * Cancel outstanding changes on all changed records.
14840 rejectChanges : function(){
14841 var m = this.modified.slice(0);
14842 this.modified = [];
14843 for(var i = 0, len = m.length; i < len; i++){
14848 onMetaChange : function(meta, rtype, o){
14849 this.recordType = rtype;
14850 this.fields = rtype.prototype.fields;
14851 delete this.snapshot;
14852 this.sortInfo = meta.sortInfo || this.sortInfo;
14853 this.modified = [];
14854 this.fireEvent('metachange', this, this.reader.meta);
14857 moveIndex : function(data, type)
14859 var index = this.indexOf(data);
14861 var newIndex = index + type;
14865 this.insert(newIndex, data);
14870 * Ext JS Library 1.1.1
14871 * Copyright(c) 2006-2007, Ext JS, LLC.
14873 * Originally Released Under LGPL - original licence link has changed is not relivant.
14876 * <script type="text/javascript">
14880 * @class Roo.data.SimpleStore
14881 * @extends Roo.data.Store
14882 * Small helper class to make creating Stores from Array data easier.
14883 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
14884 * @cfg {Array} fields An array of field definition objects, or field name strings.
14885 * @cfg {Object} an existing reader (eg. copied from another store)
14886 * @cfg {Array} data The multi-dimensional array of data
14888 * @param {Object} config
14890 Roo.data.SimpleStore = function(config)
14892 Roo.data.SimpleStore.superclass.constructor.call(this, {
14894 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
14897 Roo.data.Record.create(config.fields)
14899 proxy : new Roo.data.MemoryProxy(config.data)
14903 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
14905 * Ext JS Library 1.1.1
14906 * Copyright(c) 2006-2007, Ext JS, LLC.
14908 * Originally Released Under LGPL - original licence link has changed is not relivant.
14911 * <script type="text/javascript">
14916 * @extends Roo.data.Store
14917 * @class Roo.data.JsonStore
14918 * Small helper class to make creating Stores for JSON data easier. <br/>
14920 var store = new Roo.data.JsonStore({
14921 url: 'get-images.php',
14923 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14926 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14927 * JsonReader and HttpProxy (unless inline data is provided).</b>
14928 * @cfg {Array} fields An array of field definition objects, or field name strings.
14930 * @param {Object} config
14932 Roo.data.JsonStore = function(c){
14933 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14934 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14935 reader: new Roo.data.JsonReader(c, c.fields)
14938 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14940 * Ext JS Library 1.1.1
14941 * Copyright(c) 2006-2007, Ext JS, LLC.
14943 * Originally Released Under LGPL - original licence link has changed is not relivant.
14946 * <script type="text/javascript">
14950 Roo.data.Field = function(config){
14951 if(typeof config == "string"){
14952 config = {name: config};
14954 Roo.apply(this, config);
14957 this.type = "auto";
14960 var st = Roo.data.SortTypes;
14961 // named sortTypes are supported, here we look them up
14962 if(typeof this.sortType == "string"){
14963 this.sortType = st[this.sortType];
14966 // set default sortType for strings and dates
14967 if(!this.sortType){
14970 this.sortType = st.asUCString;
14973 this.sortType = st.asDate;
14976 this.sortType = st.none;
14981 var stripRe = /[\$,%]/g;
14983 // prebuilt conversion function for this field, instead of
14984 // switching every time we're reading a value
14986 var cv, dateFormat = this.dateFormat;
14991 cv = function(v){ return v; };
14994 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14998 return v !== undefined && v !== null && v !== '' ?
14999 parseInt(String(v).replace(stripRe, ""), 10) : '';
15004 return v !== undefined && v !== null && v !== '' ?
15005 parseFloat(String(v).replace(stripRe, ""), 10) : '';
15010 cv = function(v){ return v === true || v === "true" || v == 1; };
15017 if(v instanceof Date){
15021 if(dateFormat == "timestamp"){
15022 return new Date(v*1000);
15024 return Date.parseDate(v, dateFormat);
15026 var parsed = Date.parse(v);
15027 return parsed ? new Date(parsed) : null;
15036 Roo.data.Field.prototype = {
15044 * Ext JS Library 1.1.1
15045 * Copyright(c) 2006-2007, Ext JS, LLC.
15047 * Originally Released Under LGPL - original licence link has changed is not relivant.
15050 * <script type="text/javascript">
15053 // Base class for reading structured data from a data source. This class is intended to be
15054 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15057 * @class Roo.data.DataReader
15058 * Base class for reading structured data from a data source. This class is intended to be
15059 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15062 Roo.data.DataReader = function(meta, recordType){
15066 this.recordType = recordType instanceof Array ?
15067 Roo.data.Record.create(recordType) : recordType;
15070 Roo.data.DataReader.prototype = {
15073 readerType : 'Data',
15075 * Create an empty record
15076 * @param {Object} data (optional) - overlay some values
15077 * @return {Roo.data.Record} record created.
15079 newRow : function(d) {
15081 this.recordType.prototype.fields.each(function(c) {
15083 case 'int' : da[c.name] = 0; break;
15084 case 'date' : da[c.name] = new Date(); break;
15085 case 'float' : da[c.name] = 0.0; break;
15086 case 'boolean' : da[c.name] = false; break;
15087 default : da[c.name] = ""; break;
15091 return new this.recordType(Roo.apply(da, d));
15097 * Ext JS Library 1.1.1
15098 * Copyright(c) 2006-2007, Ext JS, LLC.
15100 * Originally Released Under LGPL - original licence link has changed is not relivant.
15103 * <script type="text/javascript">
15107 * @class Roo.data.DataProxy
15108 * @extends Roo.data.Observable
15109 * This class is an abstract base class for implementations which provide retrieval of
15110 * unformatted data objects.<br>
15112 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
15113 * (of the appropriate type which knows how to parse the data object) to provide a block of
15114 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
15116 * Custom implementations must implement the load method as described in
15117 * {@link Roo.data.HttpProxy#load}.
15119 Roo.data.DataProxy = function(){
15122 * @event beforeload
15123 * Fires before a network request is made to retrieve a data object.
15124 * @param {Object} This DataProxy object.
15125 * @param {Object} params The params parameter to the load function.
15130 * Fires before the load method's callback is called.
15131 * @param {Object} This DataProxy object.
15132 * @param {Object} o The data object.
15133 * @param {Object} arg The callback argument object passed to the load function.
15137 * @event loadexception
15138 * Fires if an Exception occurs during data retrieval.
15139 * @param {Object} This DataProxy object.
15140 * @param {Object} o The data object.
15141 * @param {Object} arg The callback argument object passed to the load function.
15142 * @param {Object} e The Exception.
15144 loadexception : true
15146 Roo.data.DataProxy.superclass.constructor.call(this);
15149 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
15152 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
15156 * Ext JS Library 1.1.1
15157 * Copyright(c) 2006-2007, Ext JS, LLC.
15159 * Originally Released Under LGPL - original licence link has changed is not relivant.
15162 * <script type="text/javascript">
15165 * @class Roo.data.MemoryProxy
15166 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
15167 * to the Reader when its load method is called.
15169 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
15171 Roo.data.MemoryProxy = function(data){
15175 Roo.data.MemoryProxy.superclass.constructor.call(this);
15179 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
15182 * Load data from the requested source (in this case an in-memory
15183 * data object passed to the constructor), read the data object into
15184 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15185 * process that block using the passed callback.
15186 * @param {Object} params This parameter is not used by the MemoryProxy class.
15187 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15188 * object into a block of Roo.data.Records.
15189 * @param {Function} callback The function into which to pass the block of Roo.data.records.
15190 * The function must be passed <ul>
15191 * <li>The Record block object</li>
15192 * <li>The "arg" argument from the load function</li>
15193 * <li>A boolean success indicator</li>
15195 * @param {Object} scope The scope in which to call the callback
15196 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15198 load : function(params, reader, callback, scope, arg){
15199 params = params || {};
15202 result = reader.readRecords(params.data ? params.data :this.data);
15204 this.fireEvent("loadexception", this, arg, null, e);
15205 callback.call(scope, null, arg, false);
15208 callback.call(scope, result, arg, true);
15212 update : function(params, records){
15217 * Ext JS Library 1.1.1
15218 * Copyright(c) 2006-2007, Ext JS, LLC.
15220 * Originally Released Under LGPL - original licence link has changed is not relivant.
15223 * <script type="text/javascript">
15226 * @class Roo.data.HttpProxy
15227 * @extends Roo.data.DataProxy
15228 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
15229 * configured to reference a certain URL.<br><br>
15231 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
15232 * from which the running page was served.<br><br>
15234 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
15236 * Be aware that to enable the browser to parse an XML document, the server must set
15237 * the Content-Type header in the HTTP response to "text/xml".
15239 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
15240 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
15241 * will be used to make the request.
15243 Roo.data.HttpProxy = function(conn){
15244 Roo.data.HttpProxy.superclass.constructor.call(this);
15245 // is conn a conn config or a real conn?
15247 this.useAjax = !conn || !conn.events;
15251 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
15252 // thse are take from connection...
15255 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
15258 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
15259 * extra parameters to each request made by this object. (defaults to undefined)
15262 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
15263 * to each request made by this object. (defaults to undefined)
15266 * @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)
15269 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
15272 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
15278 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
15282 * Return the {@link Roo.data.Connection} object being used by this Proxy.
15283 * @return {Connection} The Connection object. This object may be used to subscribe to events on
15284 * a finer-grained basis than the DataProxy events.
15286 getConnection : function(){
15287 return this.useAjax ? Roo.Ajax : this.conn;
15291 * Load data from the configured {@link Roo.data.Connection}, read the data object into
15292 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
15293 * process that block using the passed callback.
15294 * @param {Object} params An object containing properties which are to be used as HTTP parameters
15295 * for the request to the remote server.
15296 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15297 * object into a block of Roo.data.Records.
15298 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15299 * The function must be passed <ul>
15300 * <li>The Record block object</li>
15301 * <li>The "arg" argument from the load function</li>
15302 * <li>A boolean success indicator</li>
15304 * @param {Object} scope The scope in which to call the callback
15305 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15307 load : function(params, reader, callback, scope, arg){
15308 if(this.fireEvent("beforeload", this, params) !== false){
15310 params : params || {},
15312 callback : callback,
15317 callback : this.loadResponse,
15321 Roo.applyIf(o, this.conn);
15322 if(this.activeRequest){
15323 Roo.Ajax.abort(this.activeRequest);
15325 this.activeRequest = Roo.Ajax.request(o);
15327 this.conn.request(o);
15330 callback.call(scope||this, null, arg, false);
15335 loadResponse : function(o, success, response){
15336 delete this.activeRequest;
15338 this.fireEvent("loadexception", this, o, response);
15339 o.request.callback.call(o.request.scope, null, o.request.arg, false);
15344 result = o.reader.read(response);
15346 this.fireEvent("loadexception", this, o, response, e);
15347 o.request.callback.call(o.request.scope, null, o.request.arg, false);
15351 this.fireEvent("load", this, o, o.request.arg);
15352 o.request.callback.call(o.request.scope, result, o.request.arg, true);
15356 update : function(dataSet){
15361 updateResponse : function(dataSet){
15366 * Ext JS Library 1.1.1
15367 * Copyright(c) 2006-2007, Ext JS, LLC.
15369 * Originally Released Under LGPL - original licence link has changed is not relivant.
15372 * <script type="text/javascript">
15376 * @class Roo.data.ScriptTagProxy
15377 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
15378 * other than the originating domain of the running page.<br><br>
15380 * <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
15381 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
15383 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
15384 * source code that is used as the source inside a <script> tag.<br><br>
15386 * In order for the browser to process the returned data, the server must wrap the data object
15387 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
15388 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
15389 * depending on whether the callback name was passed:
15392 boolean scriptTag = false;
15393 String cb = request.getParameter("callback");
15396 response.setContentType("text/javascript");
15398 response.setContentType("application/x-json");
15400 Writer out = response.getWriter();
15402 out.write(cb + "(");
15404 out.print(dataBlock.toJsonString());
15411 * @param {Object} config A configuration object.
15413 Roo.data.ScriptTagProxy = function(config){
15414 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
15415 Roo.apply(this, config);
15416 this.head = document.getElementsByTagName("head")[0];
15419 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
15421 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
15423 * @cfg {String} url The URL from which to request the data object.
15426 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
15430 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
15431 * the server the name of the callback function set up by the load call to process the returned data object.
15432 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
15433 * javascript output which calls this named function passing the data object as its only parameter.
15435 callbackParam : "callback",
15437 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
15438 * name to the request.
15443 * Load data from the configured URL, read the data object into
15444 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15445 * process that block using the passed callback.
15446 * @param {Object} params An object containing properties which are to be used as HTTP parameters
15447 * for the request to the remote server.
15448 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15449 * object into a block of Roo.data.Records.
15450 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15451 * The function must be passed <ul>
15452 * <li>The Record block object</li>
15453 * <li>The "arg" argument from the load function</li>
15454 * <li>A boolean success indicator</li>
15456 * @param {Object} scope The scope in which to call the callback
15457 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15459 load : function(params, reader, callback, scope, arg){
15460 if(this.fireEvent("beforeload", this, params) !== false){
15462 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
15464 var url = this.url;
15465 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
15467 url += "&_dc=" + (new Date().getTime());
15469 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
15472 cb : "stcCallback"+transId,
15473 scriptId : "stcScript"+transId,
15477 callback : callback,
15483 window[trans.cb] = function(o){
15484 conn.handleResponse(o, trans);
15487 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
15489 if(this.autoAbort !== false){
15493 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
15495 var script = document.createElement("script");
15496 script.setAttribute("src", url);
15497 script.setAttribute("type", "text/javascript");
15498 script.setAttribute("id", trans.scriptId);
15499 this.head.appendChild(script);
15501 this.trans = trans;
15503 callback.call(scope||this, null, arg, false);
15508 isLoading : function(){
15509 return this.trans ? true : false;
15513 * Abort the current server request.
15515 abort : function(){
15516 if(this.isLoading()){
15517 this.destroyTrans(this.trans);
15522 destroyTrans : function(trans, isLoaded){
15523 this.head.removeChild(document.getElementById(trans.scriptId));
15524 clearTimeout(trans.timeoutId);
15526 window[trans.cb] = undefined;
15528 delete window[trans.cb];
15531 // if hasn't been loaded, wait for load to remove it to prevent script error
15532 window[trans.cb] = function(){
15533 window[trans.cb] = undefined;
15535 delete window[trans.cb];
15542 handleResponse : function(o, trans){
15543 this.trans = false;
15544 this.destroyTrans(trans, true);
15547 result = trans.reader.readRecords(o);
15549 this.fireEvent("loadexception", this, o, trans.arg, e);
15550 trans.callback.call(trans.scope||window, null, trans.arg, false);
15553 this.fireEvent("load", this, o, trans.arg);
15554 trans.callback.call(trans.scope||window, result, trans.arg, true);
15558 handleFailure : function(trans){
15559 this.trans = false;
15560 this.destroyTrans(trans, false);
15561 this.fireEvent("loadexception", this, null, trans.arg);
15562 trans.callback.call(trans.scope||window, null, trans.arg, false);
15566 * Ext JS Library 1.1.1
15567 * Copyright(c) 2006-2007, Ext JS, LLC.
15569 * Originally Released Under LGPL - original licence link has changed is not relivant.
15572 * <script type="text/javascript">
15576 * @class Roo.data.JsonReader
15577 * @extends Roo.data.DataReader
15578 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
15579 * based on mappings in a provided Roo.data.Record constructor.
15581 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
15582 * in the reply previously.
15587 var RecordDef = Roo.data.Record.create([
15588 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
15589 {name: 'occupation'} // This field will use "occupation" as the mapping.
15591 var myReader = new Roo.data.JsonReader({
15592 totalProperty: "results", // The property which contains the total dataset size (optional)
15593 root: "rows", // The property which contains an Array of row objects
15594 id: "id" // The property within each row object that provides an ID for the record (optional)
15598 * This would consume a JSON file like this:
15600 { 'results': 2, 'rows': [
15601 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
15602 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
15605 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
15606 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
15607 * paged from the remote server.
15608 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
15609 * @cfg {String} root name of the property which contains the Array of row objects.
15610 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15611 * @cfg {Array} fields Array of field definition objects
15613 * Create a new JsonReader
15614 * @param {Object} meta Metadata configuration options
15615 * @param {Object} recordType Either an Array of field definition objects,
15616 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
15618 Roo.data.JsonReader = function(meta, recordType){
15621 // set some defaults:
15622 Roo.applyIf(meta, {
15623 totalProperty: 'total',
15624 successProperty : 'success',
15629 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15631 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
15633 readerType : 'Json',
15636 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
15637 * Used by Store query builder to append _requestMeta to params.
15640 metaFromRemote : false,
15642 * This method is only used by a DataProxy which has retrieved data from a remote server.
15643 * @param {Object} response The XHR object which contains the JSON data in its responseText.
15644 * @return {Object} data A data block which is used by an Roo.data.Store object as
15645 * a cache of Roo.data.Records.
15647 read : function(response){
15648 var json = response.responseText;
15650 var o = /* eval:var:o */ eval("("+json+")");
15652 throw {message: "JsonReader.read: Json object not found"};
15658 this.metaFromRemote = true;
15659 this.meta = o.metaData;
15660 this.recordType = Roo.data.Record.create(o.metaData.fields);
15661 this.onMetaChange(this.meta, this.recordType, o);
15663 return this.readRecords(o);
15666 // private function a store will implement
15667 onMetaChange : function(meta, recordType, o){
15674 simpleAccess: function(obj, subsc) {
15681 getJsonAccessor: function(){
15683 return function(expr) {
15685 return(re.test(expr))
15686 ? new Function("obj", "return obj." + expr)
15691 return Roo.emptyFn;
15696 * Create a data block containing Roo.data.Records from an XML document.
15697 * @param {Object} o An object which contains an Array of row objects in the property specified
15698 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
15699 * which contains the total size of the dataset.
15700 * @return {Object} data A data block which is used by an Roo.data.Store object as
15701 * a cache of Roo.data.Records.
15703 readRecords : function(o){
15705 * After any data loads, the raw JSON data is available for further custom processing.
15709 var s = this.meta, Record = this.recordType,
15710 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
15712 // Generate extraction functions for the totalProperty, the root, the id, and for each field
15714 if(s.totalProperty) {
15715 this.getTotal = this.getJsonAccessor(s.totalProperty);
15717 if(s.successProperty) {
15718 this.getSuccess = this.getJsonAccessor(s.successProperty);
15720 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
15722 var g = this.getJsonAccessor(s.id);
15723 this.getId = function(rec) {
15725 return (r === undefined || r === "") ? null : r;
15728 this.getId = function(){return null;};
15731 for(var jj = 0; jj < fl; jj++){
15733 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
15734 this.ef[jj] = this.getJsonAccessor(map);
15738 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
15739 if(s.totalProperty){
15740 var vt = parseInt(this.getTotal(o), 10);
15745 if(s.successProperty){
15746 var vs = this.getSuccess(o);
15747 if(vs === false || vs === 'false'){
15752 for(var i = 0; i < c; i++){
15755 var id = this.getId(n);
15756 for(var j = 0; j < fl; j++){
15758 var v = this.ef[j](n);
15760 Roo.log('missing convert for ' + f.name);
15764 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
15766 var record = new Record(values, id);
15768 records[i] = record;
15774 totalRecords : totalRecords
15777 // used when loading children.. @see loadDataFromChildren
15778 toLoadData: function(rec)
15780 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15781 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15782 return { data : data, total : data.length };
15787 * Ext JS Library 1.1.1
15788 * Copyright(c) 2006-2007, Ext JS, LLC.
15790 * Originally Released Under LGPL - original licence link has changed is not relivant.
15793 * <script type="text/javascript">
15797 * @class Roo.data.ArrayReader
15798 * @extends Roo.data.DataReader
15799 * Data reader class to create an Array of Roo.data.Record objects from an Array.
15800 * Each element of that Array represents a row of data fields. The
15801 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
15802 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
15806 var RecordDef = Roo.data.Record.create([
15807 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
15808 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
15810 var myReader = new Roo.data.ArrayReader({
15811 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
15815 * This would consume an Array like this:
15817 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
15821 * Create a new JsonReader
15822 * @param {Object} meta Metadata configuration options.
15823 * @param {Object|Array} recordType Either an Array of field definition objects
15825 * @cfg {Array} fields Array of field definition objects
15826 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15827 * as specified to {@link Roo.data.Record#create},
15828 * or an {@link Roo.data.Record} object
15831 * created using {@link Roo.data.Record#create}.
15833 Roo.data.ArrayReader = function(meta, recordType)
15835 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15838 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
15841 * Create a data block containing Roo.data.Records from an XML document.
15842 * @param {Object} o An Array of row objects which represents the dataset.
15843 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
15844 * a cache of Roo.data.Records.
15846 readRecords : function(o)
15848 var sid = this.meta ? this.meta.id : null;
15849 var recordType = this.recordType, fields = recordType.prototype.fields;
15852 for(var i = 0; i < root.length; i++){
15855 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
15856 for(var j = 0, jlen = fields.length; j < jlen; j++){
15857 var f = fields.items[j];
15858 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
15859 var v = n[k] !== undefined ? n[k] : f.defaultValue;
15861 values[f.name] = v;
15863 var record = new recordType(values, id);
15865 records[records.length] = record;
15869 totalRecords : records.length
15872 // used when loading children.. @see loadDataFromChildren
15873 toLoadData: function(rec)
15875 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15876 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15887 * @class Roo.bootstrap.ComboBox
15888 * @extends Roo.bootstrap.TriggerField
15889 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
15890 * @cfg {Boolean} append (true|false) default false
15891 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
15892 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
15893 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
15894 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
15895 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
15896 * @cfg {Boolean} animate default true
15897 * @cfg {Boolean} emptyResultText only for touch device
15898 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15899 * @cfg {String} emptyTitle default ''
15900 * @cfg {Number} width fixed with? experimental
15902 * Create a new ComboBox.
15903 * @param {Object} config Configuration options
15905 Roo.bootstrap.ComboBox = function(config){
15906 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15910 * Fires when the dropdown list is expanded
15911 * @param {Roo.bootstrap.ComboBox} combo This combo box
15916 * Fires when the dropdown list is collapsed
15917 * @param {Roo.bootstrap.ComboBox} combo This combo box
15921 * @event beforeselect
15922 * Fires before a list item is selected. Return false to cancel the selection.
15923 * @param {Roo.bootstrap.ComboBox} combo This combo box
15924 * @param {Roo.data.Record} record The data record returned from the underlying store
15925 * @param {Number} index The index of the selected item in the dropdown list
15927 'beforeselect' : true,
15930 * Fires when a list item is selected
15931 * @param {Roo.bootstrap.ComboBox} combo This combo box
15932 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15933 * @param {Number} index The index of the selected item in the dropdown list
15937 * @event beforequery
15938 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15939 * The event object passed has these properties:
15940 * @param {Roo.bootstrap.ComboBox} combo This combo box
15941 * @param {String} query The query
15942 * @param {Boolean} forceAll true to force "all" query
15943 * @param {Boolean} cancel true to cancel the query
15944 * @param {Object} e The query event object
15946 'beforequery': true,
15949 * Fires when the 'add' icon is pressed (add a listener to enable add button)
15950 * @param {Roo.bootstrap.ComboBox} combo This combo box
15955 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15956 * @param {Roo.bootstrap.ComboBox} combo This combo box
15957 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15962 * Fires when the remove value from the combobox array
15963 * @param {Roo.bootstrap.ComboBox} combo This combo box
15967 * @event afterremove
15968 * Fires when the remove value from the combobox array
15969 * @param {Roo.bootstrap.ComboBox} combo This combo box
15971 'afterremove' : true,
15973 * @event specialfilter
15974 * Fires when specialfilter
15975 * @param {Roo.bootstrap.ComboBox} combo This combo box
15977 'specialfilter' : true,
15980 * Fires when tick the element
15981 * @param {Roo.bootstrap.ComboBox} combo This combo box
15985 * @event touchviewdisplay
15986 * Fires when touch view require special display (default is using displayField)
15987 * @param {Roo.bootstrap.ComboBox} combo This combo box
15988 * @param {Object} cfg set html .
15990 'touchviewdisplay' : true
15995 this.tickItems = [];
15997 this.selectedIndex = -1;
15998 if(this.mode == 'local'){
15999 if(config.queryDelay === undefined){
16000 this.queryDelay = 10;
16002 if(config.minChars === undefined){
16008 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
16011 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16012 * rendering into an Roo.Editor, defaults to false)
16015 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16016 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16019 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16022 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16023 * the dropdown list (defaults to undefined, with no header element)
16027 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
16031 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16033 listWidth: undefined,
16035 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16036 * mode = 'remote' or 'text' if mode = 'local')
16038 displayField: undefined,
16041 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16042 * mode = 'remote' or 'value' if mode = 'local').
16043 * Note: use of a valueField requires the user make a selection
16044 * in order for a value to be mapped.
16046 valueField: undefined,
16048 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16053 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16054 * field's data value (defaults to the underlying DOM element's name)
16056 hiddenName: undefined,
16058 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16062 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16064 selectedClass: 'active',
16067 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16071 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16072 * anchor positions (defaults to 'tl-bl')
16074 listAlign: 'tl-bl?',
16076 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16080 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
16081 * query specified by the allQuery config option (defaults to 'query')
16083 triggerAction: 'query',
16085 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16086 * (defaults to 4, does not apply if editable = false)
16090 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16091 * delay (typeAheadDelay) if it matches a known value (defaults to false)
16095 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
16096 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
16100 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
16101 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
16105 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
16106 * when editable = true (defaults to false)
16108 selectOnFocus:false,
16110 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
16112 queryParam: 'query',
16114 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
16115 * when mode = 'remote' (defaults to 'Loading...')
16117 loadingText: 'Loading...',
16119 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
16123 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
16127 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
16128 * traditional select (defaults to true)
16132 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
16136 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
16140 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
16141 * listWidth has a higher value)
16145 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
16146 * allow the user to set arbitrary text into the field (defaults to false)
16148 forceSelection:false,
16150 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
16151 * if typeAhead = true (defaults to 250)
16153 typeAheadDelay : 250,
16155 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
16156 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
16158 valueNotFoundText : undefined,
16160 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
16162 blockFocus : false,
16165 * @cfg {Boolean} disableClear Disable showing of clear button.
16167 disableClear : false,
16169 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
16171 alwaysQuery : false,
16174 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
16179 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
16181 invalidClass : "has-warning",
16184 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
16186 validClass : "has-success",
16189 * @cfg {Boolean} specialFilter (true|false) special filter default false
16191 specialFilter : false,
16194 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
16196 mobileTouchView : true,
16199 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
16201 useNativeIOS : false,
16204 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
16206 mobile_restrict_height : false,
16208 ios_options : false,
16220 btnPosition : 'right',
16221 triggerList : true,
16222 showToggleBtn : true,
16224 emptyResultText: 'Empty',
16225 triggerText : 'Select',
16229 // element that contains real text value.. (when hidden is used..)
16231 getAutoCreate : function()
16236 * Render classic select for iso
16239 if(Roo.isIOS && this.useNativeIOS){
16240 cfg = this.getAutoCreateNativeIOS();
16248 if(Roo.isTouch && this.mobileTouchView){
16249 cfg = this.getAutoCreateTouchView();
16256 if(!this.tickable){
16257 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
16262 * ComboBox with tickable selections
16265 var align = this.labelAlign || this.parentLabelAlign();
16268 cls : 'form-group roo-combobox-tickable' //input-group
16271 var btn_text_select = '';
16272 var btn_text_done = '';
16273 var btn_text_cancel = '';
16275 if (this.btn_text_show) {
16276 btn_text_select = 'Select';
16277 btn_text_done = 'Done';
16278 btn_text_cancel = 'Cancel';
16283 cls : 'tickable-buttons',
16288 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
16289 //html : this.triggerText
16290 html: btn_text_select
16296 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
16298 html: btn_text_done
16304 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
16306 html: btn_text_cancel
16312 buttons.cn.unshift({
16314 cls: 'roo-select2-search-field-input'
16320 Roo.each(buttons.cn, function(c){
16322 c.cls += ' btn-' + _this.size;
16325 if (_this.disabled) {
16332 style : 'display: contents',
16337 cls: 'form-hidden-field'
16341 cls: 'roo-select2-choices',
16345 cls: 'roo-select2-search-field',
16356 cls: 'roo-select2-container input-group roo-select2-container-multi',
16362 // cls: 'typeahead typeahead-long dropdown-menu',
16363 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
16368 if(this.hasFeedback && !this.allowBlank){
16372 cls: 'glyphicon form-control-feedback'
16375 combobox.cn.push(feedback);
16382 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
16383 tooltip : 'This field is required'
16385 if (Roo.bootstrap.version == 4) {
16388 style : 'display:none'
16391 if (align ==='left' && this.fieldLabel.length) {
16393 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
16400 cls : 'control-label col-form-label',
16401 html : this.fieldLabel
16413 var labelCfg = cfg.cn[1];
16414 var contentCfg = cfg.cn[2];
16417 if(this.indicatorpos == 'right'){
16423 cls : 'control-label col-form-label',
16427 html : this.fieldLabel
16443 labelCfg = cfg.cn[0];
16444 contentCfg = cfg.cn[1];
16448 if(this.labelWidth > 12){
16449 labelCfg.style = "width: " + this.labelWidth + 'px';
16451 if(this.width * 1 > 0){
16452 contentCfg.style = "width: " + this.width + 'px';
16454 if(this.labelWidth < 13 && this.labelmd == 0){
16455 this.labelmd = this.labelWidth;
16458 if(this.labellg > 0){
16459 labelCfg.cls += ' col-lg-' + this.labellg;
16460 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
16463 if(this.labelmd > 0){
16464 labelCfg.cls += ' col-md-' + this.labelmd;
16465 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
16468 if(this.labelsm > 0){
16469 labelCfg.cls += ' col-sm-' + this.labelsm;
16470 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
16473 if(this.labelxs > 0){
16474 labelCfg.cls += ' col-xs-' + this.labelxs;
16475 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
16479 } else if ( this.fieldLabel.length) {
16480 // Roo.log(" label");
16485 //cls : 'input-group-addon',
16486 html : this.fieldLabel
16491 if(this.indicatorpos == 'right'){
16495 //cls : 'input-group-addon',
16496 html : this.fieldLabel
16506 // Roo.log(" no label && no align");
16513 ['xs','sm','md','lg'].map(function(size){
16514 if (settings[size]) {
16515 cfg.cls += ' col-' + size + '-' + settings[size];
16523 _initEventsCalled : false,
16526 initEvents: function()
16528 if (this._initEventsCalled) { // as we call render... prevent looping...
16531 this._initEventsCalled = true;
16534 throw "can not find store for combo";
16537 this.indicator = this.indicatorEl();
16539 this.store = Roo.factory(this.store, Roo.data);
16540 this.store.parent = this;
16542 // if we are building from html. then this element is so complex, that we can not really
16543 // use the rendered HTML.
16544 // so we have to trash and replace the previous code.
16545 if (Roo.XComponent.build_from_html) {
16546 // remove this element....
16547 var e = this.el.dom, k=0;
16548 while (e ) { e = e.previousSibling; ++k;}
16553 this.rendered = false;
16555 this.render(this.parent().getChildContainer(true), k);
16558 if(Roo.isIOS && this.useNativeIOS){
16559 this.initIOSView();
16567 if(Roo.isTouch && this.mobileTouchView){
16568 this.initTouchView();
16573 this.initTickableEvents();
16577 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
16579 if(this.hiddenName){
16581 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16583 this.hiddenField.dom.value =
16584 this.hiddenValue !== undefined ? this.hiddenValue :
16585 this.value !== undefined ? this.value : '';
16587 // prevent input submission
16588 this.el.dom.removeAttribute('name');
16589 this.hiddenField.dom.setAttribute('name', this.hiddenName);
16594 // this.el.dom.setAttribute('autocomplete', 'off');
16597 var cls = 'x-combo-list';
16599 //this.list = new Roo.Layer({
16600 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
16606 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16607 _this.list.setWidth(lw);
16610 this.list.on('mouseover', this.onViewOver, this);
16611 this.list.on('mousemove', this.onViewMove, this);
16612 this.list.on('scroll', this.onViewScroll, this);
16615 this.list.swallowEvent('mousewheel');
16616 this.assetHeight = 0;
16619 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
16620 this.assetHeight += this.header.getHeight();
16623 this.innerList = this.list.createChild({cls:cls+'-inner'});
16624 this.innerList.on('mouseover', this.onViewOver, this);
16625 this.innerList.on('mousemove', this.onViewMove, this);
16626 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16628 if(this.allowBlank && !this.pageSize && !this.disableClear){
16629 this.footer = this.list.createChild({cls:cls+'-ft'});
16630 this.pageTb = new Roo.Toolbar(this.footer);
16634 this.footer = this.list.createChild({cls:cls+'-ft'});
16635 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
16636 {pageSize: this.pageSize});
16640 if (this.pageTb && this.allowBlank && !this.disableClear) {
16642 this.pageTb.add(new Roo.Toolbar.Fill(), {
16643 cls: 'x-btn-icon x-btn-clear',
16645 handler: function()
16648 _this.clearValue();
16649 _this.onSelect(false, -1);
16654 this.assetHeight += this.footer.getHeight();
16659 this.tpl = Roo.bootstrap.version == 4 ?
16660 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
16661 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
16664 this.view = new Roo.View(this.list, this.tpl, {
16665 singleSelect:true, store: this.store, selectedClass: this.selectedClass
16667 //this.view.wrapEl.setDisplayed(false);
16668 this.view.on('click', this.onViewClick, this);
16671 this.store.on('beforeload', this.onBeforeLoad, this);
16672 this.store.on('load', this.onLoad, this);
16673 this.store.on('loadexception', this.onLoadException, this);
16675 if(this.resizable){
16676 this.resizer = new Roo.Resizable(this.list, {
16677 pinned:true, handles:'se'
16679 this.resizer.on('resize', function(r, w, h){
16680 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
16681 this.listWidth = w;
16682 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
16683 this.restrictHeight();
16685 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
16688 if(!this.editable){
16689 this.editable = true;
16690 this.setEditable(false);
16695 if (typeof(this.events.add.listeners) != 'undefined') {
16697 this.addicon = this.wrap.createChild(
16698 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
16700 this.addicon.on('click', function(e) {
16701 this.fireEvent('add', this);
16704 if (typeof(this.events.edit.listeners) != 'undefined') {
16706 this.editicon = this.wrap.createChild(
16707 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
16708 if (this.addicon) {
16709 this.editicon.setStyle('margin-left', '40px');
16711 this.editicon.on('click', function(e) {
16713 // we fire even if inothing is selected..
16714 this.fireEvent('edit', this, this.lastData );
16720 this.keyNav = new Roo.KeyNav(this.inputEl(), {
16721 "up" : function(e){
16722 this.inKeyMode = true;
16726 "down" : function(e){
16727 if(!this.isExpanded()){
16728 this.onTriggerClick();
16730 this.inKeyMode = true;
16735 "enter" : function(e){
16736 // this.onViewClick();
16740 if(this.fireEvent("specialkey", this, e)){
16741 this.onViewClick(false);
16747 "esc" : function(e){
16751 "tab" : function(e){
16754 if(this.fireEvent("specialkey", this, e)){
16755 this.onViewClick(false);
16763 doRelay : function(foo, bar, hname){
16764 if(hname == 'down' || this.scope.isExpanded()){
16765 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16774 this.queryDelay = Math.max(this.queryDelay || 10,
16775 this.mode == 'local' ? 10 : 250);
16778 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16780 if(this.typeAhead){
16781 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16783 if(this.editable !== false){
16784 this.inputEl().on("keyup", this.onKeyUp, this);
16786 if(this.forceSelection){
16787 this.inputEl().on('blur', this.doForce, this);
16791 this.choices = this.el.select('ul.roo-select2-choices', true).first();
16792 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16796 initTickableEvents: function()
16800 if(this.hiddenName){
16802 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16804 this.hiddenField.dom.value =
16805 this.hiddenValue !== undefined ? this.hiddenValue :
16806 this.value !== undefined ? this.value : '';
16808 // prevent input submission
16809 this.el.dom.removeAttribute('name');
16810 this.hiddenField.dom.setAttribute('name', this.hiddenName);
16815 // this.list = this.el.select('ul.dropdown-menu',true).first();
16817 this.choices = this.el.select('ul.roo-select2-choices', true).first();
16818 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16819 if(this.triggerList){
16820 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
16823 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
16824 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
16826 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
16827 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
16829 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
16830 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
16832 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
16833 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
16834 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
16837 this.cancelBtn.hide();
16842 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16843 _this.list.setWidth(lw);
16846 this.list.on('mouseover', this.onViewOver, this);
16847 this.list.on('mousemove', this.onViewMove, this);
16849 this.list.on('scroll', this.onViewScroll, this);
16852 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
16853 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
16856 this.view = new Roo.View(this.list, this.tpl, {
16861 selectedClass: this.selectedClass
16864 //this.view.wrapEl.setDisplayed(false);
16865 this.view.on('click', this.onViewClick, this);
16869 this.store.on('beforeload', this.onBeforeLoad, this);
16870 this.store.on('load', this.onLoad, this);
16871 this.store.on('loadexception', this.onLoadException, this);
16874 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
16875 "up" : function(e){
16876 this.inKeyMode = true;
16880 "down" : function(e){
16881 this.inKeyMode = true;
16885 "enter" : function(e){
16886 if(this.fireEvent("specialkey", this, e)){
16887 this.onViewClick(false);
16893 "esc" : function(e){
16894 this.onTickableFooterButtonClick(e, false, false);
16897 "tab" : function(e){
16898 this.fireEvent("specialkey", this, e);
16900 this.onTickableFooterButtonClick(e, false, false);
16907 doRelay : function(e, fn, key){
16908 if(this.scope.isExpanded()){
16909 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16918 this.queryDelay = Math.max(this.queryDelay || 10,
16919 this.mode == 'local' ? 10 : 250);
16922 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16924 if(this.typeAhead){
16925 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16928 if(this.editable !== false){
16929 this.tickableInputEl().on("keyup", this.onKeyUp, this);
16932 this.indicator = this.indicatorEl();
16934 if(this.indicator){
16935 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16936 this.indicator.hide();
16941 onDestroy : function(){
16943 this.view.setStore(null);
16944 this.view.el.removeAllListeners();
16945 this.view.el.remove();
16946 this.view.purgeListeners();
16949 this.list.dom.innerHTML = '';
16953 this.store.un('beforeload', this.onBeforeLoad, this);
16954 this.store.un('load', this.onLoad, this);
16955 this.store.un('loadexception', this.onLoadException, this);
16957 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16961 fireKey : function(e){
16962 if(e.isNavKeyPress() && !this.list.isVisible()){
16963 this.fireEvent("specialkey", this, e);
16968 onResize: function(w, h)
16972 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16974 // if(typeof w != 'number'){
16975 // // we do not handle it!?!?
16978 // var tw = this.trigger.getWidth();
16979 // // tw += this.addicon ? this.addicon.getWidth() : 0;
16980 // // tw += this.editicon ? this.editicon.getWidth() : 0;
16982 // this.inputEl().setWidth( this.adjustWidth('input', x));
16984 // //this.trigger.setStyle('left', x+'px');
16986 // if(this.list && this.listWidth === undefined){
16987 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16988 // this.list.setWidth(lw);
16989 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16997 * Allow or prevent the user from directly editing the field text. If false is passed,
16998 * the user will only be able to select from the items defined in the dropdown list. This method
16999 * is the runtime equivalent of setting the 'editable' config option at config time.
17000 * @param {Boolean} value True to allow the user to directly edit the field text
17002 setEditable : function(value){
17003 if(value == this.editable){
17006 this.editable = value;
17008 this.inputEl().dom.setAttribute('readOnly', true);
17009 this.inputEl().on('mousedown', this.onTriggerClick, this);
17010 this.inputEl().addClass('x-combo-noedit');
17012 this.inputEl().dom.removeAttribute('readOnly');
17013 this.inputEl().un('mousedown', this.onTriggerClick, this);
17014 this.inputEl().removeClass('x-combo-noedit');
17020 onBeforeLoad : function(combo,opts){
17021 if(!this.hasFocus){
17025 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17027 this.restrictHeight();
17028 this.selectedIndex = -1;
17032 onLoad : function(){
17034 this.hasQuery = false;
17036 if(!this.hasFocus){
17040 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17041 this.loading.hide();
17044 if(this.store.getCount() > 0){
17047 this.restrictHeight();
17048 if(this.lastQuery == this.allQuery){
17049 if(this.editable && !this.tickable){
17050 this.inputEl().dom.select();
17054 !this.selectByValue(this.value, true) &&
17057 !this.store.lastOptions ||
17058 typeof(this.store.lastOptions.add) == 'undefined' ||
17059 this.store.lastOptions.add != true
17062 this.select(0, true);
17065 if(this.autoFocus){
17068 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17069 this.taTask.delay(this.typeAheadDelay);
17073 this.onEmptyResults();
17079 onLoadException : function()
17081 this.hasQuery = false;
17083 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17084 this.loading.hide();
17087 if(this.tickable && this.editable){
17092 // only causes errors at present
17093 //Roo.log(this.store.reader.jsonData);
17094 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
17096 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
17102 onTypeAhead : function(){
17103 if(this.store.getCount() > 0){
17104 var r = this.store.getAt(0);
17105 var newValue = r.data[this.displayField];
17106 var len = newValue.length;
17107 var selStart = this.getRawValue().length;
17109 if(selStart != len){
17110 this.setRawValue(newValue);
17111 this.selectText(selStart, newValue.length);
17117 onSelect : function(record, index){
17119 if(this.fireEvent('beforeselect', this, record, index) !== false){
17121 this.setFromData(index > -1 ? record.data : false);
17124 this.fireEvent('select', this, record, index);
17129 * Returns the currently selected field value or empty string if no value is set.
17130 * @return {String} value The selected value
17132 getValue : function()
17134 if(Roo.isIOS && this.useNativeIOS){
17135 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
17139 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
17142 if(this.valueField){
17143 return typeof this.value != 'undefined' ? this.value : '';
17145 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
17149 getRawValue : function()
17151 if(Roo.isIOS && this.useNativeIOS){
17152 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
17155 var v = this.inputEl().getValue();
17161 * Clears any text/value currently set in the field
17163 clearValue : function(){
17165 if(this.hiddenField){
17166 this.hiddenField.dom.value = '';
17169 this.setRawValue('');
17170 this.lastSelectionText = '';
17171 this.lastData = false;
17173 var close = this.closeTriggerEl();
17184 * Sets the specified value into the field. If the value finds a match, the corresponding record text
17185 * will be displayed in the field. If the value does not match the data value of an existing item,
17186 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
17187 * Otherwise the field will be blank (although the value will still be set).
17188 * @param {String} value The value to match
17190 setValue : function(v)
17192 if(Roo.isIOS && this.useNativeIOS){
17193 this.setIOSValue(v);
17203 if(this.valueField){
17204 var r = this.findRecord(this.valueField, v);
17206 text = r.data[this.displayField];
17207 }else if(this.valueNotFoundText !== undefined){
17208 text = this.valueNotFoundText;
17211 this.lastSelectionText = text;
17212 if(this.hiddenField){
17213 this.hiddenField.dom.value = v;
17215 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
17218 var close = this.closeTriggerEl();
17221 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
17227 * @property {Object} the last set data for the element
17232 * Sets the value of the field based on a object which is related to the record format for the store.
17233 * @param {Object} value the value to set as. or false on reset?
17235 setFromData : function(o){
17242 var dv = ''; // display value
17243 var vv = ''; // value value..
17245 if (this.displayField) {
17246 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17248 // this is an error condition!!!
17249 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
17252 if(this.valueField){
17253 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
17256 var close = this.closeTriggerEl();
17259 if(dv.length || vv * 1 > 0){
17261 this.blockFocus=true;
17267 if(this.hiddenField){
17268 this.hiddenField.dom.value = vv;
17270 this.lastSelectionText = dv;
17271 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17275 // no hidden field.. - we store the value in 'value', but still display
17276 // display field!!!!
17277 this.lastSelectionText = dv;
17278 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17285 reset : function(){
17286 // overridden so that last data is reset..
17293 this.setValue(this.originalValue);
17294 //this.clearInvalid();
17295 this.lastData = false;
17297 this.view.clearSelections();
17303 findRecord : function(prop, value){
17305 if(this.store.getCount() > 0){
17306 this.store.each(function(r){
17307 if(r.data[prop] == value){
17317 getName: function()
17319 // returns hidden if it's set..
17320 if (!this.rendered) {return ''};
17321 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
17325 onViewMove : function(e, t){
17326 this.inKeyMode = false;
17330 onViewOver : function(e, t){
17331 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
17334 var item = this.view.findItemFromChild(t);
17337 var index = this.view.indexOf(item);
17338 this.select(index, false);
17343 onViewClick : function(view, doFocus, el, e)
17345 var index = this.view.getSelectedIndexes()[0];
17347 var r = this.store.getAt(index);
17351 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
17358 Roo.each(this.tickItems, function(v,k){
17360 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
17362 _this.tickItems.splice(k, 1);
17364 if(typeof(e) == 'undefined' && view == false){
17365 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
17377 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
17378 this.tickItems.push(r.data);
17381 if(typeof(e) == 'undefined' && view == false){
17382 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
17389 this.onSelect(r, index);
17391 if(doFocus !== false && !this.blockFocus){
17392 this.inputEl().focus();
17397 restrictHeight : function(){
17398 //this.innerList.dom.style.height = '';
17399 //var inner = this.innerList.dom;
17400 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
17401 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
17402 //this.list.beginUpdate();
17403 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
17404 this.list.alignTo(this.inputEl(), this.listAlign);
17405 this.list.alignTo(this.inputEl(), this.listAlign);
17406 //this.list.endUpdate();
17410 onEmptyResults : function(){
17412 if(this.tickable && this.editable){
17413 this.hasFocus = false;
17414 this.restrictHeight();
17422 * Returns true if the dropdown list is expanded, else false.
17424 isExpanded : function(){
17425 return this.list.isVisible();
17429 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
17430 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17431 * @param {String} value The data value of the item to select
17432 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17433 * selected item if it is not currently in view (defaults to true)
17434 * @return {Boolean} True if the value matched an item in the list, else false
17436 selectByValue : function(v, scrollIntoView){
17437 if(v !== undefined && v !== null){
17438 var r = this.findRecord(this.valueField || this.displayField, v);
17440 this.select(this.store.indexOf(r), scrollIntoView);
17448 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
17449 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17450 * @param {Number} index The zero-based index of the list item to select
17451 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17452 * selected item if it is not currently in view (defaults to true)
17454 select : function(index, scrollIntoView){
17455 this.selectedIndex = index;
17456 this.view.select(index);
17457 if(scrollIntoView !== false){
17458 var el = this.view.getNode(index);
17460 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
17463 this.list.scrollChildIntoView(el, false);
17469 selectNext : function(){
17470 var ct = this.store.getCount();
17472 if(this.selectedIndex == -1){
17474 }else if(this.selectedIndex < ct-1){
17475 this.select(this.selectedIndex+1);
17481 selectPrev : function(){
17482 var ct = this.store.getCount();
17484 if(this.selectedIndex == -1){
17486 }else if(this.selectedIndex != 0){
17487 this.select(this.selectedIndex-1);
17493 onKeyUp : function(e){
17494 if(this.editable !== false && !e.isSpecialKey()){
17495 this.lastKey = e.getKey();
17496 this.dqTask.delay(this.queryDelay);
17501 validateBlur : function(){
17502 return !this.list || !this.list.isVisible();
17506 initQuery : function(){
17508 var v = this.getRawValue();
17510 if(this.tickable && this.editable){
17511 v = this.tickableInputEl().getValue();
17518 doForce : function(){
17519 if(this.inputEl().dom.value.length > 0){
17520 this.inputEl().dom.value =
17521 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
17527 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
17528 * query allowing the query action to be canceled if needed.
17529 * @param {String} query The SQL query to execute
17530 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
17531 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
17532 * saved in the current store (defaults to false)
17534 doQuery : function(q, forceAll){
17536 if(q === undefined || q === null){
17541 forceAll: forceAll,
17545 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
17550 forceAll = qe.forceAll;
17551 if(forceAll === true || (q.length >= this.minChars)){
17553 this.hasQuery = true;
17555 if(this.lastQuery != q || this.alwaysQuery){
17556 this.lastQuery = q;
17557 if(this.mode == 'local'){
17558 this.selectedIndex = -1;
17560 this.store.clearFilter();
17563 if(this.specialFilter){
17564 this.fireEvent('specialfilter', this);
17569 this.store.filter(this.displayField, q);
17572 this.store.fireEvent("datachanged", this.store);
17579 this.store.baseParams[this.queryParam] = q;
17581 var options = {params : this.getParams(q)};
17584 options.add = true;
17585 options.params.start = this.page * this.pageSize;
17588 this.store.load(options);
17591 * this code will make the page width larger, at the beginning, the list not align correctly,
17592 * we should expand the list on onLoad
17593 * so command out it
17598 this.selectedIndex = -1;
17603 this.loadNext = false;
17607 getParams : function(q){
17609 //p[this.queryParam] = q;
17613 p.limit = this.pageSize;
17619 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
17621 collapse : function(){
17622 if(!this.isExpanded()){
17628 this.hasFocus = false;
17632 this.cancelBtn.hide();
17633 this.trigger.show();
17636 this.tickableInputEl().dom.value = '';
17637 this.tickableInputEl().blur();
17642 Roo.get(document).un('mousedown', this.collapseIf, this);
17643 Roo.get(document).un('mousewheel', this.collapseIf, this);
17644 if (!this.editable) {
17645 Roo.get(document).un('keydown', this.listKeyPress, this);
17647 this.fireEvent('collapse', this);
17653 collapseIf : function(e){
17654 var in_combo = e.within(this.el);
17655 var in_list = e.within(this.list);
17656 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
17658 if (in_combo || in_list || is_list) {
17659 //e.stopPropagation();
17664 this.onTickableFooterButtonClick(e, false, false);
17672 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
17674 expand : function(){
17676 if(this.isExpanded() || !this.hasFocus){
17680 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
17681 this.list.setWidth(lw);
17687 this.restrictHeight();
17691 this.tickItems = Roo.apply([], this.item);
17694 this.cancelBtn.show();
17695 this.trigger.hide();
17698 this.tickableInputEl().focus();
17703 Roo.get(document).on('mousedown', this.collapseIf, this);
17704 Roo.get(document).on('mousewheel', this.collapseIf, this);
17705 if (!this.editable) {
17706 Roo.get(document).on('keydown', this.listKeyPress, this);
17709 this.fireEvent('expand', this);
17713 // Implements the default empty TriggerField.onTriggerClick function
17714 onTriggerClick : function(e)
17716 Roo.log('trigger click');
17718 if(this.disabled || !this.triggerList){
17723 this.loadNext = false;
17725 if(this.isExpanded()){
17727 if (!this.blockFocus) {
17728 this.inputEl().focus();
17732 this.hasFocus = true;
17733 if(this.triggerAction == 'all') {
17734 this.doQuery(this.allQuery, true);
17736 this.doQuery(this.getRawValue());
17738 if (!this.blockFocus) {
17739 this.inputEl().focus();
17744 onTickableTriggerClick : function(e)
17751 this.loadNext = false;
17752 this.hasFocus = true;
17754 if(this.triggerAction == 'all') {
17755 this.doQuery(this.allQuery, true);
17757 this.doQuery(this.getRawValue());
17761 onSearchFieldClick : function(e)
17763 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
17764 this.onTickableFooterButtonClick(e, false, false);
17768 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
17773 this.loadNext = false;
17774 this.hasFocus = true;
17776 if(this.triggerAction == 'all') {
17777 this.doQuery(this.allQuery, true);
17779 this.doQuery(this.getRawValue());
17783 listKeyPress : function(e)
17785 //Roo.log('listkeypress');
17786 // scroll to first matching element based on key pres..
17787 if (e.isSpecialKey()) {
17790 var k = String.fromCharCode(e.getKey()).toUpperCase();
17793 var csel = this.view.getSelectedNodes();
17794 var cselitem = false;
17796 var ix = this.view.indexOf(csel[0]);
17797 cselitem = this.store.getAt(ix);
17798 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
17804 this.store.each(function(v) {
17806 // start at existing selection.
17807 if (cselitem.id == v.id) {
17813 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
17814 match = this.store.indexOf(v);
17820 if (match === false) {
17821 return true; // no more action?
17824 this.view.select(match);
17825 var sn = Roo.get(this.view.getSelectedNodes()[0]);
17826 sn.scrollIntoView(sn.dom.parentNode, false);
17829 onViewScroll : function(e, t){
17831 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){
17835 this.hasQuery = true;
17837 this.loading = this.list.select('.loading', true).first();
17839 if(this.loading === null){
17840 this.list.createChild({
17842 cls: 'loading roo-select2-more-results roo-select2-active',
17843 html: 'Loading more results...'
17846 this.loading = this.list.select('.loading', true).first();
17848 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
17850 this.loading.hide();
17853 this.loading.show();
17858 this.loadNext = true;
17860 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
17865 addItem : function(o)
17867 var dv = ''; // display value
17869 if (this.displayField) {
17870 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17872 // this is an error condition!!!
17873 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
17880 var choice = this.choices.createChild({
17882 cls: 'roo-select2-search-choice',
17891 cls: 'roo-select2-search-choice-close fa fa-times',
17896 }, this.searchField);
17898 var close = choice.select('a.roo-select2-search-choice-close', true).first();
17900 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17908 this.inputEl().dom.value = '';
17913 onRemoveItem : function(e, _self, o)
17915 e.preventDefault();
17917 this.lastItem = Roo.apply([], this.item);
17919 var index = this.item.indexOf(o.data) * 1;
17922 Roo.log('not this item?!');
17926 this.item.splice(index, 1);
17931 this.fireEvent('remove', this, e);
17937 syncValue : function()
17939 if(!this.item.length){
17946 Roo.each(this.item, function(i){
17947 if(_this.valueField){
17948 value.push(i[_this.valueField]);
17955 this.value = value.join(',');
17957 if(this.hiddenField){
17958 this.hiddenField.dom.value = this.value;
17961 this.store.fireEvent("datachanged", this.store);
17966 clearItem : function()
17968 if(!this.multiple){
17974 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17982 if(this.tickable && !Roo.isTouch){
17983 this.view.refresh();
17987 inputEl: function ()
17989 if(Roo.isIOS && this.useNativeIOS){
17990 return this.el.select('select.roo-ios-select', true).first();
17993 if(Roo.isTouch && this.mobileTouchView){
17994 return this.el.select('input.form-control',true).first();
17998 return this.searchField;
18001 return this.el.select('input.form-control',true).first();
18004 onTickableFooterButtonClick : function(e, btn, el)
18006 e.preventDefault();
18008 this.lastItem = Roo.apply([], this.item);
18010 if(btn && btn.name == 'cancel'){
18011 this.tickItems = Roo.apply([], this.item);
18020 Roo.each(this.tickItems, function(o){
18028 validate : function()
18030 if(this.getVisibilityEl().hasClass('hidden')){
18034 var v = this.getRawValue();
18037 v = this.getValue();
18040 if(this.disabled || this.allowBlank || v.length){
18045 this.markInvalid();
18049 tickableInputEl : function()
18051 if(!this.tickable || !this.editable){
18052 return this.inputEl();
18055 return this.inputEl().select('.roo-select2-search-field-input', true).first();
18059 getAutoCreateTouchView : function()
18064 cls: 'form-group' //input-group
18070 type : this.inputType,
18071 cls : 'form-control x-combo-noedit',
18072 autocomplete: 'new-password',
18073 placeholder : this.placeholder || '',
18078 input.name = this.name;
18082 input.cls += ' input-' + this.size;
18085 if (this.disabled) {
18086 input.disabled = true;
18090 cls : 'roo-combobox-wrap',
18097 inputblock.cls += ' input-group';
18099 inputblock.cn.unshift({
18101 cls : 'input-group-addon input-group-prepend input-group-text',
18106 if(this.removable && !this.multiple){
18107 inputblock.cls += ' roo-removable';
18109 inputblock.cn.push({
18112 cls : 'roo-combo-removable-btn close'
18116 if(this.hasFeedback && !this.allowBlank){
18118 inputblock.cls += ' has-feedback';
18120 inputblock.cn.push({
18122 cls: 'glyphicon form-control-feedback'
18129 inputblock.cls += (this.before) ? '' : ' input-group';
18131 inputblock.cn.push({
18133 cls : 'input-group-addon input-group-append input-group-text',
18139 var ibwrap = inputblock;
18144 cls: 'roo-select2-choices',
18148 cls: 'roo-select2-search-field',
18161 cls: 'roo-select2-container input-group roo-touchview-combobox ',
18166 cls: 'form-hidden-field'
18172 if(!this.multiple && this.showToggleBtn){
18178 if (this.caret != false) {
18181 cls: 'fa fa-' + this.caret
18188 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
18190 Roo.bootstrap.version == 3 ? caret : '',
18193 cls: 'combobox-clear',
18207 combobox.cls += ' roo-select2-container-multi';
18210 var required = this.allowBlank ? {
18212 style: 'display: none'
18215 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
18216 tooltip : 'This field is required'
18219 var align = this.labelAlign || this.parentLabelAlign();
18221 if (align ==='left' && this.fieldLabel.length) {
18227 cls : 'control-label col-form-label',
18228 html : this.fieldLabel
18232 cls : 'roo-combobox-wrap ',
18239 var labelCfg = cfg.cn[1];
18240 var contentCfg = cfg.cn[2];
18243 if(this.indicatorpos == 'right'){
18248 cls : 'control-label col-form-label',
18252 html : this.fieldLabel
18258 cls : "roo-combobox-wrap ",
18266 labelCfg = cfg.cn[0];
18267 contentCfg = cfg.cn[1];
18272 if(this.labelWidth > 12){
18273 labelCfg.style = "width: " + this.labelWidth + 'px';
18276 if(this.labelWidth < 13 && this.labelmd == 0){
18277 this.labelmd = this.labelWidth;
18280 if(this.labellg > 0){
18281 labelCfg.cls += ' col-lg-' + this.labellg;
18282 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
18285 if(this.labelmd > 0){
18286 labelCfg.cls += ' col-md-' + this.labelmd;
18287 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
18290 if(this.labelsm > 0){
18291 labelCfg.cls += ' col-sm-' + this.labelsm;
18292 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
18295 if(this.labelxs > 0){
18296 labelCfg.cls += ' col-xs-' + this.labelxs;
18297 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
18301 } else if ( this.fieldLabel.length) {
18306 cls : 'control-label',
18307 html : this.fieldLabel
18318 if(this.indicatorpos == 'right'){
18322 cls : 'control-label',
18323 html : this.fieldLabel,
18341 var settings = this;
18343 ['xs','sm','md','lg'].map(function(size){
18344 if (settings[size]) {
18345 cfg.cls += ' col-' + size + '-' + settings[size];
18352 initTouchView : function()
18354 this.renderTouchView();
18356 this.touchViewEl.on('scroll', function(){
18357 this.el.dom.scrollTop = 0;
18360 this.originalValue = this.getValue();
18362 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
18364 this.inputEl().on("click", this.showTouchView, this);
18365 if (this.triggerEl) {
18366 this.triggerEl.on("click", this.showTouchView, this);
18370 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
18371 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
18373 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
18375 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
18376 this.store.on('load', this.onTouchViewLoad, this);
18377 this.store.on('loadexception', this.onTouchViewLoadException, this);
18379 if(this.hiddenName){
18381 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
18383 this.hiddenField.dom.value =
18384 this.hiddenValue !== undefined ? this.hiddenValue :
18385 this.value !== undefined ? this.value : '';
18387 this.el.dom.removeAttribute('name');
18388 this.hiddenField.dom.setAttribute('name', this.hiddenName);
18392 this.choices = this.el.select('ul.roo-select2-choices', true).first();
18393 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
18396 if(this.removable && !this.multiple){
18397 var close = this.closeTriggerEl();
18399 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
18400 close.on('click', this.removeBtnClick, this, close);
18404 * fix the bug in Safari iOS8
18406 this.inputEl().on("focus", function(e){
18407 document.activeElement.blur();
18410 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
18417 renderTouchView : function()
18419 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
18420 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18422 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
18423 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18425 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
18426 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18427 this.touchViewBodyEl.setStyle('overflow', 'auto');
18429 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
18430 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18432 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
18433 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18437 showTouchView : function()
18443 this.touchViewHeaderEl.hide();
18445 if(this.modalTitle.length){
18446 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
18447 this.touchViewHeaderEl.show();
18450 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
18451 this.touchViewEl.show();
18453 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
18455 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
18456 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18458 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18460 if(this.modalTitle.length){
18461 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18464 this.touchViewBodyEl.setHeight(bodyHeight);
18468 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
18470 this.touchViewEl.addClass(['in','show']);
18473 if(this._touchViewMask){
18474 Roo.get(document.body).addClass("x-body-masked");
18475 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18476 this._touchViewMask.setStyle('z-index', 10000);
18477 this._touchViewMask.addClass('show');
18480 this.doTouchViewQuery();
18484 hideTouchView : function()
18486 this.touchViewEl.removeClass(['in','show']);
18490 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
18492 this.touchViewEl.setStyle('display', 'none');
18495 if(this._touchViewMask){
18496 this._touchViewMask.removeClass('show');
18497 Roo.get(document.body).removeClass("x-body-masked");
18501 setTouchViewValue : function()
18508 Roo.each(this.tickItems, function(o){
18513 this.hideTouchView();
18516 doTouchViewQuery : function()
18525 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
18529 if(!this.alwaysQuery || this.mode == 'local'){
18530 this.onTouchViewLoad();
18537 onTouchViewBeforeLoad : function(combo,opts)
18543 onTouchViewLoad : function()
18545 if(this.store.getCount() < 1){
18546 this.onTouchViewEmptyResults();
18550 this.clearTouchView();
18552 var rawValue = this.getRawValue();
18554 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
18556 this.tickItems = [];
18558 this.store.data.each(function(d, rowIndex){
18559 var row = this.touchViewListGroup.createChild(template);
18561 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
18562 row.addClass(d.data.cls);
18565 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
18568 html : d.data[this.displayField]
18571 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
18572 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
18575 row.removeClass('selected');
18576 if(!this.multiple && this.valueField &&
18577 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
18580 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18581 row.addClass('selected');
18584 if(this.multiple && this.valueField &&
18585 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
18589 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18590 this.tickItems.push(d.data);
18593 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
18597 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
18599 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18601 if(this.modalTitle.length){
18602 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18605 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
18607 if(this.mobile_restrict_height && listHeight < bodyHeight){
18608 this.touchViewBodyEl.setHeight(listHeight);
18613 if(firstChecked && listHeight > bodyHeight){
18614 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
18619 onTouchViewLoadException : function()
18621 this.hideTouchView();
18624 onTouchViewEmptyResults : function()
18626 this.clearTouchView();
18628 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
18630 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
18634 clearTouchView : function()
18636 this.touchViewListGroup.dom.innerHTML = '';
18639 onTouchViewClick : function(e, el, o)
18641 e.preventDefault();
18644 var rowIndex = o.rowIndex;
18646 var r = this.store.getAt(rowIndex);
18648 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
18650 if(!this.multiple){
18651 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
18652 c.dom.removeAttribute('checked');
18655 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18657 this.setFromData(r.data);
18659 var close = this.closeTriggerEl();
18665 this.hideTouchView();
18667 this.fireEvent('select', this, r, rowIndex);
18672 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
18673 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
18674 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
18678 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18679 this.addItem(r.data);
18680 this.tickItems.push(r.data);
18684 getAutoCreateNativeIOS : function()
18687 cls: 'form-group' //input-group,
18692 cls : 'roo-ios-select'
18696 combobox.name = this.name;
18699 if (this.disabled) {
18700 combobox.disabled = true;
18703 var settings = this;
18705 ['xs','sm','md','lg'].map(function(size){
18706 if (settings[size]) {
18707 cfg.cls += ' col-' + size + '-' + settings[size];
18717 initIOSView : function()
18719 this.store.on('load', this.onIOSViewLoad, this);
18724 onIOSViewLoad : function()
18726 if(this.store.getCount() < 1){
18730 this.clearIOSView();
18732 if(this.allowBlank) {
18734 var default_text = '-- SELECT --';
18736 if(this.placeholder.length){
18737 default_text = this.placeholder;
18740 if(this.emptyTitle.length){
18741 default_text += ' - ' + this.emptyTitle + ' -';
18744 var opt = this.inputEl().createChild({
18747 html : default_text
18751 o[this.valueField] = 0;
18752 o[this.displayField] = default_text;
18754 this.ios_options.push({
18761 this.store.data.each(function(d, rowIndex){
18765 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
18766 html = d.data[this.displayField];
18771 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
18772 value = d.data[this.valueField];
18781 if(this.value == d.data[this.valueField]){
18782 option['selected'] = true;
18785 var opt = this.inputEl().createChild(option);
18787 this.ios_options.push({
18794 this.inputEl().on('change', function(){
18795 this.fireEvent('select', this);
18800 clearIOSView: function()
18802 this.inputEl().dom.innerHTML = '';
18804 this.ios_options = [];
18807 setIOSValue: function(v)
18811 if(!this.ios_options){
18815 Roo.each(this.ios_options, function(opts){
18817 opts.el.dom.removeAttribute('selected');
18819 if(opts.data[this.valueField] != v){
18823 opts.el.dom.setAttribute('selected', true);
18829 * @cfg {Boolean} grow
18833 * @cfg {Number} growMin
18837 * @cfg {Number} growMax
18846 Roo.apply(Roo.bootstrap.ComboBox, {
18850 cls: 'modal-header',
18872 cls: 'list-group-item',
18876 cls: 'roo-combobox-list-group-item-value'
18880 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
18894 listItemCheckbox : {
18896 cls: 'list-group-item',
18900 cls: 'roo-combobox-list-group-item-value'
18904 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18920 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18925 cls: 'modal-footer',
18933 cls: 'col-xs-6 text-left',
18936 cls: 'btn btn-danger roo-touch-view-cancel',
18942 cls: 'col-xs-6 text-right',
18945 cls: 'btn btn-success roo-touch-view-ok',
18956 Roo.apply(Roo.bootstrap.ComboBox, {
18958 touchViewTemplate : {
18960 cls: 'modal fade roo-combobox-touch-view',
18964 cls: 'modal-dialog',
18965 style : 'position:fixed', // we have to fix position....
18969 cls: 'modal-content',
18971 Roo.bootstrap.ComboBox.header,
18972 Roo.bootstrap.ComboBox.body,
18973 Roo.bootstrap.ComboBox.footer
18982 * Ext JS Library 1.1.1
18983 * Copyright(c) 2006-2007, Ext JS, LLC.
18985 * Originally Released Under LGPL - original licence link has changed is not relivant.
18988 * <script type="text/javascript">
18993 * @extends Roo.util.Observable
18994 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
18995 * This class also supports single and multi selection modes. <br>
18996 * Create a data model bound view:
18998 var store = new Roo.data.Store(...);
19000 var view = new Roo.View({
19002 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
19004 singleSelect: true,
19005 selectedClass: "ydataview-selected",
19009 // listen for node click?
19010 view.on("click", function(vw, index, node, e){
19011 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19015 dataModel.load("foobar.xml");
19017 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19019 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19020 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19022 * Note: old style constructor is still suported (container, template, config)
19025 * Create a new View
19026 * @param {Object} config The config object
19029 Roo.View = function(config, depreciated_tpl, depreciated_config){
19031 this.parent = false;
19033 if (typeof(depreciated_tpl) == 'undefined') {
19034 // new way.. - universal constructor.
19035 Roo.apply(this, config);
19036 this.el = Roo.get(this.el);
19039 this.el = Roo.get(config);
19040 this.tpl = depreciated_tpl;
19041 Roo.apply(this, depreciated_config);
19043 this.wrapEl = this.el.wrap().wrap();
19044 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19047 if(typeof(this.tpl) == "string"){
19048 this.tpl = new Roo.Template(this.tpl);
19050 // support xtype ctors..
19051 this.tpl = new Roo.factory(this.tpl, Roo);
19055 this.tpl.compile();
19060 * @event beforeclick
19061 * Fires before a click is processed. Returns false to cancel the default action.
19062 * @param {Roo.View} this
19063 * @param {Number} index The index of the target node
19064 * @param {HTMLElement} node The target node
19065 * @param {Roo.EventObject} e The raw event object
19067 "beforeclick" : true,
19070 * Fires when a template node is clicked.
19071 * @param {Roo.View} this
19072 * @param {Number} index The index of the target node
19073 * @param {HTMLElement} node The target node
19074 * @param {Roo.EventObject} e The raw event object
19079 * Fires when a template node is double clicked.
19080 * @param {Roo.View} this
19081 * @param {Number} index The index of the target node
19082 * @param {HTMLElement} node The target node
19083 * @param {Roo.EventObject} e The raw event object
19087 * @event contextmenu
19088 * Fires when a template node is right clicked.
19089 * @param {Roo.View} this
19090 * @param {Number} index The index of the target node
19091 * @param {HTMLElement} node The target node
19092 * @param {Roo.EventObject} e The raw event object
19094 "contextmenu" : true,
19096 * @event selectionchange
19097 * Fires when the selected nodes change.
19098 * @param {Roo.View} this
19099 * @param {Array} selections Array of the selected nodes
19101 "selectionchange" : true,
19104 * @event beforeselect
19105 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
19106 * @param {Roo.View} this
19107 * @param {HTMLElement} node The node to be selected
19108 * @param {Array} selections Array of currently selected nodes
19110 "beforeselect" : true,
19112 * @event preparedata
19113 * Fires on every row to render, to allow you to change the data.
19114 * @param {Roo.View} this
19115 * @param {Object} data to be rendered (change this)
19117 "preparedata" : true
19125 "click": this.onClick,
19126 "dblclick": this.onDblClick,
19127 "contextmenu": this.onContextMenu,
19131 this.selections = [];
19133 this.cmp = new Roo.CompositeElementLite([]);
19135 this.store = Roo.factory(this.store, Roo.data);
19136 this.setStore(this.store, true);
19139 if ( this.footer && this.footer.xtype) {
19141 var fctr = this.wrapEl.appendChild(document.createElement("div"));
19143 this.footer.dataSource = this.store;
19144 this.footer.container = fctr;
19145 this.footer = Roo.factory(this.footer, Roo);
19146 fctr.insertFirst(this.el);
19148 // this is a bit insane - as the paging toolbar seems to detach the el..
19149 // dom.parentNode.parentNode.parentNode
19150 // they get detached?
19154 Roo.View.superclass.constructor.call(this);
19159 Roo.extend(Roo.View, Roo.util.Observable, {
19162 * @cfg {Roo.data.Store} store Data store to load data from.
19167 * @cfg {String|Roo.Element} el The container element.
19172 * @cfg {String|Roo.Template} tpl The template used by this View
19176 * @cfg {String} dataName the named area of the template to use as the data area
19177 * Works with domtemplates roo-name="name"
19181 * @cfg {String} selectedClass The css class to add to selected nodes
19183 selectedClass : "x-view-selected",
19185 * @cfg {String} emptyText The empty text to show when nothing is loaded.
19190 * @cfg {String} text to display on mask (default Loading)
19194 * @cfg {Boolean} multiSelect Allow multiple selection
19196 multiSelect : false,
19198 * @cfg {Boolean} singleSelect Allow single selection
19200 singleSelect: false,
19203 * @cfg {Boolean} toggleSelect - selecting
19205 toggleSelect : false,
19208 * @cfg {Boolean} tickable - selecting
19213 * Returns the element this view is bound to.
19214 * @return {Roo.Element}
19216 getEl : function(){
19217 return this.wrapEl;
19223 * Refreshes the view. - called by datachanged on the store. - do not call directly.
19225 refresh : function(){
19226 //Roo.log('refresh');
19229 // if we are using something like 'domtemplate', then
19230 // the what gets used is:
19231 // t.applySubtemplate(NAME, data, wrapping data..)
19232 // the outer template then get' applied with
19233 // the store 'extra data'
19234 // and the body get's added to the
19235 // roo-name="data" node?
19236 // <span class='roo-tpl-{name}'></span> ?????
19240 this.clearSelections();
19241 this.el.update("");
19243 var records = this.store.getRange();
19244 if(records.length < 1) {
19246 // is this valid?? = should it render a template??
19248 this.el.update(this.emptyText);
19252 if (this.dataName) {
19253 this.el.update(t.apply(this.store.meta)); //????
19254 el = this.el.child('.roo-tpl-' + this.dataName);
19257 for(var i = 0, len = records.length; i < len; i++){
19258 var data = this.prepareData(records[i].data, i, records[i]);
19259 this.fireEvent("preparedata", this, data, i, records[i]);
19261 var d = Roo.apply({}, data);
19264 Roo.apply(d, {'roo-id' : Roo.id()});
19268 Roo.each(this.parent.item, function(item){
19269 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
19272 Roo.apply(d, {'roo-data-checked' : 'checked'});
19276 html[html.length] = Roo.util.Format.trim(
19278 t.applySubtemplate(this.dataName, d, this.store.meta) :
19285 el.update(html.join(""));
19286 this.nodes = el.dom.childNodes;
19287 this.updateIndexes(0);
19292 * Function to override to reformat the data that is sent to
19293 * the template for each node.
19294 * DEPRICATED - use the preparedata event handler.
19295 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
19296 * a JSON object for an UpdateManager bound view).
19298 prepareData : function(data, index, record)
19300 this.fireEvent("preparedata", this, data, index, record);
19304 onUpdate : function(ds, record){
19305 // Roo.log('on update');
19306 this.clearSelections();
19307 var index = this.store.indexOf(record);
19308 var n = this.nodes[index];
19309 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
19310 n.parentNode.removeChild(n);
19311 this.updateIndexes(index, index);
19317 onAdd : function(ds, records, index)
19319 //Roo.log(['on Add', ds, records, index] );
19320 this.clearSelections();
19321 if(this.nodes.length == 0){
19325 var n = this.nodes[index];
19326 for(var i = 0, len = records.length; i < len; i++){
19327 var d = this.prepareData(records[i].data, i, records[i]);
19329 this.tpl.insertBefore(n, d);
19332 this.tpl.append(this.el, d);
19335 this.updateIndexes(index);
19338 onRemove : function(ds, record, index){
19339 // Roo.log('onRemove');
19340 this.clearSelections();
19341 var el = this.dataName ?
19342 this.el.child('.roo-tpl-' + this.dataName) :
19345 el.dom.removeChild(this.nodes[index]);
19346 this.updateIndexes(index);
19350 * Refresh an individual node.
19351 * @param {Number} index
19353 refreshNode : function(index){
19354 this.onUpdate(this.store, this.store.getAt(index));
19357 updateIndexes : function(startIndex, endIndex){
19358 var ns = this.nodes;
19359 startIndex = startIndex || 0;
19360 endIndex = endIndex || ns.length - 1;
19361 for(var i = startIndex; i <= endIndex; i++){
19362 ns[i].nodeIndex = i;
19367 * Changes the data store this view uses and refresh the view.
19368 * @param {Store} store
19370 setStore : function(store, initial){
19371 if(!initial && this.store){
19372 this.store.un("datachanged", this.refresh);
19373 this.store.un("add", this.onAdd);
19374 this.store.un("remove", this.onRemove);
19375 this.store.un("update", this.onUpdate);
19376 this.store.un("clear", this.refresh);
19377 this.store.un("beforeload", this.onBeforeLoad);
19378 this.store.un("load", this.onLoad);
19379 this.store.un("loadexception", this.onLoad);
19383 store.on("datachanged", this.refresh, this);
19384 store.on("add", this.onAdd, this);
19385 store.on("remove", this.onRemove, this);
19386 store.on("update", this.onUpdate, this);
19387 store.on("clear", this.refresh, this);
19388 store.on("beforeload", this.onBeforeLoad, this);
19389 store.on("load", this.onLoad, this);
19390 store.on("loadexception", this.onLoad, this);
19398 * onbeforeLoad - masks the loading area.
19401 onBeforeLoad : function(store,opts)
19403 //Roo.log('onBeforeLoad');
19405 this.el.update("");
19407 this.el.mask(this.mask ? this.mask : "Loading" );
19409 onLoad : function ()
19416 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
19417 * @param {HTMLElement} node
19418 * @return {HTMLElement} The template node
19420 findItemFromChild : function(node){
19421 var el = this.dataName ?
19422 this.el.child('.roo-tpl-' + this.dataName,true) :
19425 if(!node || node.parentNode == el){
19428 var p = node.parentNode;
19429 while(p && p != el){
19430 if(p.parentNode == el){
19439 onClick : function(e){
19440 var item = this.findItemFromChild(e.getTarget());
19442 var index = this.indexOf(item);
19443 if(this.onItemClick(item, index, e) !== false){
19444 this.fireEvent("click", this, index, item, e);
19447 this.clearSelections();
19452 onContextMenu : function(e){
19453 var item = this.findItemFromChild(e.getTarget());
19455 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
19460 onDblClick : function(e){
19461 var item = this.findItemFromChild(e.getTarget());
19463 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
19467 onItemClick : function(item, index, e)
19469 if(this.fireEvent("beforeclick", this, index, item, e) === false){
19472 if (this.toggleSelect) {
19473 var m = this.isSelected(item) ? 'unselect' : 'select';
19476 _t[m](item, true, false);
19479 if(this.multiSelect || this.singleSelect){
19480 if(this.multiSelect && e.shiftKey && this.lastSelection){
19481 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
19483 this.select(item, this.multiSelect && e.ctrlKey);
19484 this.lastSelection = item;
19487 if(!this.tickable){
19488 e.preventDefault();
19496 * Get the number of selected nodes.
19499 getSelectionCount : function(){
19500 return this.selections.length;
19504 * Get the currently selected nodes.
19505 * @return {Array} An array of HTMLElements
19507 getSelectedNodes : function(){
19508 return this.selections;
19512 * Get the indexes of the selected nodes.
19515 getSelectedIndexes : function(){
19516 var indexes = [], s = this.selections;
19517 for(var i = 0, len = s.length; i < len; i++){
19518 indexes.push(s[i].nodeIndex);
19524 * Clear all selections
19525 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
19527 clearSelections : function(suppressEvent){
19528 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
19529 this.cmp.elements = this.selections;
19530 this.cmp.removeClass(this.selectedClass);
19531 this.selections = [];
19532 if(!suppressEvent){
19533 this.fireEvent("selectionchange", this, this.selections);
19539 * Returns true if the passed node is selected
19540 * @param {HTMLElement/Number} node The node or node index
19541 * @return {Boolean}
19543 isSelected : function(node){
19544 var s = this.selections;
19548 node = this.getNode(node);
19549 return s.indexOf(node) !== -1;
19554 * @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
19555 * @param {Boolean} keepExisting (optional) true to keep existing selections
19556 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
19558 select : function(nodeInfo, keepExisting, suppressEvent){
19559 if(nodeInfo instanceof Array){
19561 this.clearSelections(true);
19563 for(var i = 0, len = nodeInfo.length; i < len; i++){
19564 this.select(nodeInfo[i], true, true);
19568 var node = this.getNode(nodeInfo);
19569 if(!node || this.isSelected(node)){
19570 return; // already selected.
19573 this.clearSelections(true);
19576 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
19577 Roo.fly(node).addClass(this.selectedClass);
19578 this.selections.push(node);
19579 if(!suppressEvent){
19580 this.fireEvent("selectionchange", this, this.selections);
19588 * @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
19589 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
19590 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
19592 unselect : function(nodeInfo, keepExisting, suppressEvent)
19594 if(nodeInfo instanceof Array){
19595 Roo.each(this.selections, function(s) {
19596 this.unselect(s, nodeInfo);
19600 var node = this.getNode(nodeInfo);
19601 if(!node || !this.isSelected(node)){
19602 //Roo.log("not selected");
19603 return; // not selected.
19607 Roo.each(this.selections, function(s) {
19609 Roo.fly(node).removeClass(this.selectedClass);
19616 this.selections= ns;
19617 this.fireEvent("selectionchange", this, this.selections);
19621 * Gets a template node.
19622 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19623 * @return {HTMLElement} The node or null if it wasn't found
19625 getNode : function(nodeInfo){
19626 if(typeof nodeInfo == "string"){
19627 return document.getElementById(nodeInfo);
19628 }else if(typeof nodeInfo == "number"){
19629 return this.nodes[nodeInfo];
19635 * Gets a range template nodes.
19636 * @param {Number} startIndex
19637 * @param {Number} endIndex
19638 * @return {Array} An array of nodes
19640 getNodes : function(start, end){
19641 var ns = this.nodes;
19642 start = start || 0;
19643 end = typeof end == "undefined" ? ns.length - 1 : end;
19646 for(var i = start; i <= end; i++){
19650 for(var i = start; i >= end; i--){
19658 * Finds the index of the passed node
19659 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19660 * @return {Number} The index of the node or -1
19662 indexOf : function(node){
19663 node = this.getNode(node);
19664 if(typeof node.nodeIndex == "number"){
19665 return node.nodeIndex;
19667 var ns = this.nodes;
19668 for(var i = 0, len = ns.length; i < len; i++){
19679 * based on jquery fullcalendar
19683 Roo.bootstrap = Roo.bootstrap || {};
19685 * @class Roo.bootstrap.Calendar
19686 * @extends Roo.bootstrap.Component
19687 * Bootstrap Calendar class
19688 * @cfg {Boolean} loadMask (true|false) default false
19689 * @cfg {Object} header generate the user specific header of the calendar, default false
19692 * Create a new Container
19693 * @param {Object} config The config object
19698 Roo.bootstrap.Calendar = function(config){
19699 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
19703 * Fires when a date is selected
19704 * @param {DatePicker} this
19705 * @param {Date} date The selected date
19709 * @event monthchange
19710 * Fires when the displayed month changes
19711 * @param {DatePicker} this
19712 * @param {Date} date The selected month
19714 'monthchange': true,
19716 * @event evententer
19717 * Fires when mouse over an event
19718 * @param {Calendar} this
19719 * @param {event} Event
19721 'evententer': true,
19723 * @event eventleave
19724 * Fires when the mouse leaves an
19725 * @param {Calendar} this
19728 'eventleave': true,
19730 * @event eventclick
19731 * Fires when the mouse click an
19732 * @param {Calendar} this
19741 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
19744 * @cfg {Number} startDay
19745 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
19753 getAutoCreate : function(){
19756 var fc_button = function(name, corner, style, content ) {
19757 return Roo.apply({},{
19759 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
19761 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
19764 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
19775 style : 'width:100%',
19782 cls : 'fc-header-left',
19784 fc_button('prev', 'left', 'arrow', '‹' ),
19785 fc_button('next', 'right', 'arrow', '›' ),
19786 { tag: 'span', cls: 'fc-header-space' },
19787 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
19795 cls : 'fc-header-center',
19799 cls: 'fc-header-title',
19802 html : 'month / year'
19810 cls : 'fc-header-right',
19812 /* fc_button('month', 'left', '', 'month' ),
19813 fc_button('week', '', '', 'week' ),
19814 fc_button('day', 'right', '', 'day' )
19826 header = this.header;
19829 var cal_heads = function() {
19831 // fixme - handle this.
19833 for (var i =0; i < Date.dayNames.length; i++) {
19834 var d = Date.dayNames[i];
19837 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
19838 html : d.substring(0,3)
19842 ret[0].cls += ' fc-first';
19843 ret[6].cls += ' fc-last';
19846 var cal_cell = function(n) {
19849 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
19854 cls: 'fc-day-number',
19858 cls: 'fc-day-content',
19862 style: 'position: relative;' // height: 17px;
19874 var cal_rows = function() {
19877 for (var r = 0; r < 6; r++) {
19884 for (var i =0; i < Date.dayNames.length; i++) {
19885 var d = Date.dayNames[i];
19886 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
19889 row.cn[0].cls+=' fc-first';
19890 row.cn[0].cn[0].style = 'min-height:90px';
19891 row.cn[6].cls+=' fc-last';
19895 ret[0].cls += ' fc-first';
19896 ret[4].cls += ' fc-prev-last';
19897 ret[5].cls += ' fc-last';
19904 cls: 'fc-border-separate',
19905 style : 'width:100%',
19913 cls : 'fc-first fc-last',
19931 cls : 'fc-content',
19932 style : "position: relative;",
19935 cls : 'fc-view fc-view-month fc-grid',
19936 style : 'position: relative',
19937 unselectable : 'on',
19940 cls : 'fc-event-container',
19941 style : 'position:absolute;z-index:8;top:0;left:0;'
19959 initEvents : function()
19962 throw "can not find store for calendar";
19968 style: "text-align:center",
19972 style: "background-color:white;width:50%;margin:250 auto",
19976 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
19987 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19989 var size = this.el.select('.fc-content', true).first().getSize();
19990 this.maskEl.setSize(size.width, size.height);
19991 this.maskEl.enableDisplayMode("block");
19992 if(!this.loadMask){
19993 this.maskEl.hide();
19996 this.store = Roo.factory(this.store, Roo.data);
19997 this.store.on('load', this.onLoad, this);
19998 this.store.on('beforeload', this.onBeforeLoad, this);
20002 this.cells = this.el.select('.fc-day',true);
20003 //Roo.log(this.cells);
20004 this.textNodes = this.el.query('.fc-day-number');
20005 this.cells.addClassOnOver('fc-state-hover');
20007 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20008 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20009 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20010 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20012 this.on('monthchange', this.onMonthChange, this);
20014 this.update(new Date().clearTime());
20017 resize : function() {
20018 var sz = this.el.getSize();
20020 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20021 this.el.select('.fc-day-content div',true).setHeight(34);
20026 showPrevMonth : function(e){
20027 this.update(this.activeDate.add("mo", -1));
20029 showToday : function(e){
20030 this.update(new Date().clearTime());
20033 showNextMonth : function(e){
20034 this.update(this.activeDate.add("mo", 1));
20038 showPrevYear : function(){
20039 this.update(this.activeDate.add("y", -1));
20043 showNextYear : function(){
20044 this.update(this.activeDate.add("y", 1));
20049 update : function(date)
20051 var vd = this.activeDate;
20052 this.activeDate = date;
20053 // if(vd && this.el){
20054 // var t = date.getTime();
20055 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20056 // Roo.log('using add remove');
20058 // this.fireEvent('monthchange', this, date);
20060 // this.cells.removeClass("fc-state-highlight");
20061 // this.cells.each(function(c){
20062 // if(c.dateValue == t){
20063 // c.addClass("fc-state-highlight");
20064 // setTimeout(function(){
20065 // try{c.dom.firstChild.focus();}catch(e){}
20075 var days = date.getDaysInMonth();
20077 var firstOfMonth = date.getFirstDateOfMonth();
20078 var startingPos = firstOfMonth.getDay()-this.startDay;
20080 if(startingPos < this.startDay){
20084 var pm = date.add(Date.MONTH, -1);
20085 var prevStart = pm.getDaysInMonth()-startingPos;
20087 this.cells = this.el.select('.fc-day',true);
20088 this.textNodes = this.el.query('.fc-day-number');
20089 this.cells.addClassOnOver('fc-state-hover');
20091 var cells = this.cells.elements;
20092 var textEls = this.textNodes;
20094 Roo.each(cells, function(cell){
20095 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
20098 days += startingPos;
20100 // convert everything to numbers so it's fast
20101 var day = 86400000;
20102 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
20105 //Roo.log(prevStart);
20107 var today = new Date().clearTime().getTime();
20108 var sel = date.clearTime().getTime();
20109 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
20110 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
20111 var ddMatch = this.disabledDatesRE;
20112 var ddText = this.disabledDatesText;
20113 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
20114 var ddaysText = this.disabledDaysText;
20115 var format = this.format;
20117 var setCellClass = function(cal, cell){
20121 //Roo.log('set Cell Class');
20123 var t = d.getTime();
20127 cell.dateValue = t;
20129 cell.className += " fc-today";
20130 cell.className += " fc-state-highlight";
20131 cell.title = cal.todayText;
20134 // disable highlight in other month..
20135 //cell.className += " fc-state-highlight";
20140 cell.className = " fc-state-disabled";
20141 cell.title = cal.minText;
20145 cell.className = " fc-state-disabled";
20146 cell.title = cal.maxText;
20150 if(ddays.indexOf(d.getDay()) != -1){
20151 cell.title = ddaysText;
20152 cell.className = " fc-state-disabled";
20155 if(ddMatch && format){
20156 var fvalue = d.dateFormat(format);
20157 if(ddMatch.test(fvalue)){
20158 cell.title = ddText.replace("%0", fvalue);
20159 cell.className = " fc-state-disabled";
20163 if (!cell.initialClassName) {
20164 cell.initialClassName = cell.dom.className;
20167 cell.dom.className = cell.initialClassName + ' ' + cell.className;
20172 for(; i < startingPos; i++) {
20173 textEls[i].innerHTML = (++prevStart);
20174 d.setDate(d.getDate()+1);
20176 cells[i].className = "fc-past fc-other-month";
20177 setCellClass(this, cells[i]);
20182 for(; i < days; i++){
20183 intDay = i - startingPos + 1;
20184 textEls[i].innerHTML = (intDay);
20185 d.setDate(d.getDate()+1);
20187 cells[i].className = ''; // "x-date-active";
20188 setCellClass(this, cells[i]);
20192 for(; i < 42; i++) {
20193 textEls[i].innerHTML = (++extraDays);
20194 d.setDate(d.getDate()+1);
20196 cells[i].className = "fc-future fc-other-month";
20197 setCellClass(this, cells[i]);
20200 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
20202 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
20204 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
20205 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
20207 if(totalRows != 6){
20208 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
20209 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
20212 this.fireEvent('monthchange', this, date);
20216 if(!this.internalRender){
20217 var main = this.el.dom.firstChild;
20218 var w = main.offsetWidth;
20219 this.el.setWidth(w + this.el.getBorderWidth("lr"));
20220 Roo.fly(main).setWidth(w);
20221 this.internalRender = true;
20222 // opera does not respect the auto grow header center column
20223 // then, after it gets a width opera refuses to recalculate
20224 // without a second pass
20225 if(Roo.isOpera && !this.secondPass){
20226 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
20227 this.secondPass = true;
20228 this.update.defer(10, this, [date]);
20235 findCell : function(dt) {
20236 dt = dt.clearTime().getTime();
20238 this.cells.each(function(c){
20239 //Roo.log("check " +c.dateValue + '?=' + dt);
20240 if(c.dateValue == dt){
20250 findCells : function(ev) {
20251 var s = ev.start.clone().clearTime().getTime();
20253 var e= ev.end.clone().clearTime().getTime();
20256 this.cells.each(function(c){
20257 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
20259 if(c.dateValue > e){
20262 if(c.dateValue < s){
20271 // findBestRow: function(cells)
20275 // for (var i =0 ; i < cells.length;i++) {
20276 // ret = Math.max(cells[i].rows || 0,ret);
20283 addItem : function(ev)
20285 // look for vertical location slot in
20286 var cells = this.findCells(ev);
20288 // ev.row = this.findBestRow(cells);
20290 // work out the location.
20294 for(var i =0; i < cells.length; i++) {
20296 cells[i].row = cells[0].row;
20299 cells[i].row = cells[i].row + 1;
20309 if (crow.start.getY() == cells[i].getY()) {
20311 crow.end = cells[i];
20328 cells[0].events.push(ev);
20330 this.calevents.push(ev);
20333 clearEvents: function() {
20335 if(!this.calevents){
20339 Roo.each(this.cells.elements, function(c){
20345 Roo.each(this.calevents, function(e) {
20346 Roo.each(e.els, function(el) {
20347 el.un('mouseenter' ,this.onEventEnter, this);
20348 el.un('mouseleave' ,this.onEventLeave, this);
20353 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
20359 renderEvents: function()
20363 this.cells.each(function(c) {
20372 if(c.row != c.events.length){
20373 r = 4 - (4 - (c.row - c.events.length));
20376 c.events = ev.slice(0, r);
20377 c.more = ev.slice(r);
20379 if(c.more.length && c.more.length == 1){
20380 c.events.push(c.more.pop());
20383 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
20387 this.cells.each(function(c) {
20389 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
20392 for (var e = 0; e < c.events.length; e++){
20393 var ev = c.events[e];
20394 var rows = ev.rows;
20396 for(var i = 0; i < rows.length; i++) {
20398 // how many rows should it span..
20401 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
20402 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
20404 unselectable : "on",
20407 cls: 'fc-event-inner',
20411 // cls: 'fc-event-time',
20412 // html : cells.length > 1 ? '' : ev.time
20416 cls: 'fc-event-title',
20417 html : String.format('{0}', ev.title)
20424 cls: 'ui-resizable-handle ui-resizable-e',
20425 html : '  '
20432 cfg.cls += ' fc-event-start';
20434 if ((i+1) == rows.length) {
20435 cfg.cls += ' fc-event-end';
20438 var ctr = _this.el.select('.fc-event-container',true).first();
20439 var cg = ctr.createChild(cfg);
20441 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
20442 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
20444 var r = (c.more.length) ? 1 : 0;
20445 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
20446 cg.setWidth(ebox.right - sbox.x -2);
20448 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
20449 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
20450 cg.on('click', _this.onEventClick, _this, ev);
20461 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
20462 style : 'position: absolute',
20463 unselectable : "on",
20466 cls: 'fc-event-inner',
20470 cls: 'fc-event-title',
20478 cls: 'ui-resizable-handle ui-resizable-e',
20479 html : '  '
20485 var ctr = _this.el.select('.fc-event-container',true).first();
20486 var cg = ctr.createChild(cfg);
20488 var sbox = c.select('.fc-day-content',true).first().getBox();
20489 var ebox = c.select('.fc-day-content',true).first().getBox();
20491 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
20492 cg.setWidth(ebox.right - sbox.x -2);
20494 cg.on('click', _this.onMoreEventClick, _this, c.more);
20504 onEventEnter: function (e, el,event,d) {
20505 this.fireEvent('evententer', this, el, event);
20508 onEventLeave: function (e, el,event,d) {
20509 this.fireEvent('eventleave', this, el, event);
20512 onEventClick: function (e, el,event,d) {
20513 this.fireEvent('eventclick', this, el, event);
20516 onMonthChange: function () {
20520 onMoreEventClick: function(e, el, more)
20524 this.calpopover.placement = 'right';
20525 this.calpopover.setTitle('More');
20527 this.calpopover.setContent('');
20529 var ctr = this.calpopover.el.select('.popover-content', true).first();
20531 Roo.each(more, function(m){
20533 cls : 'fc-event-hori fc-event-draggable',
20536 var cg = ctr.createChild(cfg);
20538 cg.on('click', _this.onEventClick, _this, m);
20541 this.calpopover.show(el);
20546 onLoad: function ()
20548 this.calevents = [];
20551 if(this.store.getCount() > 0){
20552 this.store.data.each(function(d){
20555 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
20556 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
20557 time : d.data.start_time,
20558 title : d.data.title,
20559 description : d.data.description,
20560 venue : d.data.venue
20565 this.renderEvents();
20567 if(this.calevents.length && this.loadMask){
20568 this.maskEl.hide();
20572 onBeforeLoad: function()
20574 this.clearEvents();
20576 this.maskEl.show();
20590 * @class Roo.bootstrap.Popover
20591 * @extends Roo.bootstrap.Component
20592 * Bootstrap Popover class
20593 * @cfg {String} html contents of the popover (or false to use children..)
20594 * @cfg {String} title of popover (or false to hide)
20595 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
20596 * @cfg {String} trigger click || hover (or false to trigger manually)
20597 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
20598 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
20599 * - if false and it has a 'parent' then it will be automatically added to that element
20600 * - if string - Roo.get will be called
20601 * @cfg {Number} delay - delay before showing
20604 * Create a new Popover
20605 * @param {Object} config The config object
20608 Roo.bootstrap.Popover = function(config){
20609 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
20615 * After the popover show
20617 * @param {Roo.bootstrap.Popover} this
20622 * After the popover hide
20624 * @param {Roo.bootstrap.Popover} this
20630 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
20635 placement : 'right',
20636 trigger : 'hover', // hover
20642 can_build_overlaid : false,
20644 maskEl : false, // the mask element
20647 alignEl : false, // when show is called with an element - this get's stored.
20649 getChildContainer : function()
20651 return this.contentEl;
20654 getPopoverHeader : function()
20656 this.title = true; // flag not to hide it..
20657 this.headerEl.addClass('p-0');
20658 return this.headerEl
20662 getAutoCreate : function(){
20665 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
20666 style: 'display:block',
20672 cls : 'popover-inner ',
20676 cls: 'popover-title popover-header',
20677 html : this.title === false ? '' : this.title
20680 cls : 'popover-content popover-body ' + (this.cls || ''),
20681 html : this.html || ''
20692 * @param {string} the title
20694 setTitle: function(str)
20698 this.headerEl.dom.innerHTML = str;
20703 * @param {string} the body content
20705 setContent: function(str)
20708 if (this.contentEl) {
20709 this.contentEl.dom.innerHTML = str;
20713 // as it get's added to the bottom of the page.
20714 onRender : function(ct, position)
20716 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
20721 var cfg = Roo.apply({}, this.getAutoCreate());
20725 cfg.cls += ' ' + this.cls;
20728 cfg.style = this.style;
20730 //Roo.log("adding to ");
20731 this.el = Roo.get(document.body).createChild(cfg, position);
20732 // Roo.log(this.el);
20735 this.contentEl = this.el.select('.popover-content',true).first();
20736 this.headerEl = this.el.select('.popover-title',true).first();
20739 if(typeof(this.items) != 'undefined'){
20740 var items = this.items;
20743 for(var i =0;i < items.length;i++) {
20744 nitems.push(this.addxtype(Roo.apply({}, items[i])));
20748 this.items = nitems;
20750 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
20751 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
20758 resizeMask : function()
20760 this.maskEl.setSize(
20761 Roo.lib.Dom.getViewWidth(true),
20762 Roo.lib.Dom.getViewHeight(true)
20766 initEvents : function()
20770 Roo.bootstrap.Popover.register(this);
20773 this.arrowEl = this.el.select('.arrow',true).first();
20774 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
20775 this.el.enableDisplayMode('block');
20779 if (this.over === false && !this.parent()) {
20782 if (this.triggers === false) {
20787 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
20788 var triggers = this.trigger ? this.trigger.split(' ') : [];
20789 Roo.each(triggers, function(trigger) {
20791 if (trigger == 'click') {
20792 on_el.on('click', this.toggle, this);
20793 } else if (trigger != 'manual') {
20794 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
20795 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
20797 on_el.on(eventIn ,this.enter, this);
20798 on_el.on(eventOut, this.leave, this);
20808 toggle : function () {
20809 this.hoverState == 'in' ? this.leave() : this.enter();
20812 enter : function () {
20814 clearTimeout(this.timeout);
20816 this.hoverState = 'in';
20818 if (!this.delay || !this.delay.show) {
20823 this.timeout = setTimeout(function () {
20824 if (_t.hoverState == 'in') {
20827 }, this.delay.show)
20830 leave : function() {
20831 clearTimeout(this.timeout);
20833 this.hoverState = 'out';
20835 if (!this.delay || !this.delay.hide) {
20840 this.timeout = setTimeout(function () {
20841 if (_t.hoverState == 'out') {
20844 }, this.delay.hide)
20848 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
20849 * @param {string} (left|right|top|bottom) position
20851 show : function (on_el, placement)
20853 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
20854 on_el = on_el || false; // default to false
20857 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
20858 on_el = this.parent().el;
20859 } else if (this.over) {
20860 on_el = Roo.get(this.over);
20865 this.alignEl = Roo.get( on_el );
20868 this.render(document.body);
20874 if (this.title === false) {
20875 this.headerEl.hide();
20880 this.el.dom.style.display = 'block';
20883 if (this.alignEl) {
20884 this.updatePosition(this.placement, true);
20887 // this is usually just done by the builder = to show the popoup in the middle of the scren.
20888 var es = this.el.getSize();
20889 var x = Roo.lib.Dom.getViewWidth()/2;
20890 var y = Roo.lib.Dom.getViewHeight()/2;
20891 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
20896 //var arrow = this.el.select('.arrow',true).first();
20897 //arrow.set(align[2],
20899 this.el.addClass('in');
20903 this.hoverState = 'in';
20906 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
20907 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20908 this.maskEl.dom.style.display = 'block';
20909 this.maskEl.addClass('show');
20911 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20913 this.fireEvent('show', this);
20917 * fire this manually after loading a grid in the table for example
20918 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20919 * @param {Boolean} try and move it if we cant get right position.
20921 updatePosition : function(placement, try_move)
20923 // allow for calling with no parameters
20924 placement = placement ? placement : this.placement;
20925 try_move = typeof(try_move) == 'undefined' ? true : try_move;
20927 this.el.removeClass([
20928 'fade','top','bottom', 'left', 'right','in',
20929 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20931 this.el.addClass(placement + ' bs-popover-' + placement);
20933 if (!this.alignEl ) {
20937 switch (placement) {
20939 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20940 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20941 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20942 //normal display... or moved up/down.
20943 this.el.setXY(offset);
20944 var xy = this.alignEl.getAnchorXY('tr', false);
20946 this.arrowEl.setXY(xy);
20949 // continue through...
20950 return this.updatePosition('left', false);
20954 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20955 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20956 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20957 //normal display... or moved up/down.
20958 this.el.setXY(offset);
20959 var xy = this.alignEl.getAnchorXY('tl', false);
20960 xy[0]-=10;xy[1]+=5; // << fix me
20961 this.arrowEl.setXY(xy);
20965 return this.updatePosition('right', false);
20968 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20969 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20970 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20971 //normal display... or moved up/down.
20972 this.el.setXY(offset);
20973 var xy = this.alignEl.getAnchorXY('t', false);
20974 xy[1]-=10; // << fix me
20975 this.arrowEl.setXY(xy);
20979 return this.updatePosition('bottom', false);
20982 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20983 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20984 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20985 //normal display... or moved up/down.
20986 this.el.setXY(offset);
20987 var xy = this.alignEl.getAnchorXY('b', false);
20988 xy[1]+=2; // << fix me
20989 this.arrowEl.setXY(xy);
20993 return this.updatePosition('top', false);
21004 this.el.setXY([0,0]);
21005 this.el.removeClass('in');
21007 this.hoverState = null;
21008 this.maskEl.hide(); // always..
21009 this.fireEvent('hide', this);
21015 Roo.apply(Roo.bootstrap.Popover, {
21018 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21019 'right' : ['l-br', [10,0], 'right bs-popover-right'],
21020 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21021 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21026 clickHander : false,
21030 onMouseDown : function(e)
21032 if (this.popups.length && !e.getTarget(".roo-popover")) {
21033 /// what is nothing is showing..
21042 register : function(popup)
21044 if (!Roo.bootstrap.Popover.clickHandler) {
21045 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21047 // hide other popups.
21048 popup.on('show', Roo.bootstrap.Popover.onShow, popup);
21049 popup.on('hide', Roo.bootstrap.Popover.onHide, popup);
21050 this.hideAll(); //<< why?
21051 //this.popups.push(popup);
21053 hideAll : function()
21055 this.popups.forEach(function(p) {
21059 onShow : function() {
21060 Roo.bootstrap.Popover.popups.push(this);
21062 onHide : function() {
21063 Roo.bootstrap.Popover.popups.remove(this);
21069 * Card header - holder for the card header elements.
21074 * @class Roo.bootstrap.PopoverNav
21075 * @extends Roo.bootstrap.NavGroup
21076 * Bootstrap Popover header navigation class
21078 * Create a new Popover Header Navigation
21079 * @param {Object} config The config object
21082 Roo.bootstrap.PopoverNav = function(config){
21083 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
21086 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
21089 container_method : 'getPopoverHeader'
21107 * @class Roo.bootstrap.Progress
21108 * @extends Roo.bootstrap.Component
21109 * Bootstrap Progress class
21110 * @cfg {Boolean} striped striped of the progress bar
21111 * @cfg {Boolean} active animated of the progress bar
21115 * Create a new Progress
21116 * @param {Object} config The config object
21119 Roo.bootstrap.Progress = function(config){
21120 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
21123 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
21128 getAutoCreate : function(){
21136 cfg.cls += ' progress-striped';
21140 cfg.cls += ' active';
21159 * @class Roo.bootstrap.ProgressBar
21160 * @extends Roo.bootstrap.Component
21161 * Bootstrap ProgressBar class
21162 * @cfg {Number} aria_valuenow aria-value now
21163 * @cfg {Number} aria_valuemin aria-value min
21164 * @cfg {Number} aria_valuemax aria-value max
21165 * @cfg {String} label label for the progress bar
21166 * @cfg {String} panel (success | info | warning | danger )
21167 * @cfg {String} role role of the progress bar
21168 * @cfg {String} sr_only text
21172 * Create a new ProgressBar
21173 * @param {Object} config The config object
21176 Roo.bootstrap.ProgressBar = function(config){
21177 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
21180 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
21184 aria_valuemax : 100,
21190 getAutoCreate : function()
21195 cls: 'progress-bar',
21196 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
21208 cfg.role = this.role;
21211 if(this.aria_valuenow){
21212 cfg['aria-valuenow'] = this.aria_valuenow;
21215 if(this.aria_valuemin){
21216 cfg['aria-valuemin'] = this.aria_valuemin;
21219 if(this.aria_valuemax){
21220 cfg['aria-valuemax'] = this.aria_valuemax;
21223 if(this.label && !this.sr_only){
21224 cfg.html = this.label;
21228 cfg.cls += ' progress-bar-' + this.panel;
21234 update : function(aria_valuenow)
21236 this.aria_valuenow = aria_valuenow;
21238 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
21253 * @class Roo.bootstrap.TabGroup
21254 * @extends Roo.bootstrap.Column
21255 * Bootstrap Column class
21256 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
21257 * @cfg {Boolean} carousel true to make the group behave like a carousel
21258 * @cfg {Boolean} bullets show bullets for the panels
21259 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
21260 * @cfg {Number} timer auto slide timer .. default 0 millisecond
21261 * @cfg {Boolean} showarrow (true|false) show arrow default true
21264 * Create a new TabGroup
21265 * @param {Object} config The config object
21268 Roo.bootstrap.TabGroup = function(config){
21269 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
21271 this.navId = Roo.id();
21274 Roo.bootstrap.TabGroup.register(this);
21278 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
21281 transition : false,
21286 slideOnTouch : false,
21289 getAutoCreate : function()
21291 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
21293 cfg.cls += ' tab-content';
21295 if (this.carousel) {
21296 cfg.cls += ' carousel slide';
21299 cls : 'carousel-inner',
21303 if(this.bullets && !Roo.isTouch){
21306 cls : 'carousel-bullets',
21310 if(this.bullets_cls){
21311 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
21318 cfg.cn[0].cn.push(bullets);
21321 if(this.showarrow){
21322 cfg.cn[0].cn.push({
21324 class : 'carousel-arrow',
21328 class : 'carousel-prev',
21332 class : 'fa fa-chevron-left'
21338 class : 'carousel-next',
21342 class : 'fa fa-chevron-right'
21355 initEvents: function()
21357 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
21358 // this.el.on("touchstart", this.onTouchStart, this);
21361 if(this.autoslide){
21364 this.slideFn = window.setInterval(function() {
21365 _this.showPanelNext();
21369 if(this.showarrow){
21370 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
21371 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
21377 // onTouchStart : function(e, el, o)
21379 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
21383 // this.showPanelNext();
21387 getChildContainer : function()
21389 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
21393 * register a Navigation item
21394 * @param {Roo.bootstrap.NavItem} the navitem to add
21396 register : function(item)
21398 this.tabs.push( item);
21399 item.navId = this.navId; // not really needed..
21404 getActivePanel : function()
21407 Roo.each(this.tabs, function(t) {
21417 getPanelByName : function(n)
21420 Roo.each(this.tabs, function(t) {
21421 if (t.tabId == n) {
21429 indexOfPanel : function(p)
21432 Roo.each(this.tabs, function(t,i) {
21433 if (t.tabId == p.tabId) {
21442 * show a specific panel
21443 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
21444 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
21446 showPanel : function (pan)
21448 if(this.transition || typeof(pan) == 'undefined'){
21449 Roo.log("waiting for the transitionend");
21453 if (typeof(pan) == 'number') {
21454 pan = this.tabs[pan];
21457 if (typeof(pan) == 'string') {
21458 pan = this.getPanelByName(pan);
21461 var cur = this.getActivePanel();
21464 Roo.log('pan or acitve pan is undefined');
21468 if (pan.tabId == this.getActivePanel().tabId) {
21472 if (false === cur.fireEvent('beforedeactivate')) {
21476 if(this.bullets > 0 && !Roo.isTouch){
21477 this.setActiveBullet(this.indexOfPanel(pan));
21480 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
21482 //class="carousel-item carousel-item-next carousel-item-left"
21484 this.transition = true;
21485 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
21486 var lr = dir == 'next' ? 'left' : 'right';
21487 pan.el.addClass(dir); // or prev
21488 pan.el.addClass('carousel-item-' + dir); // or prev
21489 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
21490 cur.el.addClass(lr); // or right
21491 pan.el.addClass(lr);
21492 cur.el.addClass('carousel-item-' +lr); // or right
21493 pan.el.addClass('carousel-item-' +lr);
21497 cur.el.on('transitionend', function() {
21498 Roo.log("trans end?");
21500 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
21501 pan.setActive(true);
21503 cur.el.removeClass([lr, 'carousel-item-' + lr]);
21504 cur.setActive(false);
21506 _this.transition = false;
21508 }, this, { single: true } );
21513 cur.setActive(false);
21514 pan.setActive(true);
21519 showPanelNext : function()
21521 var i = this.indexOfPanel(this.getActivePanel());
21523 if (i >= this.tabs.length - 1 && !this.autoslide) {
21527 if (i >= this.tabs.length - 1 && this.autoslide) {
21531 this.showPanel(this.tabs[i+1]);
21534 showPanelPrev : function()
21536 var i = this.indexOfPanel(this.getActivePanel());
21538 if (i < 1 && !this.autoslide) {
21542 if (i < 1 && this.autoslide) {
21543 i = this.tabs.length;
21546 this.showPanel(this.tabs[i-1]);
21550 addBullet: function()
21552 if(!this.bullets || Roo.isTouch){
21555 var ctr = this.el.select('.carousel-bullets',true).first();
21556 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
21557 var bullet = ctr.createChild({
21558 cls : 'bullet bullet-' + i
21559 },ctr.dom.lastChild);
21564 bullet.on('click', (function(e, el, o, ii, t){
21566 e.preventDefault();
21568 this.showPanel(ii);
21570 if(this.autoslide && this.slideFn){
21571 clearInterval(this.slideFn);
21572 this.slideFn = window.setInterval(function() {
21573 _this.showPanelNext();
21577 }).createDelegate(this, [i, bullet], true));
21582 setActiveBullet : function(i)
21588 Roo.each(this.el.select('.bullet', true).elements, function(el){
21589 el.removeClass('selected');
21592 var bullet = this.el.select('.bullet-' + i, true).first();
21598 bullet.addClass('selected');
21609 Roo.apply(Roo.bootstrap.TabGroup, {
21613 * register a Navigation Group
21614 * @param {Roo.bootstrap.NavGroup} the navgroup to add
21616 register : function(navgrp)
21618 this.groups[navgrp.navId] = navgrp;
21622 * fetch a Navigation Group based on the navigation ID
21623 * if one does not exist , it will get created.
21624 * @param {string} the navgroup to add
21625 * @returns {Roo.bootstrap.NavGroup} the navgroup
21627 get: function(navId) {
21628 if (typeof(this.groups[navId]) == 'undefined') {
21629 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
21631 return this.groups[navId] ;
21646 * @class Roo.bootstrap.TabPanel
21647 * @extends Roo.bootstrap.Component
21648 * Bootstrap TabPanel class
21649 * @cfg {Boolean} active panel active
21650 * @cfg {String} html panel content
21651 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
21652 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
21653 * @cfg {String} href click to link..
21654 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
21658 * Create a new TabPanel
21659 * @param {Object} config The config object
21662 Roo.bootstrap.TabPanel = function(config){
21663 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
21667 * Fires when the active status changes
21668 * @param {Roo.bootstrap.TabPanel} this
21669 * @param {Boolean} state the new state
21674 * @event beforedeactivate
21675 * Fires before a tab is de-activated - can be used to do validation on a form.
21676 * @param {Roo.bootstrap.TabPanel} this
21677 * @return {Boolean} false if there is an error
21680 'beforedeactivate': true
21683 this.tabId = this.tabId || Roo.id();
21687 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
21694 touchSlide : false,
21695 getAutoCreate : function(){
21700 // item is needed for carousel - not sure if it has any effect otherwise
21701 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
21702 html: this.html || ''
21706 cfg.cls += ' active';
21710 cfg.tabId = this.tabId;
21718 initEvents: function()
21720 var p = this.parent();
21722 this.navId = this.navId || p.navId;
21724 if (typeof(this.navId) != 'undefined') {
21725 // not really needed.. but just in case.. parent should be a NavGroup.
21726 var tg = Roo.bootstrap.TabGroup.get(this.navId);
21730 var i = tg.tabs.length - 1;
21732 if(this.active && tg.bullets > 0 && i < tg.bullets){
21733 tg.setActiveBullet(i);
21737 this.el.on('click', this.onClick, this);
21739 if(Roo.isTouch && this.touchSlide){
21740 this.el.on("touchstart", this.onTouchStart, this);
21741 this.el.on("touchmove", this.onTouchMove, this);
21742 this.el.on("touchend", this.onTouchEnd, this);
21747 onRender : function(ct, position)
21749 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
21752 setActive : function(state)
21754 Roo.log("panel - set active " + this.tabId + "=" + state);
21756 this.active = state;
21758 this.el.removeClass('active');
21760 } else if (!this.el.hasClass('active')) {
21761 this.el.addClass('active');
21764 this.fireEvent('changed', this, state);
21767 onClick : function(e)
21769 e.preventDefault();
21771 if(!this.href.length){
21775 window.location.href = this.href;
21784 onTouchStart : function(e)
21786 this.swiping = false;
21788 this.startX = e.browserEvent.touches[0].clientX;
21789 this.startY = e.browserEvent.touches[0].clientY;
21792 onTouchMove : function(e)
21794 this.swiping = true;
21796 this.endX = e.browserEvent.touches[0].clientX;
21797 this.endY = e.browserEvent.touches[0].clientY;
21800 onTouchEnd : function(e)
21807 var tabGroup = this.parent();
21809 if(this.endX > this.startX){ // swiping right
21810 tabGroup.showPanelPrev();
21814 if(this.startX > this.endX){ // swiping left
21815 tabGroup.showPanelNext();
21834 * @class Roo.bootstrap.DateField
21835 * @extends Roo.bootstrap.Input
21836 * Bootstrap DateField class
21837 * @cfg {Number} weekStart default 0
21838 * @cfg {String} viewMode default empty, (months|years)
21839 * @cfg {String} minViewMode default empty, (months|years)
21840 * @cfg {Number} startDate default -Infinity
21841 * @cfg {Number} endDate default Infinity
21842 * @cfg {Boolean} todayHighlight default false
21843 * @cfg {Boolean} todayBtn default false
21844 * @cfg {Boolean} calendarWeeks default false
21845 * @cfg {Object} daysOfWeekDisabled default empty
21846 * @cfg {Boolean} singleMode default false (true | false)
21848 * @cfg {Boolean} keyboardNavigation default true
21849 * @cfg {String} language default en
21852 * Create a new DateField
21853 * @param {Object} config The config object
21856 Roo.bootstrap.DateField = function(config){
21857 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
21861 * Fires when this field show.
21862 * @param {Roo.bootstrap.DateField} this
21863 * @param {Mixed} date The date value
21868 * Fires when this field hide.
21869 * @param {Roo.bootstrap.DateField} this
21870 * @param {Mixed} date The date value
21875 * Fires when select a date.
21876 * @param {Roo.bootstrap.DateField} this
21877 * @param {Mixed} date The date value
21881 * @event beforeselect
21882 * Fires when before select a date.
21883 * @param {Roo.bootstrap.DateField} this
21884 * @param {Mixed} date The date value
21886 beforeselect : true
21890 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
21893 * @cfg {String} format
21894 * The default date format string which can be overriden for localization support. The format must be
21895 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
21899 * @cfg {String} altFormats
21900 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
21901 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21903 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21911 todayHighlight : false,
21917 keyboardNavigation: true,
21919 calendarWeeks: false,
21921 startDate: -Infinity,
21925 daysOfWeekDisabled: [],
21929 singleMode : false,
21931 UTCDate: function()
21933 return new Date(Date.UTC.apply(Date, arguments));
21936 UTCToday: function()
21938 var today = new Date();
21939 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21942 getDate: function() {
21943 var d = this.getUTCDate();
21944 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21947 getUTCDate: function() {
21951 setDate: function(d) {
21952 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21955 setUTCDate: function(d) {
21957 this.setValue(this.formatDate(this.date));
21960 onRender: function(ct, position)
21963 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21965 this.language = this.language || 'en';
21966 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21967 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21969 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21970 this.format = this.format || 'm/d/y';
21971 this.isInline = false;
21972 this.isInput = true;
21973 this.component = this.el.select('.add-on', true).first() || false;
21974 this.component = (this.component && this.component.length === 0) ? false : this.component;
21975 this.hasInput = this.component && this.inputEl().length;
21977 if (typeof(this.minViewMode === 'string')) {
21978 switch (this.minViewMode) {
21980 this.minViewMode = 1;
21983 this.minViewMode = 2;
21986 this.minViewMode = 0;
21991 if (typeof(this.viewMode === 'string')) {
21992 switch (this.viewMode) {
22005 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
22007 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
22009 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22011 this.picker().on('mousedown', this.onMousedown, this);
22012 this.picker().on('click', this.onClick, this);
22014 this.picker().addClass('datepicker-dropdown');
22016 this.startViewMode = this.viewMode;
22018 if(this.singleMode){
22019 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22020 v.setVisibilityMode(Roo.Element.DISPLAY);
22024 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22025 v.setStyle('width', '189px');
22029 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22030 if(!this.calendarWeeks){
22035 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22036 v.attr('colspan', function(i, val){
22037 return parseInt(val) + 1;
22042 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22044 this.setStartDate(this.startDate);
22045 this.setEndDate(this.endDate);
22047 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22054 if(this.isInline) {
22059 picker : function()
22061 return this.pickerEl;
22062 // return this.el.select('.datepicker', true).first();
22065 fillDow: function()
22067 var dowCnt = this.weekStart;
22076 if(this.calendarWeeks){
22084 while (dowCnt < this.weekStart + 7) {
22088 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
22092 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
22095 fillMonths: function()
22098 var months = this.picker().select('>.datepicker-months td', true).first();
22100 months.dom.innerHTML = '';
22106 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
22109 months.createChild(month);
22116 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;
22118 if (this.date < this.startDate) {
22119 this.viewDate = new Date(this.startDate);
22120 } else if (this.date > this.endDate) {
22121 this.viewDate = new Date(this.endDate);
22123 this.viewDate = new Date(this.date);
22131 var d = new Date(this.viewDate),
22132 year = d.getUTCFullYear(),
22133 month = d.getUTCMonth(),
22134 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
22135 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
22136 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
22137 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
22138 currentDate = this.date && this.date.valueOf(),
22139 today = this.UTCToday();
22141 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
22143 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22145 // this.picker.select('>tfoot th.today').
22146 // .text(dates[this.language].today)
22147 // .toggle(this.todayBtn !== false);
22149 this.updateNavArrows();
22152 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
22154 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
22156 prevMonth.setUTCDate(day);
22158 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
22160 var nextMonth = new Date(prevMonth);
22162 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
22164 nextMonth = nextMonth.valueOf();
22166 var fillMonths = false;
22168 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
22170 while(prevMonth.valueOf() <= nextMonth) {
22173 if (prevMonth.getUTCDay() === this.weekStart) {
22175 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
22183 if(this.calendarWeeks){
22184 // ISO 8601: First week contains first thursday.
22185 // ISO also states week starts on Monday, but we can be more abstract here.
22187 // Start of current week: based on weekstart/current date
22188 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
22189 // Thursday of this week
22190 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
22191 // First Thursday of year, year from thursday
22192 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
22193 // Calendar week: ms between thursdays, div ms per day, div 7 days
22194 calWeek = (th - yth) / 864e5 / 7 + 1;
22196 fillMonths.cn.push({
22204 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
22206 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
22209 if (this.todayHighlight &&
22210 prevMonth.getUTCFullYear() == today.getFullYear() &&
22211 prevMonth.getUTCMonth() == today.getMonth() &&
22212 prevMonth.getUTCDate() == today.getDate()) {
22213 clsName += ' today';
22216 if (currentDate && prevMonth.valueOf() === currentDate) {
22217 clsName += ' active';
22220 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
22221 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
22222 clsName += ' disabled';
22225 fillMonths.cn.push({
22227 cls: 'day ' + clsName,
22228 html: prevMonth.getDate()
22231 prevMonth.setDate(prevMonth.getDate()+1);
22234 var currentYear = this.date && this.date.getUTCFullYear();
22235 var currentMonth = this.date && this.date.getUTCMonth();
22237 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
22239 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
22240 v.removeClass('active');
22242 if(currentYear === year && k === currentMonth){
22243 v.addClass('active');
22246 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
22247 v.addClass('disabled');
22253 year = parseInt(year/10, 10) * 10;
22255 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
22257 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
22260 for (var i = -1; i < 11; i++) {
22261 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
22263 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
22271 showMode: function(dir)
22274 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
22277 Roo.each(this.picker().select('>div',true).elements, function(v){
22278 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22281 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
22286 if(this.isInline) {
22290 this.picker().removeClass(['bottom', 'top']);
22292 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22294 * place to the top of element!
22298 this.picker().addClass('top');
22299 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22304 this.picker().addClass('bottom');
22306 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22309 parseDate : function(value)
22311 if(!value || value instanceof Date){
22314 var v = Date.parseDate(value, this.format);
22315 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
22316 v = Date.parseDate(value, 'Y-m-d');
22318 if(!v && this.altFormats){
22319 if(!this.altFormatsArray){
22320 this.altFormatsArray = this.altFormats.split("|");
22322 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22323 v = Date.parseDate(value, this.altFormatsArray[i]);
22329 formatDate : function(date, fmt)
22331 return (!date || !(date instanceof Date)) ?
22332 date : date.dateFormat(fmt || this.format);
22335 onFocus : function()
22337 Roo.bootstrap.DateField.superclass.onFocus.call(this);
22341 onBlur : function()
22343 Roo.bootstrap.DateField.superclass.onBlur.call(this);
22345 var d = this.inputEl().getValue();
22352 showPopup : function()
22354 this.picker().show();
22358 this.fireEvent('showpopup', this, this.date);
22361 hidePopup : function()
22363 if(this.isInline) {
22366 this.picker().hide();
22367 this.viewMode = this.startViewMode;
22370 this.fireEvent('hidepopup', this, this.date);
22374 onMousedown: function(e)
22376 e.stopPropagation();
22377 e.preventDefault();
22382 Roo.bootstrap.DateField.superclass.keyup.call(this);
22386 setValue: function(v)
22388 if(this.fireEvent('beforeselect', this, v) !== false){
22389 var d = new Date(this.parseDate(v) ).clearTime();
22391 if(isNaN(d.getTime())){
22392 this.date = this.viewDate = '';
22393 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22397 v = this.formatDate(d);
22399 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
22401 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
22405 this.fireEvent('select', this, this.date);
22409 getValue: function()
22411 return this.formatDate(this.date);
22414 fireKey: function(e)
22416 if (!this.picker().isVisible()){
22417 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22423 var dateChanged = false,
22425 newDate, newViewDate;
22430 e.preventDefault();
22434 if (!this.keyboardNavigation) {
22437 dir = e.keyCode == 37 ? -1 : 1;
22440 newDate = this.moveYear(this.date, dir);
22441 newViewDate = this.moveYear(this.viewDate, dir);
22442 } else if (e.shiftKey){
22443 newDate = this.moveMonth(this.date, dir);
22444 newViewDate = this.moveMonth(this.viewDate, dir);
22446 newDate = new Date(this.date);
22447 newDate.setUTCDate(this.date.getUTCDate() + dir);
22448 newViewDate = new Date(this.viewDate);
22449 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
22451 if (this.dateWithinRange(newDate)){
22452 this.date = newDate;
22453 this.viewDate = newViewDate;
22454 this.setValue(this.formatDate(this.date));
22456 e.preventDefault();
22457 dateChanged = true;
22462 if (!this.keyboardNavigation) {
22465 dir = e.keyCode == 38 ? -1 : 1;
22467 newDate = this.moveYear(this.date, dir);
22468 newViewDate = this.moveYear(this.viewDate, dir);
22469 } else if (e.shiftKey){
22470 newDate = this.moveMonth(this.date, dir);
22471 newViewDate = this.moveMonth(this.viewDate, dir);
22473 newDate = new Date(this.date);
22474 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
22475 newViewDate = new Date(this.viewDate);
22476 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
22478 if (this.dateWithinRange(newDate)){
22479 this.date = newDate;
22480 this.viewDate = newViewDate;
22481 this.setValue(this.formatDate(this.date));
22483 e.preventDefault();
22484 dateChanged = true;
22488 this.setValue(this.formatDate(this.date));
22490 e.preventDefault();
22493 this.setValue(this.formatDate(this.date));
22507 onClick: function(e)
22509 e.stopPropagation();
22510 e.preventDefault();
22512 var target = e.getTarget();
22514 if(target.nodeName.toLowerCase() === 'i'){
22515 target = Roo.get(target).dom.parentNode;
22518 var nodeName = target.nodeName;
22519 var className = target.className;
22520 var html = target.innerHTML;
22521 //Roo.log(nodeName);
22523 switch(nodeName.toLowerCase()) {
22525 switch(className) {
22531 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
22532 switch(this.viewMode){
22534 this.viewDate = this.moveMonth(this.viewDate, dir);
22538 this.viewDate = this.moveYear(this.viewDate, dir);
22544 var date = new Date();
22545 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
22547 this.setValue(this.formatDate(this.date));
22554 if (className.indexOf('disabled') < 0) {
22555 if (!this.viewDate) {
22556 this.viewDate = new Date();
22558 this.viewDate.setUTCDate(1);
22559 if (className.indexOf('month') > -1) {
22560 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
22562 var year = parseInt(html, 10) || 0;
22563 this.viewDate.setUTCFullYear(year);
22567 if(this.singleMode){
22568 this.setValue(this.formatDate(this.viewDate));
22579 //Roo.log(className);
22580 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
22581 var day = parseInt(html, 10) || 1;
22582 var year = (this.viewDate || new Date()).getUTCFullYear(),
22583 month = (this.viewDate || new Date()).getUTCMonth();
22585 if (className.indexOf('old') > -1) {
22592 } else if (className.indexOf('new') > -1) {
22600 //Roo.log([year,month,day]);
22601 this.date = this.UTCDate(year, month, day,0,0,0,0);
22602 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
22604 //Roo.log(this.formatDate(this.date));
22605 this.setValue(this.formatDate(this.date));
22612 setStartDate: function(startDate)
22614 this.startDate = startDate || -Infinity;
22615 if (this.startDate !== -Infinity) {
22616 this.startDate = this.parseDate(this.startDate);
22619 this.updateNavArrows();
22622 setEndDate: function(endDate)
22624 this.endDate = endDate || Infinity;
22625 if (this.endDate !== Infinity) {
22626 this.endDate = this.parseDate(this.endDate);
22629 this.updateNavArrows();
22632 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
22634 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
22635 if (typeof(this.daysOfWeekDisabled) !== 'object') {
22636 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
22638 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
22639 return parseInt(d, 10);
22642 this.updateNavArrows();
22645 updateNavArrows: function()
22647 if(this.singleMode){
22651 var d = new Date(this.viewDate),
22652 year = d.getUTCFullYear(),
22653 month = d.getUTCMonth();
22655 Roo.each(this.picker().select('.prev', true).elements, function(v){
22657 switch (this.viewMode) {
22660 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
22666 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
22673 Roo.each(this.picker().select('.next', true).elements, function(v){
22675 switch (this.viewMode) {
22678 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
22684 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
22692 moveMonth: function(date, dir)
22697 var new_date = new Date(date.valueOf()),
22698 day = new_date.getUTCDate(),
22699 month = new_date.getUTCMonth(),
22700 mag = Math.abs(dir),
22702 dir = dir > 0 ? 1 : -1;
22705 // If going back one month, make sure month is not current month
22706 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
22708 return new_date.getUTCMonth() == month;
22710 // If going forward one month, make sure month is as expected
22711 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
22713 return new_date.getUTCMonth() != new_month;
22715 new_month = month + dir;
22716 new_date.setUTCMonth(new_month);
22717 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
22718 if (new_month < 0 || new_month > 11) {
22719 new_month = (new_month + 12) % 12;
22722 // For magnitudes >1, move one month at a time...
22723 for (var i=0; i<mag; i++) {
22724 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
22725 new_date = this.moveMonth(new_date, dir);
22727 // ...then reset the day, keeping it in the new month
22728 new_month = new_date.getUTCMonth();
22729 new_date.setUTCDate(day);
22731 return new_month != new_date.getUTCMonth();
22734 // Common date-resetting loop -- if date is beyond end of month, make it
22737 new_date.setUTCDate(--day);
22738 new_date.setUTCMonth(new_month);
22743 moveYear: function(date, dir)
22745 return this.moveMonth(date, dir*12);
22748 dateWithinRange: function(date)
22750 return date >= this.startDate && date <= this.endDate;
22756 this.picker().remove();
22759 validateValue : function(value)
22761 if(this.getVisibilityEl().hasClass('hidden')){
22765 if(value.length < 1) {
22766 if(this.allowBlank){
22772 if(value.length < this.minLength){
22775 if(value.length > this.maxLength){
22779 var vt = Roo.form.VTypes;
22780 if(!vt[this.vtype](value, this)){
22784 if(typeof this.validator == "function"){
22785 var msg = this.validator(value);
22791 if(this.regex && !this.regex.test(value)){
22795 if(typeof(this.parseDate(value)) == 'undefined'){
22799 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
22803 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
22813 this.date = this.viewDate = '';
22815 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22820 Roo.apply(Roo.bootstrap.DateField, {
22831 html: '<i class="fa fa-arrow-left"/>'
22841 html: '<i class="fa fa-arrow-right"/>'
22883 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
22884 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
22885 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
22886 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22887 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
22900 navFnc: 'FullYear',
22905 navFnc: 'FullYear',
22910 Roo.apply(Roo.bootstrap.DateField, {
22914 cls: 'datepicker dropdown-menu roo-dynamic shadow',
22918 cls: 'datepicker-days',
22922 cls: 'table-condensed',
22924 Roo.bootstrap.DateField.head,
22928 Roo.bootstrap.DateField.footer
22935 cls: 'datepicker-months',
22939 cls: 'table-condensed',
22941 Roo.bootstrap.DateField.head,
22942 Roo.bootstrap.DateField.content,
22943 Roo.bootstrap.DateField.footer
22950 cls: 'datepicker-years',
22954 cls: 'table-condensed',
22956 Roo.bootstrap.DateField.head,
22957 Roo.bootstrap.DateField.content,
22958 Roo.bootstrap.DateField.footer
22977 * @class Roo.bootstrap.TimeField
22978 * @extends Roo.bootstrap.Input
22979 * Bootstrap DateField class
22983 * Create a new TimeField
22984 * @param {Object} config The config object
22987 Roo.bootstrap.TimeField = function(config){
22988 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22992 * Fires when this field show.
22993 * @param {Roo.bootstrap.DateField} thisthis
22994 * @param {Mixed} date The date value
22999 * Fires when this field hide.
23000 * @param {Roo.bootstrap.DateField} this
23001 * @param {Mixed} date The date value
23006 * Fires when select a date.
23007 * @param {Roo.bootstrap.DateField} this
23008 * @param {Mixed} date The date value
23014 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
23017 * @cfg {String} format
23018 * The default time format string which can be overriden for localization support. The format must be
23019 * valid according to {@link Date#parseDate} (defaults to 'H:i').
23023 getAutoCreate : function()
23025 this.after = '<i class="fa far fa-clock"></i>';
23026 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
23030 onRender: function(ct, position)
23033 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
23035 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
23037 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23039 this.pop = this.picker().select('>.datepicker-time',true).first();
23040 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23042 this.picker().on('mousedown', this.onMousedown, this);
23043 this.picker().on('click', this.onClick, this);
23045 this.picker().addClass('datepicker-dropdown');
23050 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23051 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23052 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23053 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23054 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23055 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23059 fireKey: function(e){
23060 if (!this.picker().isVisible()){
23061 if (e.keyCode == 27) { // allow escape to hide and re-show picker
23067 e.preventDefault();
23075 this.onTogglePeriod();
23078 this.onIncrementMinutes();
23081 this.onDecrementMinutes();
23090 onClick: function(e) {
23091 e.stopPropagation();
23092 e.preventDefault();
23095 picker : function()
23097 return this.pickerEl;
23100 fillTime: function()
23102 var time = this.pop.select('tbody', true).first();
23104 time.dom.innerHTML = '';
23119 cls: 'hours-up fa fas fa-chevron-up'
23139 cls: 'minutes-up fa fas fa-chevron-up'
23160 cls: 'timepicker-hour',
23175 cls: 'timepicker-minute',
23190 cls: 'btn btn-primary period',
23212 cls: 'hours-down fa fas fa-chevron-down'
23232 cls: 'minutes-down fa fas fa-chevron-down'
23250 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
23257 var hours = this.time.getHours();
23258 var minutes = this.time.getMinutes();
23271 hours = hours - 12;
23275 hours = '0' + hours;
23279 minutes = '0' + minutes;
23282 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
23283 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
23284 this.pop.select('button', true).first().dom.innerHTML = period;
23290 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
23292 var cls = ['bottom'];
23294 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
23301 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
23305 //this.picker().setXY(20000,20000);
23306 this.picker().addClass(cls.join('-'));
23310 Roo.each(cls, function(c){
23315 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
23316 //_this.picker().setTop(_this.inputEl().getHeight());
23320 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
23322 //_this.picker().setTop(0 - _this.picker().getHeight());
23327 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
23331 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
23339 onFocus : function()
23341 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
23345 onBlur : function()
23347 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
23353 this.picker().show();
23358 this.fireEvent('show', this, this.date);
23363 this.picker().hide();
23366 this.fireEvent('hide', this, this.date);
23369 setTime : function()
23372 this.setValue(this.time.format(this.format));
23374 this.fireEvent('select', this, this.date);
23379 onMousedown: function(e){
23380 e.stopPropagation();
23381 e.preventDefault();
23384 onIncrementHours: function()
23386 Roo.log('onIncrementHours');
23387 this.time = this.time.add(Date.HOUR, 1);
23392 onDecrementHours: function()
23394 Roo.log('onDecrementHours');
23395 this.time = this.time.add(Date.HOUR, -1);
23399 onIncrementMinutes: function()
23401 Roo.log('onIncrementMinutes');
23402 this.time = this.time.add(Date.MINUTE, 1);
23406 onDecrementMinutes: function()
23408 Roo.log('onDecrementMinutes');
23409 this.time = this.time.add(Date.MINUTE, -1);
23413 onTogglePeriod: function()
23415 Roo.log('onTogglePeriod');
23416 this.time = this.time.add(Date.HOUR, 12);
23424 Roo.apply(Roo.bootstrap.TimeField, {
23428 cls: 'datepicker dropdown-menu',
23432 cls: 'datepicker-time',
23436 cls: 'table-condensed',
23465 cls: 'btn btn-info ok',
23493 * @class Roo.bootstrap.MonthField
23494 * @extends Roo.bootstrap.Input
23495 * Bootstrap MonthField class
23497 * @cfg {String} language default en
23500 * Create a new MonthField
23501 * @param {Object} config The config object
23504 Roo.bootstrap.MonthField = function(config){
23505 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
23510 * Fires when this field show.
23511 * @param {Roo.bootstrap.MonthField} this
23512 * @param {Mixed} date The date value
23517 * Fires when this field hide.
23518 * @param {Roo.bootstrap.MonthField} this
23519 * @param {Mixed} date The date value
23524 * Fires when select a date.
23525 * @param {Roo.bootstrap.MonthField} this
23526 * @param {String} oldvalue The old value
23527 * @param {String} newvalue The new value
23533 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
23535 onRender: function(ct, position)
23538 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
23540 this.language = this.language || 'en';
23541 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
23542 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
23544 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
23545 this.isInline = false;
23546 this.isInput = true;
23547 this.component = this.el.select('.add-on', true).first() || false;
23548 this.component = (this.component && this.component.length === 0) ? false : this.component;
23549 this.hasInput = this.component && this.inputEL().length;
23551 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
23553 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23555 this.picker().on('mousedown', this.onMousedown, this);
23556 this.picker().on('click', this.onClick, this);
23558 this.picker().addClass('datepicker-dropdown');
23560 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
23561 v.setStyle('width', '189px');
23568 if(this.isInline) {
23574 setValue: function(v, suppressEvent)
23576 var o = this.getValue();
23578 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
23582 if(suppressEvent !== true){
23583 this.fireEvent('select', this, o, v);
23588 getValue: function()
23593 onClick: function(e)
23595 e.stopPropagation();
23596 e.preventDefault();
23598 var target = e.getTarget();
23600 if(target.nodeName.toLowerCase() === 'i'){
23601 target = Roo.get(target).dom.parentNode;
23604 var nodeName = target.nodeName;
23605 var className = target.className;
23606 var html = target.innerHTML;
23608 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
23612 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
23614 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23620 picker : function()
23622 return this.pickerEl;
23625 fillMonths: function()
23628 var months = this.picker().select('>.datepicker-months td', true).first();
23630 months.dom.innerHTML = '';
23636 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
23639 months.createChild(month);
23648 if(typeof(this.vIndex) == 'undefined' && this.value.length){
23649 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
23652 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
23653 e.removeClass('active');
23655 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
23656 e.addClass('active');
23663 if(this.isInline) {
23667 this.picker().removeClass(['bottom', 'top']);
23669 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23671 * place to the top of element!
23675 this.picker().addClass('top');
23676 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23681 this.picker().addClass('bottom');
23683 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23686 onFocus : function()
23688 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
23692 onBlur : function()
23694 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
23696 var d = this.inputEl().getValue();
23705 this.picker().show();
23706 this.picker().select('>.datepicker-months', true).first().show();
23710 this.fireEvent('show', this, this.date);
23715 if(this.isInline) {
23718 this.picker().hide();
23719 this.fireEvent('hide', this, this.date);
23723 onMousedown: function(e)
23725 e.stopPropagation();
23726 e.preventDefault();
23731 Roo.bootstrap.MonthField.superclass.keyup.call(this);
23735 fireKey: function(e)
23737 if (!this.picker().isVisible()){
23738 if (e.keyCode == 27) {// allow escape to hide and re-show picker
23749 e.preventDefault();
23753 dir = e.keyCode == 37 ? -1 : 1;
23755 this.vIndex = this.vIndex + dir;
23757 if(this.vIndex < 0){
23761 if(this.vIndex > 11){
23765 if(isNaN(this.vIndex)){
23769 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23775 dir = e.keyCode == 38 ? -1 : 1;
23777 this.vIndex = this.vIndex + dir * 4;
23779 if(this.vIndex < 0){
23783 if(this.vIndex > 11){
23787 if(isNaN(this.vIndex)){
23791 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23796 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23797 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23801 e.preventDefault();
23804 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23805 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23821 this.picker().remove();
23826 Roo.apply(Roo.bootstrap.MonthField, {
23845 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23846 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
23851 Roo.apply(Roo.bootstrap.MonthField, {
23855 cls: 'datepicker dropdown-menu roo-dynamic',
23859 cls: 'datepicker-months',
23863 cls: 'table-condensed',
23865 Roo.bootstrap.DateField.content
23885 * @class Roo.bootstrap.CheckBox
23886 * @extends Roo.bootstrap.Input
23887 * Bootstrap CheckBox class
23889 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23890 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
23891 * @cfg {String} boxLabel The text that appears beside the checkbox
23892 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
23893 * @cfg {Boolean} checked initnal the element
23894 * @cfg {Boolean} inline inline the element (default false)
23895 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
23896 * @cfg {String} tooltip label tooltip
23899 * Create a new CheckBox
23900 * @param {Object} config The config object
23903 Roo.bootstrap.CheckBox = function(config){
23904 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23909 * Fires when the element is checked or unchecked.
23910 * @param {Roo.bootstrap.CheckBox} this This input
23911 * @param {Boolean} checked The new checked value
23916 * Fires when the element is click.
23917 * @param {Roo.bootstrap.CheckBox} this This input
23924 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
23926 inputType: 'checkbox',
23935 // checkbox success does not make any sense really..
23940 getAutoCreate : function()
23942 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23948 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23951 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
23957 type : this.inputType,
23958 value : this.inputValue,
23959 cls : 'roo-' + this.inputType, //'form-box',
23960 placeholder : this.placeholder || ''
23964 if(this.inputType != 'radio'){
23968 cls : 'roo-hidden-value',
23969 value : this.checked ? this.inputValue : this.valueOff
23974 if (this.weight) { // Validity check?
23975 cfg.cls += " " + this.inputType + "-" + this.weight;
23978 if (this.disabled) {
23979 input.disabled=true;
23983 input.checked = this.checked;
23988 input.name = this.name;
23990 if(this.inputType != 'radio'){
23991 hidden.name = this.name;
23992 input.name = '_hidden_' + this.name;
23997 input.cls += ' input-' + this.size;
24002 ['xs','sm','md','lg'].map(function(size){
24003 if (settings[size]) {
24004 cfg.cls += ' col-' + size + '-' + settings[size];
24008 var inputblock = input;
24010 if (this.before || this.after) {
24013 cls : 'input-group',
24018 inputblock.cn.push({
24020 cls : 'input-group-addon',
24025 inputblock.cn.push(input);
24027 if(this.inputType != 'radio'){
24028 inputblock.cn.push(hidden);
24032 inputblock.cn.push({
24034 cls : 'input-group-addon',
24040 var boxLabelCfg = false;
24046 //'for': id, // box label is handled by onclick - so no for...
24048 html: this.boxLabel
24051 boxLabelCfg.tooltip = this.tooltip;
24057 if (align ==='left' && this.fieldLabel.length) {
24058 // Roo.log("left and has label");
24063 cls : 'control-label',
24064 html : this.fieldLabel
24075 cfg.cn[1].cn.push(boxLabelCfg);
24078 if(this.labelWidth > 12){
24079 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
24082 if(this.labelWidth < 13 && this.labelmd == 0){
24083 this.labelmd = this.labelWidth;
24086 if(this.labellg > 0){
24087 cfg.cn[0].cls += ' col-lg-' + this.labellg;
24088 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
24091 if(this.labelmd > 0){
24092 cfg.cn[0].cls += ' col-md-' + this.labelmd;
24093 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
24096 if(this.labelsm > 0){
24097 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
24098 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
24101 if(this.labelxs > 0){
24102 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
24103 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
24106 } else if ( this.fieldLabel.length) {
24107 // Roo.log(" label");
24111 tag: this.boxLabel ? 'span' : 'label',
24113 cls: 'control-label box-input-label',
24114 //cls : 'input-group-addon',
24115 html : this.fieldLabel
24122 cfg.cn.push(boxLabelCfg);
24127 // Roo.log(" no label && no align");
24128 cfg.cn = [ inputblock ] ;
24130 cfg.cn.push(boxLabelCfg);
24138 if(this.inputType != 'radio'){
24139 cfg.cn.push(hidden);
24147 * return the real input element.
24149 inputEl: function ()
24151 return this.el.select('input.roo-' + this.inputType,true).first();
24153 hiddenEl: function ()
24155 return this.el.select('input.roo-hidden-value',true).first();
24158 labelEl: function()
24160 return this.el.select('label.control-label',true).first();
24162 /* depricated... */
24166 return this.labelEl();
24169 boxLabelEl: function()
24171 return this.el.select('label.box-label',true).first();
24174 initEvents : function()
24176 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
24178 this.inputEl().on('click', this.onClick, this);
24180 if (this.boxLabel) {
24181 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
24184 this.startValue = this.getValue();
24187 Roo.bootstrap.CheckBox.register(this);
24191 onClick : function(e)
24193 if(this.fireEvent('click', this, e) !== false){
24194 this.setChecked(!this.checked);
24199 setChecked : function(state,suppressEvent)
24201 this.startValue = this.getValue();
24203 if(this.inputType == 'radio'){
24205 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24206 e.dom.checked = false;
24209 this.inputEl().dom.checked = true;
24211 this.inputEl().dom.value = this.inputValue;
24213 if(suppressEvent !== true){
24214 this.fireEvent('check', this, true);
24222 this.checked = state;
24224 this.inputEl().dom.checked = state;
24227 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
24229 if(suppressEvent !== true){
24230 this.fireEvent('check', this, state);
24236 getValue : function()
24238 if(this.inputType == 'radio'){
24239 return this.getGroupValue();
24242 return this.hiddenEl().dom.value;
24246 getGroupValue : function()
24248 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
24252 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
24255 setValue : function(v,suppressEvent)
24257 if(this.inputType == 'radio'){
24258 this.setGroupValue(v, suppressEvent);
24262 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
24267 setGroupValue : function(v, suppressEvent)
24269 this.startValue = this.getValue();
24271 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24272 e.dom.checked = false;
24274 if(e.dom.value == v){
24275 e.dom.checked = true;
24279 if(suppressEvent !== true){
24280 this.fireEvent('check', this, true);
24288 validate : function()
24290 if(this.getVisibilityEl().hasClass('hidden')){
24296 (this.inputType == 'radio' && this.validateRadio()) ||
24297 (this.inputType == 'checkbox' && this.validateCheckbox())
24303 this.markInvalid();
24307 validateRadio : function()
24309 if(this.getVisibilityEl().hasClass('hidden')){
24313 if(this.allowBlank){
24319 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24320 if(!e.dom.checked){
24332 validateCheckbox : function()
24335 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
24336 //return (this.getValue() == this.inputValue) ? true : false;
24339 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24347 for(var i in group){
24348 if(group[i].el.isVisible(true)){
24356 for(var i in group){
24361 r = (group[i].getValue() == group[i].inputValue) ? true : false;
24368 * Mark this field as valid
24370 markValid : function()
24374 this.fireEvent('valid', this);
24376 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24379 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24386 if(this.inputType == 'radio'){
24387 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24388 var fg = e.findParent('.form-group', false, true);
24389 if (Roo.bootstrap.version == 3) {
24390 fg.removeClass([_this.invalidClass, _this.validClass]);
24391 fg.addClass(_this.validClass);
24393 fg.removeClass(['is-valid', 'is-invalid']);
24394 fg.addClass('is-valid');
24402 var fg = this.el.findParent('.form-group', false, true);
24403 if (Roo.bootstrap.version == 3) {
24404 fg.removeClass([this.invalidClass, this.validClass]);
24405 fg.addClass(this.validClass);
24407 fg.removeClass(['is-valid', 'is-invalid']);
24408 fg.addClass('is-valid');
24413 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24419 for(var i in group){
24420 var fg = group[i].el.findParent('.form-group', false, true);
24421 if (Roo.bootstrap.version == 3) {
24422 fg.removeClass([this.invalidClass, this.validClass]);
24423 fg.addClass(this.validClass);
24425 fg.removeClass(['is-valid', 'is-invalid']);
24426 fg.addClass('is-valid');
24432 * Mark this field as invalid
24433 * @param {String} msg The validation message
24435 markInvalid : function(msg)
24437 if(this.allowBlank){
24443 this.fireEvent('invalid', this, msg);
24445 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24448 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24452 label.markInvalid();
24455 if(this.inputType == 'radio'){
24457 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24458 var fg = e.findParent('.form-group', false, true);
24459 if (Roo.bootstrap.version == 3) {
24460 fg.removeClass([_this.invalidClass, _this.validClass]);
24461 fg.addClass(_this.invalidClass);
24463 fg.removeClass(['is-invalid', 'is-valid']);
24464 fg.addClass('is-invalid');
24472 var fg = this.el.findParent('.form-group', false, true);
24473 if (Roo.bootstrap.version == 3) {
24474 fg.removeClass([_this.invalidClass, _this.validClass]);
24475 fg.addClass(_this.invalidClass);
24477 fg.removeClass(['is-invalid', 'is-valid']);
24478 fg.addClass('is-invalid');
24483 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24489 for(var i in group){
24490 var fg = group[i].el.findParent('.form-group', false, true);
24491 if (Roo.bootstrap.version == 3) {
24492 fg.removeClass([_this.invalidClass, _this.validClass]);
24493 fg.addClass(_this.invalidClass);
24495 fg.removeClass(['is-invalid', 'is-valid']);
24496 fg.addClass('is-invalid');
24502 clearInvalid : function()
24504 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
24506 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
24508 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24510 if (label && label.iconEl) {
24511 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
24512 label.iconEl.removeClass(['is-invalid', 'is-valid']);
24516 disable : function()
24518 if(this.inputType != 'radio'){
24519 Roo.bootstrap.CheckBox.superclass.disable.call(this);
24526 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24527 _this.getActionEl().addClass(this.disabledClass);
24528 e.dom.disabled = true;
24532 this.disabled = true;
24533 this.fireEvent("disable", this);
24537 enable : function()
24539 if(this.inputType != 'radio'){
24540 Roo.bootstrap.CheckBox.superclass.enable.call(this);
24547 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24548 _this.getActionEl().removeClass(this.disabledClass);
24549 e.dom.disabled = false;
24553 this.disabled = false;
24554 this.fireEvent("enable", this);
24558 setBoxLabel : function(v)
24563 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24569 Roo.apply(Roo.bootstrap.CheckBox, {
24574 * register a CheckBox Group
24575 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
24577 register : function(checkbox)
24579 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
24580 this.groups[checkbox.groupId] = {};
24583 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
24587 this.groups[checkbox.groupId][checkbox.name] = checkbox;
24591 * fetch a CheckBox Group based on the group ID
24592 * @param {string} the group ID
24593 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
24595 get: function(groupId) {
24596 if (typeof(this.groups[groupId]) == 'undefined') {
24600 return this.groups[groupId] ;
24613 * @class Roo.bootstrap.Radio
24614 * @extends Roo.bootstrap.Component
24615 * Bootstrap Radio class
24616 * @cfg {String} boxLabel - the label associated
24617 * @cfg {String} value - the value of radio
24620 * Create a new Radio
24621 * @param {Object} config The config object
24623 Roo.bootstrap.Radio = function(config){
24624 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
24628 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
24634 getAutoCreate : function()
24638 cls : 'form-group radio',
24643 html : this.boxLabel
24651 initEvents : function()
24653 this.parent().register(this);
24655 this.el.on('click', this.onClick, this);
24659 onClick : function(e)
24661 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
24662 this.setChecked(true);
24666 setChecked : function(state, suppressEvent)
24668 this.parent().setValue(this.value, suppressEvent);
24672 setBoxLabel : function(v)
24677 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24692 * @class Roo.bootstrap.SecurePass
24693 * @extends Roo.bootstrap.Input
24694 * Bootstrap SecurePass class
24698 * Create a new SecurePass
24699 * @param {Object} config The config object
24702 Roo.bootstrap.SecurePass = function (config) {
24703 // these go here, so the translation tool can replace them..
24705 PwdEmpty: "Please type a password, and then retype it to confirm.",
24706 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24707 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24708 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24709 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24710 FNInPwd: "Your password can't contain your first name. Please type a different password.",
24711 LNInPwd: "Your password can't contain your last name. Please type a different password.",
24712 TooWeak: "Your password is Too Weak."
24714 this.meterLabel = "Password strength:";
24715 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
24716 this.meterClass = [
24717 "roo-password-meter-tooweak",
24718 "roo-password-meter-weak",
24719 "roo-password-meter-medium",
24720 "roo-password-meter-strong",
24721 "roo-password-meter-grey"
24726 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
24729 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
24731 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
24733 * PwdEmpty: "Please type a password, and then retype it to confirm.",
24734 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24735 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24736 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24737 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24738 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
24739 * LNInPwd: "Your password can't contain your last name. Please type a different password."
24749 * @cfg {String/Object} Label for the strength meter (defaults to
24750 * 'Password strength:')
24755 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
24756 * ['Weak', 'Medium', 'Strong'])
24759 pwdStrengths: false,
24772 initEvents: function ()
24774 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
24776 if (this.el.is('input[type=password]') && Roo.isSafari) {
24777 this.el.on('keydown', this.SafariOnKeyDown, this);
24780 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
24783 onRender: function (ct, position)
24785 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
24786 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
24787 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
24789 this.trigger.createChild({
24794 cls: 'roo-password-meter-grey col-xs-12',
24797 //width: this.meterWidth + 'px'
24801 cls: 'roo-password-meter-text'
24807 if (this.hideTrigger) {
24808 this.trigger.setDisplayed(false);
24810 this.setSize(this.width || '', this.height || '');
24813 onDestroy: function ()
24815 if (this.trigger) {
24816 this.trigger.removeAllListeners();
24817 this.trigger.remove();
24820 this.wrap.remove();
24822 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
24825 checkStrength: function ()
24827 var pwd = this.inputEl().getValue();
24828 if (pwd == this._lastPwd) {
24833 if (this.ClientSideStrongPassword(pwd)) {
24835 } else if (this.ClientSideMediumPassword(pwd)) {
24837 } else if (this.ClientSideWeakPassword(pwd)) {
24843 Roo.log('strength1: ' + strength);
24845 //var pm = this.trigger.child('div/div/div').dom;
24846 var pm = this.trigger.child('div/div');
24847 pm.removeClass(this.meterClass);
24848 pm.addClass(this.meterClass[strength]);
24851 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24853 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24855 this._lastPwd = pwd;
24859 Roo.bootstrap.SecurePass.superclass.reset.call(this);
24861 this._lastPwd = '';
24863 var pm = this.trigger.child('div/div');
24864 pm.removeClass(this.meterClass);
24865 pm.addClass('roo-password-meter-grey');
24868 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24871 this.inputEl().dom.type='password';
24874 validateValue: function (value)
24876 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
24879 if (value.length == 0) {
24880 if (this.allowBlank) {
24881 this.clearInvalid();
24885 this.markInvalid(this.errors.PwdEmpty);
24886 this.errorMsg = this.errors.PwdEmpty;
24894 if (!value.match(/[\x21-\x7e]+/)) {
24895 this.markInvalid(this.errors.PwdBadChar);
24896 this.errorMsg = this.errors.PwdBadChar;
24899 if (value.length < 6) {
24900 this.markInvalid(this.errors.PwdShort);
24901 this.errorMsg = this.errors.PwdShort;
24904 if (value.length > 16) {
24905 this.markInvalid(this.errors.PwdLong);
24906 this.errorMsg = this.errors.PwdLong;
24910 if (this.ClientSideStrongPassword(value)) {
24912 } else if (this.ClientSideMediumPassword(value)) {
24914 } else if (this.ClientSideWeakPassword(value)) {
24921 if (strength < 2) {
24922 //this.markInvalid(this.errors.TooWeak);
24923 this.errorMsg = this.errors.TooWeak;
24928 console.log('strength2: ' + strength);
24930 //var pm = this.trigger.child('div/div/div').dom;
24932 var pm = this.trigger.child('div/div');
24933 pm.removeClass(this.meterClass);
24934 pm.addClass(this.meterClass[strength]);
24936 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24938 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24940 this.errorMsg = '';
24944 CharacterSetChecks: function (type)
24947 this.fResult = false;
24950 isctype: function (character, type)
24953 case this.kCapitalLetter:
24954 if (character >= 'A' && character <= 'Z') {
24959 case this.kSmallLetter:
24960 if (character >= 'a' && character <= 'z') {
24966 if (character >= '0' && character <= '9') {
24971 case this.kPunctuation:
24972 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24983 IsLongEnough: function (pwd, size)
24985 return !(pwd == null || isNaN(size) || pwd.length < size);
24988 SpansEnoughCharacterSets: function (word, nb)
24990 if (!this.IsLongEnough(word, nb))
24995 var characterSetChecks = new Array(
24996 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24997 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25000 for (var index = 0; index < word.length; ++index) {
25001 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25002 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25003 characterSetChecks[nCharSet].fResult = true;
25010 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25011 if (characterSetChecks[nCharSet].fResult) {
25016 if (nCharSets < nb) {
25022 ClientSideStrongPassword: function (pwd)
25024 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25027 ClientSideMediumPassword: function (pwd)
25029 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25032 ClientSideWeakPassword: function (pwd)
25034 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25037 })//<script type="text/javascript">
25040 * Based Ext JS Library 1.1.1
25041 * Copyright(c) 2006-2007, Ext JS, LLC.
25047 * @class Roo.HtmlEditorCore
25048 * @extends Roo.Component
25049 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25051 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25054 Roo.HtmlEditorCore = function(config){
25057 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25062 * @event initialize
25063 * Fires when the editor is fully initialized (including the iframe)
25064 * @param {Roo.HtmlEditorCore} this
25069 * Fires when the editor is first receives the focus. Any insertion must wait
25070 * until after this event.
25071 * @param {Roo.HtmlEditorCore} this
25075 * @event beforesync
25076 * Fires before the textarea is updated with content from the editor iframe. Return false
25077 * to cancel the sync.
25078 * @param {Roo.HtmlEditorCore} this
25079 * @param {String} html
25083 * @event beforepush
25084 * Fires before the iframe editor is updated with content from the textarea. Return false
25085 * to cancel the push.
25086 * @param {Roo.HtmlEditorCore} this
25087 * @param {String} html
25092 * Fires when the textarea is updated with content from the editor iframe.
25093 * @param {Roo.HtmlEditorCore} this
25094 * @param {String} html
25099 * Fires when the iframe editor is updated with content from the textarea.
25100 * @param {Roo.HtmlEditorCore} this
25101 * @param {String} html
25106 * @event editorevent
25107 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25108 * @param {Roo.HtmlEditorCore} this
25114 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
25116 // defaults : white / black...
25117 this.applyBlacklists();
25124 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
25128 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
25134 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
25139 * @cfg {Number} height (in pixels)
25143 * @cfg {Number} width (in pixels)
25148 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25151 stylesheets: false,
25156 // private properties
25157 validationEvent : false,
25159 initialized : false,
25161 sourceEditMode : false,
25162 onFocus : Roo.emptyFn,
25164 hideMode:'offsets',
25168 // blacklist + whitelisted elements..
25175 * Protected method that will not generally be called directly. It
25176 * is called when the editor initializes the iframe with HTML contents. Override this method if you
25177 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25179 getDocMarkup : function(){
25183 // inherit styels from page...??
25184 if (this.stylesheets === false) {
25186 Roo.get(document.head).select('style').each(function(node) {
25187 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25190 Roo.get(document.head).select('link').each(function(node) {
25191 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25194 } else if (!this.stylesheets.length) {
25196 st = '<style type="text/css">' +
25197 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25200 for (var i in this.stylesheets) {
25201 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
25206 st += '<style type="text/css">' +
25207 'IMG { cursor: pointer } ' +
25210 var cls = 'roo-htmleditor-body';
25212 if(this.bodyCls.length){
25213 cls += ' ' + this.bodyCls;
25216 return '<html><head>' + st +
25217 //<style type="text/css">' +
25218 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25220 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
25224 onRender : function(ct, position)
25227 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
25228 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
25231 this.el.dom.style.border = '0 none';
25232 this.el.dom.setAttribute('tabIndex', -1);
25233 this.el.addClass('x-hidden hide');
25237 if(Roo.isIE){ // fix IE 1px bogus margin
25238 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25242 this.frameId = Roo.id();
25246 var iframe = this.owner.wrap.createChild({
25248 cls: 'form-control', // bootstrap..
25250 name: this.frameId,
25251 frameBorder : 'no',
25252 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
25257 this.iframe = iframe.dom;
25259 this.assignDocWin();
25261 this.doc.designMode = 'on';
25264 this.doc.write(this.getDocMarkup());
25268 var task = { // must defer to wait for browser to be ready
25270 //console.log("run task?" + this.doc.readyState);
25271 this.assignDocWin();
25272 if(this.doc.body || this.doc.readyState == 'complete'){
25274 this.doc.designMode="on";
25278 Roo.TaskMgr.stop(task);
25279 this.initEditor.defer(10, this);
25286 Roo.TaskMgr.start(task);
25291 onResize : function(w, h)
25293 Roo.log('resize: ' +w + ',' + h );
25294 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
25298 if(typeof w == 'number'){
25300 this.iframe.style.width = w + 'px';
25302 if(typeof h == 'number'){
25304 this.iframe.style.height = h + 'px';
25306 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
25313 * Toggles the editor between standard and source edit mode.
25314 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25316 toggleSourceEdit : function(sourceEditMode){
25318 this.sourceEditMode = sourceEditMode === true;
25320 if(this.sourceEditMode){
25322 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
25325 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
25326 //this.iframe.className = '';
25329 //this.setSize(this.owner.wrap.getSize());
25330 //this.fireEvent('editmodechange', this, this.sourceEditMode);
25337 * Protected method that will not generally be called directly. If you need/want
25338 * custom HTML cleanup, this is the method you should override.
25339 * @param {String} html The HTML to be cleaned
25340 * return {String} The cleaned HTML
25342 cleanHtml : function(html){
25343 html = String(html);
25344 if(html.length > 5){
25345 if(Roo.isSafari){ // strip safari nonsense
25346 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25349 if(html == ' '){
25356 * HTML Editor -> Textarea
25357 * Protected method that will not generally be called directly. Syncs the contents
25358 * of the editor iframe with the textarea.
25360 syncValue : function(){
25361 if(this.initialized){
25362 var bd = (this.doc.body || this.doc.documentElement);
25363 //this.cleanUpPaste(); -- this is done else where and causes havoc..
25364 var html = bd.innerHTML;
25366 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25367 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25369 html = '<div style="'+m[0]+'">' + html + '</div>';
25372 html = this.cleanHtml(html);
25373 // fix up the special chars.. normaly like back quotes in word...
25374 // however we do not want to do this with chinese..
25375 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
25377 var cc = match.charCodeAt();
25379 // Get the character value, handling surrogate pairs
25380 if (match.length == 2) {
25381 // It's a surrogate pair, calculate the Unicode code point
25382 var high = match.charCodeAt(0) - 0xD800;
25383 var low = match.charCodeAt(1) - 0xDC00;
25384 cc = (high * 0x400) + low + 0x10000;
25386 (cc >= 0x4E00 && cc < 0xA000 ) ||
25387 (cc >= 0x3400 && cc < 0x4E00 ) ||
25388 (cc >= 0xf900 && cc < 0xfb00 )
25393 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
25394 return "&#" + cc + ";";
25401 if(this.owner.fireEvent('beforesync', this, html) !== false){
25402 this.el.dom.value = html;
25403 this.owner.fireEvent('sync', this, html);
25409 * Protected method that will not generally be called directly. Pushes the value of the textarea
25410 * into the iframe editor.
25412 pushValue : function(){
25413 if(this.initialized){
25414 var v = this.el.dom.value.trim();
25416 // if(v.length < 1){
25420 if(this.owner.fireEvent('beforepush', this, v) !== false){
25421 var d = (this.doc.body || this.doc.documentElement);
25423 this.cleanUpPaste();
25424 this.el.dom.value = d.innerHTML;
25425 this.owner.fireEvent('push', this, v);
25431 deferFocus : function(){
25432 this.focus.defer(10, this);
25436 focus : function(){
25437 if(this.win && !this.sourceEditMode){
25444 assignDocWin: function()
25446 var iframe = this.iframe;
25449 this.doc = iframe.contentWindow.document;
25450 this.win = iframe.contentWindow;
25452 // if (!Roo.get(this.frameId)) {
25455 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25456 // this.win = Roo.get(this.frameId).dom.contentWindow;
25458 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25462 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25463 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25468 initEditor : function(){
25469 //console.log("INIT EDITOR");
25470 this.assignDocWin();
25474 this.doc.designMode="on";
25476 this.doc.write(this.getDocMarkup());
25479 var dbody = (this.doc.body || this.doc.documentElement);
25480 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25481 // this copies styles from the containing element into thsi one..
25482 // not sure why we need all of this..
25483 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25485 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25486 //ss['background-attachment'] = 'fixed'; // w3c
25487 dbody.bgProperties = 'fixed'; // ie
25488 //Roo.DomHelper.applyStyles(dbody, ss);
25489 Roo.EventManager.on(this.doc, {
25490 //'mousedown': this.onEditorEvent,
25491 'mouseup': this.onEditorEvent,
25492 'dblclick': this.onEditorEvent,
25493 'click': this.onEditorEvent,
25494 'keyup': this.onEditorEvent,
25499 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25501 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25502 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25504 this.initialized = true;
25506 this.owner.fireEvent('initialize', this);
25511 onDestroy : function(){
25517 //for (var i =0; i < this.toolbars.length;i++) {
25518 // // fixme - ask toolbars for heights?
25519 // this.toolbars[i].onDestroy();
25522 //this.wrap.dom.innerHTML = '';
25523 //this.wrap.remove();
25528 onFirstFocus : function(){
25530 this.assignDocWin();
25533 this.activated = true;
25536 if(Roo.isGecko){ // prevent silly gecko errors
25538 var s = this.win.getSelection();
25539 if(!s.focusNode || s.focusNode.nodeType != 3){
25540 var r = s.getRangeAt(0);
25541 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25546 this.execCmd('useCSS', true);
25547 this.execCmd('styleWithCSS', false);
25550 this.owner.fireEvent('activate', this);
25554 adjustFont: function(btn){
25555 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25556 //if(Roo.isSafari){ // safari
25559 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25560 if(Roo.isSafari){ // safari
25561 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25562 v = (v < 10) ? 10 : v;
25563 v = (v > 48) ? 48 : v;
25564 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25569 v = Math.max(1, v+adjust);
25571 this.execCmd('FontSize', v );
25574 onEditorEvent : function(e)
25576 this.owner.fireEvent('editorevent', this, e);
25577 // this.updateToolbar();
25578 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25581 insertTag : function(tg)
25583 // could be a bit smarter... -> wrap the current selected tRoo..
25584 if (tg.toLowerCase() == 'span' ||
25585 tg.toLowerCase() == 'code' ||
25586 tg.toLowerCase() == 'sup' ||
25587 tg.toLowerCase() == 'sub'
25590 range = this.createRange(this.getSelection());
25591 var wrappingNode = this.doc.createElement(tg.toLowerCase());
25592 wrappingNode.appendChild(range.extractContents());
25593 range.insertNode(wrappingNode);
25600 this.execCmd("formatblock", tg);
25604 insertText : function(txt)
25608 var range = this.createRange();
25609 range.deleteContents();
25610 //alert(Sender.getAttribute('label'));
25612 range.insertNode(this.doc.createTextNode(txt));
25618 * Executes a Midas editor command on the editor document and performs necessary focus and
25619 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25620 * @param {String} cmd The Midas command
25621 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25623 relayCmd : function(cmd, value){
25625 this.execCmd(cmd, value);
25626 this.owner.fireEvent('editorevent', this);
25627 //this.updateToolbar();
25628 this.owner.deferFocus();
25632 * Executes a Midas editor command directly on the editor document.
25633 * For visual commands, you should use {@link #relayCmd} instead.
25634 * <b>This should only be called after the editor is initialized.</b>
25635 * @param {String} cmd The Midas command
25636 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25638 execCmd : function(cmd, value){
25639 this.doc.execCommand(cmd, false, value === undefined ? null : value);
25646 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25648 * @param {String} text | dom node..
25650 insertAtCursor : function(text)
25653 if(!this.activated){
25659 var r = this.doc.selection.createRange();
25670 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25674 // from jquery ui (MIT licenced)
25676 var win = this.win;
25678 if (win.getSelection && win.getSelection().getRangeAt) {
25679 range = win.getSelection().getRangeAt(0);
25680 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25681 range.insertNode(node);
25682 } else if (win.document.selection && win.document.selection.createRange) {
25683 // no firefox support
25684 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25685 win.document.selection.createRange().pasteHTML(txt);
25687 // no firefox support
25688 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25689 this.execCmd('InsertHTML', txt);
25698 mozKeyPress : function(e){
25700 var c = e.getCharCode(), cmd;
25703 c = String.fromCharCode(c).toLowerCase();
25717 this.cleanUpPaste.defer(100, this);
25725 e.preventDefault();
25733 fixKeys : function(){ // load time branching for fastest keydown performance
25735 return function(e){
25736 var k = e.getKey(), r;
25739 r = this.doc.selection.createRange();
25742 r.pasteHTML('    ');
25749 r = this.doc.selection.createRange();
25751 var target = r.parentElement();
25752 if(!target || target.tagName.toLowerCase() != 'li'){
25754 r.pasteHTML('<br />');
25760 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25761 this.cleanUpPaste.defer(100, this);
25767 }else if(Roo.isOpera){
25768 return function(e){
25769 var k = e.getKey();
25773 this.execCmd('InsertHTML','    ');
25776 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25777 this.cleanUpPaste.defer(100, this);
25782 }else if(Roo.isSafari){
25783 return function(e){
25784 var k = e.getKey();
25788 this.execCmd('InsertText','\t');
25792 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25793 this.cleanUpPaste.defer(100, this);
25801 getAllAncestors: function()
25803 var p = this.getSelectedNode();
25806 a.push(p); // push blank onto stack..
25807 p = this.getParentElement();
25811 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25815 a.push(this.doc.body);
25819 lastSelNode : false,
25822 getSelection : function()
25824 this.assignDocWin();
25825 return Roo.isIE ? this.doc.selection : this.win.getSelection();
25828 getSelectedNode: function()
25830 // this may only work on Gecko!!!
25832 // should we cache this!!!!
25837 var range = this.createRange(this.getSelection()).cloneRange();
25840 var parent = range.parentElement();
25842 var testRange = range.duplicate();
25843 testRange.moveToElementText(parent);
25844 if (testRange.inRange(range)) {
25847 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25850 parent = parent.parentElement;
25855 // is ancestor a text element.
25856 var ac = range.commonAncestorContainer;
25857 if (ac.nodeType == 3) {
25858 ac = ac.parentNode;
25861 var ar = ac.childNodes;
25864 var other_nodes = [];
25865 var has_other_nodes = false;
25866 for (var i=0;i<ar.length;i++) {
25867 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
25870 // fullly contained node.
25872 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25877 // probably selected..
25878 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25879 other_nodes.push(ar[i]);
25883 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
25888 has_other_nodes = true;
25890 if (!nodes.length && other_nodes.length) {
25891 nodes= other_nodes;
25893 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25899 createRange: function(sel)
25901 // this has strange effects when using with
25902 // top toolbar - not sure if it's a great idea.
25903 //this.editor.contentWindow.focus();
25904 if (typeof sel != "undefined") {
25906 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25908 return this.doc.createRange();
25911 return this.doc.createRange();
25914 getParentElement: function()
25917 this.assignDocWin();
25918 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25920 var range = this.createRange(sel);
25923 var p = range.commonAncestorContainer;
25924 while (p.nodeType == 3) { // text node
25935 * Range intersection.. the hard stuff...
25939 * [ -- selected range --- ]
25943 * if end is before start or hits it. fail.
25944 * if start is after end or hits it fail.
25946 * if either hits (but other is outside. - then it's not
25952 // @see http://www.thismuchiknow.co.uk/?p=64.
25953 rangeIntersectsNode : function(range, node)
25955 var nodeRange = node.ownerDocument.createRange();
25957 nodeRange.selectNode(node);
25959 nodeRange.selectNodeContents(node);
25962 var rangeStartRange = range.cloneRange();
25963 rangeStartRange.collapse(true);
25965 var rangeEndRange = range.cloneRange();
25966 rangeEndRange.collapse(false);
25968 var nodeStartRange = nodeRange.cloneRange();
25969 nodeStartRange.collapse(true);
25971 var nodeEndRange = nodeRange.cloneRange();
25972 nodeEndRange.collapse(false);
25974 return rangeStartRange.compareBoundaryPoints(
25975 Range.START_TO_START, nodeEndRange) == -1 &&
25976 rangeEndRange.compareBoundaryPoints(
25977 Range.START_TO_START, nodeStartRange) == 1;
25981 rangeCompareNode : function(range, node)
25983 var nodeRange = node.ownerDocument.createRange();
25985 nodeRange.selectNode(node);
25987 nodeRange.selectNodeContents(node);
25991 range.collapse(true);
25993 nodeRange.collapse(true);
25995 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25996 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
25998 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26000 var nodeIsBefore = ss == 1;
26001 var nodeIsAfter = ee == -1;
26003 if (nodeIsBefore && nodeIsAfter) {
26006 if (!nodeIsBefore && nodeIsAfter) {
26007 return 1; //right trailed.
26010 if (nodeIsBefore && !nodeIsAfter) {
26011 return 2; // left trailed.
26017 // private? - in a new class?
26018 cleanUpPaste : function()
26020 // cleans up the whole document..
26021 Roo.log('cleanuppaste');
26023 this.cleanUpChildren(this.doc.body);
26024 var clean = this.cleanWordChars(this.doc.body.innerHTML);
26025 if (clean != this.doc.body.innerHTML) {
26026 this.doc.body.innerHTML = clean;
26031 cleanWordChars : function(input) {// change the chars to hex code
26032 var he = Roo.HtmlEditorCore;
26034 var output = input;
26035 Roo.each(he.swapCodes, function(sw) {
26036 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26038 output = output.replace(swapper, sw[1]);
26045 cleanUpChildren : function (n)
26047 if (!n.childNodes.length) {
26050 for (var i = n.childNodes.length-1; i > -1 ; i--) {
26051 this.cleanUpChild(n.childNodes[i]);
26058 cleanUpChild : function (node)
26061 //console.log(node);
26062 if (node.nodeName == "#text") {
26063 // clean up silly Windows -- stuff?
26066 if (node.nodeName == "#comment") {
26067 node.parentNode.removeChild(node);
26068 // clean up silly Windows -- stuff?
26071 var lcname = node.tagName.toLowerCase();
26072 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26073 // whitelist of tags..
26075 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26077 node.parentNode.removeChild(node);
26082 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
26084 // spans with no attributes - just remove them..
26085 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
26086 remove_keep_children = true;
26089 // remove <a name=....> as rendering on yahoo mailer is borked with this.
26090 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26092 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26093 // remove_keep_children = true;
26096 if (remove_keep_children) {
26097 this.cleanUpChildren(node);
26098 // inserts everything just before this node...
26099 while (node.childNodes.length) {
26100 var cn = node.childNodes[0];
26101 node.removeChild(cn);
26102 node.parentNode.insertBefore(cn, node);
26104 node.parentNode.removeChild(node);
26108 if (!node.attributes || !node.attributes.length) {
26113 this.cleanUpChildren(node);
26117 function cleanAttr(n,v)
26120 if (v.match(/^\./) || v.match(/^\//)) {
26123 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
26126 if (v.match(/^#/)) {
26129 if (v.match(/^\{/)) { // allow template editing.
26132 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26133 node.removeAttribute(n);
26137 var cwhite = this.cwhite;
26138 var cblack = this.cblack;
26140 function cleanStyle(n,v)
26142 if (v.match(/expression/)) { //XSS?? should we even bother..
26143 node.removeAttribute(n);
26147 var parts = v.split(/;/);
26150 Roo.each(parts, function(p) {
26151 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26155 var l = p.split(':').shift().replace(/\s+/g,'');
26156 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26158 if ( cwhite.length && cblack.indexOf(l) > -1) {
26159 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26160 //node.removeAttribute(n);
26164 // only allow 'c whitelisted system attributes'
26165 if ( cwhite.length && cwhite.indexOf(l) < 0) {
26166 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26167 //node.removeAttribute(n);
26177 if (clean.length) {
26178 node.setAttribute(n, clean.join(';'));
26180 node.removeAttribute(n);
26186 for (var i = node.attributes.length-1; i > -1 ; i--) {
26187 var a = node.attributes[i];
26190 if (a.name.toLowerCase().substr(0,2)=='on') {
26191 node.removeAttribute(a.name);
26194 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
26195 node.removeAttribute(a.name);
26198 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
26199 cleanAttr(a.name,a.value); // fixme..
26202 if (a.name == 'style') {
26203 cleanStyle(a.name,a.value);
26206 /// clean up MS crap..
26207 // tecnically this should be a list of valid class'es..
26210 if (a.name == 'class') {
26211 if (a.value.match(/^Mso/)) {
26212 node.removeAttribute('class');
26215 if (a.value.match(/^body$/)) {
26216 node.removeAttribute('class');
26227 this.cleanUpChildren(node);
26233 * Clean up MS wordisms...
26235 cleanWord : function(node)
26238 this.cleanWord(this.doc.body);
26243 node.nodeName == 'SPAN' &&
26244 !node.hasAttributes() &&
26245 node.childNodes.length == 1 &&
26246 node.firstChild.nodeName == "#text"
26248 var textNode = node.firstChild;
26249 node.removeChild(textNode);
26250 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
26251 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26253 node.parentNode.insertBefore(textNode, node);
26254 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
26255 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26257 node.parentNode.removeChild(node);
26260 if (node.nodeName == "#text") {
26261 // clean up silly Windows -- stuff?
26264 if (node.nodeName == "#comment") {
26265 node.parentNode.removeChild(node);
26266 // clean up silly Windows -- stuff?
26270 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26271 node.parentNode.removeChild(node);
26274 //Roo.log(node.tagName);
26275 // remove - but keep children..
26276 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26277 //Roo.log('-- removed');
26278 while (node.childNodes.length) {
26279 var cn = node.childNodes[0];
26280 node.removeChild(cn);
26281 node.parentNode.insertBefore(cn, node);
26282 // move node to parent - and clean it..
26283 this.cleanWord(cn);
26285 node.parentNode.removeChild(node);
26286 /// no need to iterate chidlren = it's got none..
26287 //this.iterateChildren(node, this.cleanWord);
26291 if (node.className.length) {
26293 var cn = node.className.split(/\W+/);
26295 Roo.each(cn, function(cls) {
26296 if (cls.match(/Mso[a-zA-Z]+/)) {
26301 node.className = cna.length ? cna.join(' ') : '';
26303 node.removeAttribute("class");
26307 if (node.hasAttribute("lang")) {
26308 node.removeAttribute("lang");
26311 if (node.hasAttribute("style")) {
26313 var styles = node.getAttribute("style").split(";");
26315 Roo.each(styles, function(s) {
26316 if (!s.match(/:/)) {
26319 var kv = s.split(":");
26320 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26323 // what ever is left... we allow.
26326 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26327 if (!nstyle.length) {
26328 node.removeAttribute('style');
26331 this.iterateChildren(node, this.cleanWord);
26337 * iterateChildren of a Node, calling fn each time, using this as the scole..
26338 * @param {DomNode} node node to iterate children of.
26339 * @param {Function} fn method of this class to call on each item.
26341 iterateChildren : function(node, fn)
26343 if (!node.childNodes.length) {
26346 for (var i = node.childNodes.length-1; i > -1 ; i--) {
26347 fn.call(this, node.childNodes[i])
26353 * cleanTableWidths.
26355 * Quite often pasting from word etc.. results in tables with column and widths.
26356 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26359 cleanTableWidths : function(node)
26364 this.cleanTableWidths(this.doc.body);
26369 if (node.nodeName == "#text" || node.nodeName == "#comment") {
26372 Roo.log(node.tagName);
26373 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
26374 this.iterateChildren(node, this.cleanTableWidths);
26377 if (node.hasAttribute('width')) {
26378 node.removeAttribute('width');
26382 if (node.hasAttribute("style")) {
26385 var styles = node.getAttribute("style").split(";");
26387 Roo.each(styles, function(s) {
26388 if (!s.match(/:/)) {
26391 var kv = s.split(":");
26392 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26395 // what ever is left... we allow.
26398 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26399 if (!nstyle.length) {
26400 node.removeAttribute('style');
26404 this.iterateChildren(node, this.cleanTableWidths);
26412 domToHTML : function(currentElement, depth, nopadtext) {
26414 depth = depth || 0;
26415 nopadtext = nopadtext || false;
26417 if (!currentElement) {
26418 return this.domToHTML(this.doc.body);
26421 //Roo.log(currentElement);
26423 var allText = false;
26424 var nodeName = currentElement.nodeName;
26425 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
26427 if (nodeName == '#text') {
26429 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
26434 if (nodeName != 'BODY') {
26437 // Prints the node tagName, such as <A>, <IMG>, etc
26440 for(i = 0; i < currentElement.attributes.length;i++) {
26442 var aname = currentElement.attributes.item(i).name;
26443 if (!currentElement.attributes.item(i).value.length) {
26446 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
26449 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
26458 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
26461 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
26466 // Traverse the tree
26468 var currentElementChild = currentElement.childNodes.item(i);
26469 var allText = true;
26470 var innerHTML = '';
26472 while (currentElementChild) {
26473 // Formatting code (indent the tree so it looks nice on the screen)
26474 var nopad = nopadtext;
26475 if (lastnode == 'SPAN') {
26479 if (currentElementChild.nodeName == '#text') {
26480 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
26481 toadd = nopadtext ? toadd : toadd.trim();
26482 if (!nopad && toadd.length > 80) {
26483 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
26485 innerHTML += toadd;
26488 currentElementChild = currentElement.childNodes.item(i);
26494 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
26496 // Recursively traverse the tree structure of the child node
26497 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
26498 lastnode = currentElementChild.nodeName;
26500 currentElementChild=currentElement.childNodes.item(i);
26506 // The remaining code is mostly for formatting the tree
26507 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
26512 ret+= "</"+tagName+">";
26518 applyBlacklists : function()
26520 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
26521 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
26525 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
26526 if (b.indexOf(tag) > -1) {
26529 this.white.push(tag);
26533 Roo.each(w, function(tag) {
26534 if (b.indexOf(tag) > -1) {
26537 if (this.white.indexOf(tag) > -1) {
26540 this.white.push(tag);
26545 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
26546 if (w.indexOf(tag) > -1) {
26549 this.black.push(tag);
26553 Roo.each(b, function(tag) {
26554 if (w.indexOf(tag) > -1) {
26557 if (this.black.indexOf(tag) > -1) {
26560 this.black.push(tag);
26565 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
26566 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
26570 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
26571 if (b.indexOf(tag) > -1) {
26574 this.cwhite.push(tag);
26578 Roo.each(w, function(tag) {
26579 if (b.indexOf(tag) > -1) {
26582 if (this.cwhite.indexOf(tag) > -1) {
26585 this.cwhite.push(tag);
26590 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
26591 if (w.indexOf(tag) > -1) {
26594 this.cblack.push(tag);
26598 Roo.each(b, function(tag) {
26599 if (w.indexOf(tag) > -1) {
26602 if (this.cblack.indexOf(tag) > -1) {
26605 this.cblack.push(tag);
26610 setStylesheets : function(stylesheets)
26612 if(typeof(stylesheets) == 'string'){
26613 Roo.get(this.iframe.contentDocument.head).createChild({
26615 rel : 'stylesheet',
26624 Roo.each(stylesheets, function(s) {
26629 Roo.get(_this.iframe.contentDocument.head).createChild({
26631 rel : 'stylesheet',
26640 removeStylesheets : function()
26644 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
26649 setStyle : function(style)
26651 Roo.get(this.iframe.contentDocument.head).createChild({
26660 // hide stuff that is not compatible
26674 * @event specialkey
26678 * @cfg {String} fieldClass @hide
26681 * @cfg {String} focusClass @hide
26684 * @cfg {String} autoCreate @hide
26687 * @cfg {String} inputType @hide
26690 * @cfg {String} invalidClass @hide
26693 * @cfg {String} invalidText @hide
26696 * @cfg {String} msgFx @hide
26699 * @cfg {String} validateOnBlur @hide
26703 Roo.HtmlEditorCore.white = [
26704 'area', 'br', 'img', 'input', 'hr', 'wbr',
26706 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
26707 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
26708 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
26709 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
26710 'table', 'ul', 'xmp',
26712 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
26715 'dir', 'menu', 'ol', 'ul', 'dl',
26721 Roo.HtmlEditorCore.black = [
26722 // 'embed', 'object', // enable - backend responsiblity to clean thiese
26724 'base', 'basefont', 'bgsound', 'blink', 'body',
26725 'frame', 'frameset', 'head', 'html', 'ilayer',
26726 'iframe', 'layer', 'link', 'meta', 'object',
26727 'script', 'style' ,'title', 'xml' // clean later..
26729 Roo.HtmlEditorCore.clean = [
26730 'script', 'style', 'title', 'xml'
26732 Roo.HtmlEditorCore.remove = [
26737 Roo.HtmlEditorCore.ablack = [
26741 Roo.HtmlEditorCore.aclean = [
26742 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
26746 Roo.HtmlEditorCore.pwhite= [
26747 'http', 'https', 'mailto'
26750 // white listed style attributes.
26751 Roo.HtmlEditorCore.cwhite= [
26752 // 'text-align', /// default is to allow most things..
26758 // black listed style attributes.
26759 Roo.HtmlEditorCore.cblack= [
26760 // 'font-size' -- this can be set by the project
26764 Roo.HtmlEditorCore.swapCodes =[
26765 [ 8211, "–" ],
26766 [ 8212, "—" ],
26783 * @class Roo.bootstrap.HtmlEditor
26784 * @extends Roo.bootstrap.TextArea
26785 * Bootstrap HtmlEditor class
26788 * Create a new HtmlEditor
26789 * @param {Object} config The config object
26792 Roo.bootstrap.HtmlEditor = function(config){
26793 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
26794 if (!this.toolbars) {
26795 this.toolbars = [];
26798 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26801 * @event initialize
26802 * Fires when the editor is fully initialized (including the iframe)
26803 * @param {HtmlEditor} this
26808 * Fires when the editor is first receives the focus. Any insertion must wait
26809 * until after this event.
26810 * @param {HtmlEditor} this
26814 * @event beforesync
26815 * Fires before the textarea is updated with content from the editor iframe. Return false
26816 * to cancel the sync.
26817 * @param {HtmlEditor} this
26818 * @param {String} html
26822 * @event beforepush
26823 * Fires before the iframe editor is updated with content from the textarea. Return false
26824 * to cancel the push.
26825 * @param {HtmlEditor} this
26826 * @param {String} html
26831 * Fires when the textarea is updated with content from the editor iframe.
26832 * @param {HtmlEditor} this
26833 * @param {String} html
26838 * Fires when the iframe editor is updated with content from the textarea.
26839 * @param {HtmlEditor} this
26840 * @param {String} html
26844 * @event editmodechange
26845 * Fires when the editor switches edit modes
26846 * @param {HtmlEditor} this
26847 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26849 editmodechange: true,
26851 * @event editorevent
26852 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26853 * @param {HtmlEditor} this
26857 * @event firstfocus
26858 * Fires when on first focus - needed by toolbars..
26859 * @param {HtmlEditor} this
26864 * Auto save the htmlEditor value as a file into Events
26865 * @param {HtmlEditor} this
26869 * @event savedpreview
26870 * preview the saved version of htmlEditor
26871 * @param {HtmlEditor} this
26878 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
26882 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26887 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
26892 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
26897 * @cfg {Number} height (in pixels)
26901 * @cfg {Number} width (in pixels)
26906 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26909 stylesheets: false,
26914 // private properties
26915 validationEvent : false,
26917 initialized : false,
26920 onFocus : Roo.emptyFn,
26922 hideMode:'offsets',
26924 tbContainer : false,
26928 toolbarContainer :function() {
26929 return this.wrap.select('.x-html-editor-tb',true).first();
26933 * Protected method that will not generally be called directly. It
26934 * is called when the editor creates its toolbar. Override this method if you need to
26935 * add custom toolbar buttons.
26936 * @param {HtmlEditor} editor
26938 createToolbar : function(){
26939 Roo.log('renewing');
26940 Roo.log("create toolbars");
26942 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26943 this.toolbars[0].render(this.toolbarContainer());
26947 // if (!editor.toolbars || !editor.toolbars.length) {
26948 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26951 // for (var i =0 ; i < editor.toolbars.length;i++) {
26952 // editor.toolbars[i] = Roo.factory(
26953 // typeof(editor.toolbars[i]) == 'string' ?
26954 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
26955 // Roo.bootstrap.HtmlEditor);
26956 // editor.toolbars[i].init(editor);
26962 onRender : function(ct, position)
26964 // Roo.log("Call onRender: " + this.xtype);
26966 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26968 this.wrap = this.inputEl().wrap({
26969 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26972 this.editorcore.onRender(ct, position);
26974 if (this.resizable) {
26975 this.resizeEl = new Roo.Resizable(this.wrap, {
26979 minHeight : this.height,
26980 height: this.height,
26981 handles : this.resizable,
26984 resize : function(r, w, h) {
26985 _t.onResize(w,h); // -something
26991 this.createToolbar(this);
26994 if(!this.width && this.resizable){
26995 this.setSize(this.wrap.getSize());
26997 if (this.resizeEl) {
26998 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26999 // should trigger onReize..
27005 onResize : function(w, h)
27007 Roo.log('resize: ' +w + ',' + h );
27008 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
27012 if(this.inputEl() ){
27013 if(typeof w == 'number'){
27014 var aw = w - this.wrap.getFrameWidth('lr');
27015 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27018 if(typeof h == 'number'){
27019 var tbh = -11; // fixme it needs to tool bar size!
27020 for (var i =0; i < this.toolbars.length;i++) {
27021 // fixme - ask toolbars for heights?
27022 tbh += this.toolbars[i].el.getHeight();
27023 //if (this.toolbars[i].footer) {
27024 // tbh += this.toolbars[i].footer.el.getHeight();
27032 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27033 ah -= 5; // knock a few pixes off for look..
27034 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27038 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27039 this.editorcore.onResize(ew,eh);
27044 * Toggles the editor between standard and source edit mode.
27045 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27047 toggleSourceEdit : function(sourceEditMode)
27049 this.editorcore.toggleSourceEdit(sourceEditMode);
27051 if(this.editorcore.sourceEditMode){
27052 Roo.log('editor - showing textarea');
27055 // Roo.log(this.syncValue());
27057 this.inputEl().removeClass(['hide', 'x-hidden']);
27058 this.inputEl().dom.removeAttribute('tabIndex');
27059 this.inputEl().focus();
27061 Roo.log('editor - hiding textarea');
27063 // Roo.log(this.pushValue());
27066 this.inputEl().addClass(['hide', 'x-hidden']);
27067 this.inputEl().dom.setAttribute('tabIndex', -1);
27068 //this.deferFocus();
27071 if(this.resizable){
27072 this.setSize(this.wrap.getSize());
27075 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27078 // private (for BoxComponent)
27079 adjustSize : Roo.BoxComponent.prototype.adjustSize,
27081 // private (for BoxComponent)
27082 getResizeEl : function(){
27086 // private (for BoxComponent)
27087 getPositionEl : function(){
27092 initEvents : function(){
27093 this.originalValue = this.getValue();
27097 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27100 // markInvalid : Roo.emptyFn,
27102 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27105 // clearInvalid : Roo.emptyFn,
27107 setValue : function(v){
27108 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
27109 this.editorcore.pushValue();
27114 deferFocus : function(){
27115 this.focus.defer(10, this);
27119 focus : function(){
27120 this.editorcore.focus();
27126 onDestroy : function(){
27132 for (var i =0; i < this.toolbars.length;i++) {
27133 // fixme - ask toolbars for heights?
27134 this.toolbars[i].onDestroy();
27137 this.wrap.dom.innerHTML = '';
27138 this.wrap.remove();
27143 onFirstFocus : function(){
27144 //Roo.log("onFirstFocus");
27145 this.editorcore.onFirstFocus();
27146 for (var i =0; i < this.toolbars.length;i++) {
27147 this.toolbars[i].onFirstFocus();
27153 syncValue : function()
27155 this.editorcore.syncValue();
27158 pushValue : function()
27160 this.editorcore.pushValue();
27164 // hide stuff that is not compatible
27178 * @event specialkey
27182 * @cfg {String} fieldClass @hide
27185 * @cfg {String} focusClass @hide
27188 * @cfg {String} autoCreate @hide
27191 * @cfg {String} inputType @hide
27195 * @cfg {String} invalidText @hide
27198 * @cfg {String} msgFx @hide
27201 * @cfg {String} validateOnBlur @hide
27210 Roo.namespace('Roo.bootstrap.htmleditor');
27212 * @class Roo.bootstrap.HtmlEditorToolbar1
27218 new Roo.bootstrap.HtmlEditor({
27221 new Roo.bootstrap.HtmlEditorToolbar1({
27222 disable : { fonts: 1 , format: 1, ..., ... , ...],
27228 * @cfg {Object} disable List of elements to disable..
27229 * @cfg {Array} btns List of additional buttons.
27233 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27236 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
27239 Roo.apply(this, config);
27241 // default disabled, based on 'good practice'..
27242 this.disable = this.disable || {};
27243 Roo.applyIf(this.disable, {
27246 specialElements : true
27248 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
27250 this.editor = config.editor;
27251 this.editorcore = config.editor.editorcore;
27253 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
27255 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27256 // dont call parent... till later.
27258 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
27263 editorcore : false,
27268 "h1","h2","h3","h4","h5","h6",
27270 "abbr", "acronym", "address", "cite", "samp", "var",
27274 onRender : function(ct, position)
27276 // Roo.log("Call onRender: " + this.xtype);
27278 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
27280 this.el.dom.style.marginBottom = '0';
27282 var editorcore = this.editorcore;
27283 var editor= this.editor;
27286 var btn = function(id,cmd , toggle, handler, html){
27288 var event = toggle ? 'toggle' : 'click';
27293 xns: Roo.bootstrap,
27297 enableToggle:toggle !== false,
27299 pressed : toggle ? false : null,
27302 a.listeners[toggle ? 'toggle' : 'click'] = function() {
27303 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
27309 // var cb_box = function...
27314 xns: Roo.bootstrap,
27319 xns: Roo.bootstrap,
27323 Roo.each(this.formats, function(f) {
27324 style.menu.items.push({
27326 xns: Roo.bootstrap,
27327 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
27332 editorcore.insertTag(this.tagname);
27339 children.push(style);
27341 btn('bold',false,true);
27342 btn('italic',false,true);
27343 btn('align-left', 'justifyleft',true);
27344 btn('align-center', 'justifycenter',true);
27345 btn('align-right' , 'justifyright',true);
27346 btn('link', false, false, function(btn) {
27347 //Roo.log("create link?");
27348 var url = prompt(this.createLinkText, this.defaultLinkValue);
27349 if(url && url != 'http:/'+'/'){
27350 this.editorcore.relayCmd('createlink', url);
27353 btn('list','insertunorderedlist',true);
27354 btn('pencil', false,true, function(btn){
27356 this.toggleSourceEdit(btn.pressed);
27359 if (this.editor.btns.length > 0) {
27360 for (var i = 0; i<this.editor.btns.length; i++) {
27361 children.push(this.editor.btns[i]);
27369 xns: Roo.bootstrap,
27374 xns: Roo.bootstrap,
27379 cog.menu.items.push({
27381 xns: Roo.bootstrap,
27382 html : Clean styles,
27387 editorcore.insertTag(this.tagname);
27396 this.xtype = 'NavSimplebar';
27398 for(var i=0;i< children.length;i++) {
27400 this.buttons.add(this.addxtypeChild(children[i]));
27404 editor.on('editorevent', this.updateToolbar, this);
27406 onBtnClick : function(id)
27408 this.editorcore.relayCmd(id);
27409 this.editorcore.focus();
27413 * Protected method that will not generally be called directly. It triggers
27414 * a toolbar update by reading the markup state of the current selection in the editor.
27416 updateToolbar: function(){
27418 if(!this.editorcore.activated){
27419 this.editor.onFirstFocus(); // is this neeed?
27423 var btns = this.buttons;
27424 var doc = this.editorcore.doc;
27425 btns.get('bold').setActive(doc.queryCommandState('bold'));
27426 btns.get('italic').setActive(doc.queryCommandState('italic'));
27427 //btns.get('underline').setActive(doc.queryCommandState('underline'));
27429 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
27430 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
27431 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
27433 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
27434 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
27437 var ans = this.editorcore.getAllAncestors();
27438 if (this.formatCombo) {
27441 var store = this.formatCombo.store;
27442 this.formatCombo.setValue("");
27443 for (var i =0; i < ans.length;i++) {
27444 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27446 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27454 // hides menus... - so this cant be on a menu...
27455 Roo.bootstrap.MenuMgr.hideAll();
27457 Roo.bootstrap.MenuMgr.hideAll();
27458 //this.editorsyncValue();
27460 onFirstFocus: function() {
27461 this.buttons.each(function(item){
27465 toggleSourceEdit : function(sourceEditMode){
27468 if(sourceEditMode){
27469 Roo.log("disabling buttons");
27470 this.buttons.each( function(item){
27471 if(item.cmd != 'pencil'){
27477 Roo.log("enabling buttons");
27478 if(this.editorcore.initialized){
27479 this.buttons.each( function(item){
27485 Roo.log("calling toggole on editor");
27486 // tell the editor that it's been pressed..
27487 this.editor.toggleSourceEdit(sourceEditMode);
27501 * @class Roo.bootstrap.Markdown
27502 * @extends Roo.bootstrap.TextArea
27503 * Bootstrap Showdown editable area
27504 * @cfg {string} content
27507 * Create a new Showdown
27510 Roo.bootstrap.Markdown = function(config){
27511 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
27515 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
27519 initEvents : function()
27522 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
27523 this.markdownEl = this.el.createChild({
27524 cls : 'roo-markdown-area'
27526 this.inputEl().addClass('d-none');
27527 if (this.getValue() == '') {
27528 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
27531 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
27533 this.markdownEl.on('click', this.toggleTextEdit, this);
27534 this.on('blur', this.toggleTextEdit, this);
27535 this.on('specialkey', this.resizeTextArea, this);
27538 toggleTextEdit : function()
27540 var sh = this.markdownEl.getHeight();
27541 this.inputEl().addClass('d-none');
27542 this.markdownEl.addClass('d-none');
27543 if (!this.editing) {
27545 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
27546 this.inputEl().removeClass('d-none');
27547 this.inputEl().focus();
27548 this.editing = true;
27551 // show showdown...
27552 this.updateMarkdown();
27553 this.markdownEl.removeClass('d-none');
27554 this.editing = false;
27557 updateMarkdown : function()
27559 if (this.getValue() == '') {
27560 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
27564 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
27567 resizeTextArea: function () {
27570 Roo.log([sh, this.getValue().split("\n").length * 30]);
27571 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
27573 setValue : function(val)
27575 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
27576 if (!this.editing) {
27577 this.updateMarkdown();
27583 if (!this.editing) {
27584 this.toggleTextEdit();
27592 * Ext JS Library 1.1.1
27593 * Copyright(c) 2006-2007, Ext JS, LLC.
27595 * Originally Released Under LGPL - original licence link has changed is not relivant.
27598 * <script type="text/javascript">
27602 * @class Roo.bootstrap.PagingToolbar
27603 * @extends Roo.bootstrap.NavSimplebar
27604 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27606 * Create a new PagingToolbar
27607 * @param {Object} config The config object
27608 * @param {Roo.data.Store} store
27610 Roo.bootstrap.PagingToolbar = function(config)
27612 // old args format still supported... - xtype is prefered..
27613 // created from xtype...
27615 this.ds = config.dataSource;
27617 if (config.store && !this.ds) {
27618 this.store= Roo.factory(config.store, Roo.data);
27619 this.ds = this.store;
27620 this.ds.xmodule = this.xmodule || false;
27623 this.toolbarItems = [];
27624 if (config.items) {
27625 this.toolbarItems = config.items;
27628 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27633 this.bind(this.ds);
27636 if (Roo.bootstrap.version == 4) {
27637 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27639 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27644 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27646 * @cfg {Roo.data.Store} dataSource
27647 * The underlying data store providing the paged data
27650 * @cfg {String/HTMLElement/Element} container
27651 * container The id or element that will contain the toolbar
27654 * @cfg {Boolean} displayInfo
27655 * True to display the displayMsg (defaults to false)
27658 * @cfg {Number} pageSize
27659 * The number of records to display per page (defaults to 20)
27663 * @cfg {String} displayMsg
27664 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27666 displayMsg : 'Displaying {0} - {1} of {2}',
27668 * @cfg {String} emptyMsg
27669 * The message to display when no records are found (defaults to "No data to display")
27671 emptyMsg : 'No data to display',
27673 * Customizable piece of the default paging text (defaults to "Page")
27676 beforePageText : "Page",
27678 * Customizable piece of the default paging text (defaults to "of %0")
27681 afterPageText : "of {0}",
27683 * Customizable piece of the default paging text (defaults to "First Page")
27686 firstText : "First Page",
27688 * Customizable piece of the default paging text (defaults to "Previous Page")
27691 prevText : "Previous Page",
27693 * Customizable piece of the default paging text (defaults to "Next Page")
27696 nextText : "Next Page",
27698 * Customizable piece of the default paging text (defaults to "Last Page")
27701 lastText : "Last Page",
27703 * Customizable piece of the default paging text (defaults to "Refresh")
27706 refreshText : "Refresh",
27710 onRender : function(ct, position)
27712 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27713 this.navgroup.parentId = this.id;
27714 this.navgroup.onRender(this.el, null);
27715 // add the buttons to the navgroup
27717 if(this.displayInfo){
27718 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27719 this.displayEl = this.el.select('.x-paging-info', true).first();
27720 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27721 // this.displayEl = navel.el.select('span',true).first();
27727 Roo.each(_this.buttons, function(e){ // this might need to use render????
27728 Roo.factory(e).render(_this.el);
27732 Roo.each(_this.toolbarItems, function(e) {
27733 _this.navgroup.addItem(e);
27737 this.first = this.navgroup.addItem({
27738 tooltip: this.firstText,
27739 cls: "prev btn-outline-secondary",
27740 html : ' <i class="fa fa-step-backward"></i>',
27742 preventDefault: true,
27743 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27746 this.prev = this.navgroup.addItem({
27747 tooltip: this.prevText,
27748 cls: "prev btn-outline-secondary",
27749 html : ' <i class="fa fa-backward"></i>',
27751 preventDefault: true,
27752 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
27754 //this.addSeparator();
27757 var field = this.navgroup.addItem( {
27759 cls : 'x-paging-position btn-outline-secondary',
27761 html : this.beforePageText +
27762 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27763 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
27766 this.field = field.el.select('input', true).first();
27767 this.field.on("keydown", this.onPagingKeydown, this);
27768 this.field.on("focus", function(){this.dom.select();});
27771 this.afterTextEl = field.el.select('.x-paging-after',true).first();
27772 //this.field.setHeight(18);
27773 //this.addSeparator();
27774 this.next = this.navgroup.addItem({
27775 tooltip: this.nextText,
27776 cls: "next btn-outline-secondary",
27777 html : ' <i class="fa fa-forward"></i>',
27779 preventDefault: true,
27780 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
27782 this.last = this.navgroup.addItem({
27783 tooltip: this.lastText,
27784 html : ' <i class="fa fa-step-forward"></i>',
27785 cls: "next btn-outline-secondary",
27787 preventDefault: true,
27788 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
27790 //this.addSeparator();
27791 this.loading = this.navgroup.addItem({
27792 tooltip: this.refreshText,
27793 cls: "btn-outline-secondary",
27794 html : ' <i class="fa fa-refresh"></i>',
27795 preventDefault: true,
27796 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27802 updateInfo : function(){
27803 if(this.displayEl){
27804 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27805 var msg = count == 0 ?
27809 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
27811 this.displayEl.update(msg);
27816 onLoad : function(ds, r, o)
27818 this.cursor = o.params && o.params.start ? o.params.start : 0;
27820 var d = this.getPageData(),
27825 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27826 this.field.dom.value = ap;
27827 this.first.setDisabled(ap == 1);
27828 this.prev.setDisabled(ap == 1);
27829 this.next.setDisabled(ap == ps);
27830 this.last.setDisabled(ap == ps);
27831 this.loading.enable();
27836 getPageData : function(){
27837 var total = this.ds.getTotalCount();
27840 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27841 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27846 onLoadError : function(){
27847 this.loading.enable();
27851 onPagingKeydown : function(e){
27852 var k = e.getKey();
27853 var d = this.getPageData();
27855 var v = this.field.dom.value, pageNum;
27856 if(!v || isNaN(pageNum = parseInt(v, 10))){
27857 this.field.dom.value = d.activePage;
27860 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27861 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27864 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))
27866 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27867 this.field.dom.value = pageNum;
27868 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27871 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27873 var v = this.field.dom.value, pageNum;
27874 var increment = (e.shiftKey) ? 10 : 1;
27875 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27878 if(!v || isNaN(pageNum = parseInt(v, 10))) {
27879 this.field.dom.value = d.activePage;
27882 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27884 this.field.dom.value = parseInt(v, 10) + increment;
27885 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27886 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27893 beforeLoad : function(){
27895 this.loading.disable();
27900 onClick : function(which){
27909 ds.load({params:{start: 0, limit: this.pageSize}});
27912 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27915 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27918 var total = ds.getTotalCount();
27919 var extra = total % this.pageSize;
27920 var lastStart = extra ? (total - extra) : total-this.pageSize;
27921 ds.load({params:{start: lastStart, limit: this.pageSize}});
27924 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27930 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27931 * @param {Roo.data.Store} store The data store to unbind
27933 unbind : function(ds){
27934 ds.un("beforeload", this.beforeLoad, this);
27935 ds.un("load", this.onLoad, this);
27936 ds.un("loadexception", this.onLoadError, this);
27937 ds.un("remove", this.updateInfo, this);
27938 ds.un("add", this.updateInfo, this);
27939 this.ds = undefined;
27943 * Binds the paging toolbar to the specified {@link Roo.data.Store}
27944 * @param {Roo.data.Store} store The data store to bind
27946 bind : function(ds){
27947 ds.on("beforeload", this.beforeLoad, this);
27948 ds.on("load", this.onLoad, this);
27949 ds.on("loadexception", this.onLoadError, this);
27950 ds.on("remove", this.updateInfo, this);
27951 ds.on("add", this.updateInfo, this);
27962 * @class Roo.bootstrap.MessageBar
27963 * @extends Roo.bootstrap.Component
27964 * Bootstrap MessageBar class
27965 * @cfg {String} html contents of the MessageBar
27966 * @cfg {String} weight (info | success | warning | danger) default info
27967 * @cfg {String} beforeClass insert the bar before the given class
27968 * @cfg {Boolean} closable (true | false) default false
27969 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27972 * Create a new Element
27973 * @param {Object} config The config object
27976 Roo.bootstrap.MessageBar = function(config){
27977 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27980 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
27986 beforeClass: 'bootstrap-sticky-wrap',
27988 getAutoCreate : function(){
27992 cls: 'alert alert-dismissable alert-' + this.weight,
27997 html: this.html || ''
28003 cfg.cls += ' alert-messages-fixed';
28017 onRender : function(ct, position)
28019 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28022 var cfg = Roo.apply({}, this.getAutoCreate());
28026 cfg.cls += ' ' + this.cls;
28029 cfg.style = this.style;
28031 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28033 this.el.setVisibilityMode(Roo.Element.DISPLAY);
28036 this.el.select('>button.close').on('click', this.hide, this);
28042 if (!this.rendered) {
28048 this.fireEvent('show', this);
28054 if (!this.rendered) {
28060 this.fireEvent('hide', this);
28063 update : function()
28065 // var e = this.el.dom.firstChild;
28067 // if(this.closable){
28068 // e = e.nextSibling;
28071 // e.data = this.html || '';
28073 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28089 * @class Roo.bootstrap.Graph
28090 * @extends Roo.bootstrap.Component
28091 * Bootstrap Graph class
28095 @cfg {String} graphtype bar | vbar | pie
28096 @cfg {number} g_x coodinator | centre x (pie)
28097 @cfg {number} g_y coodinator | centre y (pie)
28098 @cfg {number} g_r radius (pie)
28099 @cfg {number} g_height height of the chart (respected by all elements in the set)
28100 @cfg {number} g_width width of the chart (respected by all elements in the set)
28101 @cfg {Object} title The title of the chart
28104 -opts (object) options for the chart
28106 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28107 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28109 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.
28110 o stacked (boolean) whether or not to tread values as in a stacked bar chart
28112 o stretch (boolean)
28114 -opts (object) options for the pie
28117 o startAngle (number)
28118 o endAngle (number)
28122 * Create a new Input
28123 * @param {Object} config The config object
28126 Roo.bootstrap.Graph = function(config){
28127 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28133 * The img click event for the img.
28134 * @param {Roo.EventObject} e
28140 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
28151 //g_colors: this.colors,
28158 getAutoCreate : function(){
28169 onRender : function(ct,position){
28172 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28174 if (typeof(Raphael) == 'undefined') {
28175 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28179 this.raphael = Raphael(this.el.dom);
28181 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28182 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28183 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28184 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28186 r.text(160, 10, "Single Series Chart").attr(txtattr);
28187 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28188 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28189 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28191 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28192 r.barchart(330, 10, 300, 220, data1);
28193 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28194 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28197 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28198 // r.barchart(30, 30, 560, 250, xdata, {
28199 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28200 // axis : "0 0 1 1",
28201 // axisxlabels : xdata
28202 // //yvalues : cols,
28205 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28207 // this.load(null,xdata,{
28208 // axis : "0 0 1 1",
28209 // axisxlabels : xdata
28214 load : function(graphtype,xdata,opts)
28216 this.raphael.clear();
28218 graphtype = this.graphtype;
28223 var r = this.raphael,
28224 fin = function () {
28225 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28227 fout = function () {
28228 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28230 pfin = function() {
28231 this.sector.stop();
28232 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28235 this.label[0].stop();
28236 this.label[0].attr({ r: 7.5 });
28237 this.label[1].attr({ "font-weight": 800 });
28240 pfout = function() {
28241 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28244 this.label[0].animate({ r: 5 }, 500, "bounce");
28245 this.label[1].attr({ "font-weight": 400 });
28251 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28254 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28257 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
28258 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28260 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28267 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28272 setTitle: function(o)
28277 initEvents: function() {
28280 this.el.on('click', this.onClick, this);
28284 onClick : function(e)
28286 Roo.log('img onclick');
28287 this.fireEvent('click', this, e);
28299 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28302 * @class Roo.bootstrap.dash.NumberBox
28303 * @extends Roo.bootstrap.Component
28304 * Bootstrap NumberBox class
28305 * @cfg {String} headline Box headline
28306 * @cfg {String} content Box content
28307 * @cfg {String} icon Box icon
28308 * @cfg {String} footer Footer text
28309 * @cfg {String} fhref Footer href
28312 * Create a new NumberBox
28313 * @param {Object} config The config object
28317 Roo.bootstrap.dash.NumberBox = function(config){
28318 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28322 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
28331 getAutoCreate : function(){
28335 cls : 'small-box ',
28343 cls : 'roo-headline',
28344 html : this.headline
28348 cls : 'roo-content',
28349 html : this.content
28363 cls : 'ion ' + this.icon
28372 cls : 'small-box-footer',
28373 href : this.fhref || '#',
28377 cfg.cn.push(footer);
28384 onRender : function(ct,position){
28385 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28392 setHeadline: function (value)
28394 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28397 setFooter: function (value, href)
28399 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28402 this.el.select('a.small-box-footer',true).first().attr('href', href);
28407 setContent: function (value)
28409 this.el.select('.roo-content',true).first().dom.innerHTML = value;
28412 initEvents: function()
28426 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28429 * @class Roo.bootstrap.dash.TabBox
28430 * @extends Roo.bootstrap.Component
28431 * Bootstrap TabBox class
28432 * @cfg {String} title Title of the TabBox
28433 * @cfg {String} icon Icon of the TabBox
28434 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28435 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28438 * Create a new TabBox
28439 * @param {Object} config The config object
28443 Roo.bootstrap.dash.TabBox = function(config){
28444 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28449 * When a pane is added
28450 * @param {Roo.bootstrap.dash.TabPane} pane
28454 * @event activatepane
28455 * When a pane is activated
28456 * @param {Roo.bootstrap.dash.TabPane} pane
28458 "activatepane" : true
28466 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28471 tabScrollable : false,
28473 getChildContainer : function()
28475 return this.el.select('.tab-content', true).first();
28478 getAutoCreate : function(){
28482 cls: 'pull-left header',
28490 cls: 'fa ' + this.icon
28496 cls: 'nav nav-tabs pull-right',
28502 if(this.tabScrollable){
28509 cls: 'nav nav-tabs pull-right',
28520 cls: 'nav-tabs-custom',
28525 cls: 'tab-content no-padding',
28533 initEvents : function()
28535 //Roo.log('add add pane handler');
28536 this.on('addpane', this.onAddPane, this);
28539 * Updates the box title
28540 * @param {String} html to set the title to.
28542 setTitle : function(value)
28544 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28546 onAddPane : function(pane)
28548 this.panes.push(pane);
28549 //Roo.log('addpane');
28551 // tabs are rendere left to right..
28552 if(!this.showtabs){
28556 var ctr = this.el.select('.nav-tabs', true).first();
28559 var existing = ctr.select('.nav-tab',true);
28560 var qty = existing.getCount();;
28563 var tab = ctr.createChild({
28565 cls : 'nav-tab' + (qty ? '' : ' active'),
28573 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28576 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28578 pane.el.addClass('active');
28583 onTabClick : function(ev,un,ob,pane)
28585 //Roo.log('tab - prev default');
28586 ev.preventDefault();
28589 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28590 pane.tab.addClass('active');
28591 //Roo.log(pane.title);
28592 this.getChildContainer().select('.tab-pane',true).removeClass('active');
28593 // technically we should have a deactivate event.. but maybe add later.
28594 // and it should not de-activate the selected tab...
28595 this.fireEvent('activatepane', pane);
28596 pane.el.addClass('active');
28597 pane.fireEvent('activate');
28602 getActivePane : function()
28605 Roo.each(this.panes, function(p) {
28606 if(p.el.hasClass('active')){
28627 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28629 * @class Roo.bootstrap.TabPane
28630 * @extends Roo.bootstrap.Component
28631 * Bootstrap TabPane class
28632 * @cfg {Boolean} active (false | true) Default false
28633 * @cfg {String} title title of panel
28637 * Create a new TabPane
28638 * @param {Object} config The config object
28641 Roo.bootstrap.dash.TabPane = function(config){
28642 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28648 * When a pane is activated
28649 * @param {Roo.bootstrap.dash.TabPane} pane
28656 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
28661 // the tabBox that this is attached to.
28664 getAutoCreate : function()
28672 cfg.cls += ' active';
28677 initEvents : function()
28679 //Roo.log('trigger add pane handler');
28680 this.parent().fireEvent('addpane', this)
28684 * Updates the tab title
28685 * @param {String} html to set the title to.
28687 setTitle: function(str)
28693 this.tab.select('a', true).first().dom.innerHTML = str;
28710 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28713 * @class Roo.bootstrap.menu.Menu
28714 * @extends Roo.bootstrap.Component
28715 * Bootstrap Menu class - container for Menu
28716 * @cfg {String} html Text of the menu
28717 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28718 * @cfg {String} icon Font awesome icon
28719 * @cfg {String} pos Menu align to (top | bottom) default bottom
28723 * Create a new Menu
28724 * @param {Object} config The config object
28728 Roo.bootstrap.menu.Menu = function(config){
28729 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28733 * @event beforeshow
28734 * Fires before this menu is displayed
28735 * @param {Roo.bootstrap.menu.Menu} this
28739 * @event beforehide
28740 * Fires before this menu is hidden
28741 * @param {Roo.bootstrap.menu.Menu} this
28746 * Fires after this menu is displayed
28747 * @param {Roo.bootstrap.menu.Menu} this
28752 * Fires after this menu is hidden
28753 * @param {Roo.bootstrap.menu.Menu} this
28758 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28759 * @param {Roo.bootstrap.menu.Menu} this
28760 * @param {Roo.EventObject} e
28767 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
28771 weight : 'default',
28776 getChildContainer : function() {
28777 if(this.isSubMenu){
28781 return this.el.select('ul.dropdown-menu', true).first();
28784 getAutoCreate : function()
28789 cls : 'roo-menu-text',
28797 cls : 'fa ' + this.icon
28808 cls : 'dropdown-button btn btn-' + this.weight,
28813 cls : 'dropdown-toggle btn btn-' + this.weight,
28823 cls : 'dropdown-menu'
28829 if(this.pos == 'top'){
28830 cfg.cls += ' dropup';
28833 if(this.isSubMenu){
28836 cls : 'dropdown-menu'
28843 onRender : function(ct, position)
28845 this.isSubMenu = ct.hasClass('dropdown-submenu');
28847 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28850 initEvents : function()
28852 if(this.isSubMenu){
28856 this.hidden = true;
28858 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28859 this.triggerEl.on('click', this.onTriggerPress, this);
28861 this.buttonEl = this.el.select('button.dropdown-button', true).first();
28862 this.buttonEl.on('click', this.onClick, this);
28868 if(this.isSubMenu){
28872 return this.el.select('ul.dropdown-menu', true).first();
28875 onClick : function(e)
28877 this.fireEvent("click", this, e);
28880 onTriggerPress : function(e)
28882 if (this.isVisible()) {
28889 isVisible : function(){
28890 return !this.hidden;
28895 this.fireEvent("beforeshow", this);
28897 this.hidden = false;
28898 this.el.addClass('open');
28900 Roo.get(document).on("mouseup", this.onMouseUp, this);
28902 this.fireEvent("show", this);
28909 this.fireEvent("beforehide", this);
28911 this.hidden = true;
28912 this.el.removeClass('open');
28914 Roo.get(document).un("mouseup", this.onMouseUp);
28916 this.fireEvent("hide", this);
28919 onMouseUp : function()
28933 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28936 * @class Roo.bootstrap.menu.Item
28937 * @extends Roo.bootstrap.Component
28938 * Bootstrap MenuItem class
28939 * @cfg {Boolean} submenu (true | false) default false
28940 * @cfg {String} html text of the item
28941 * @cfg {String} href the link
28942 * @cfg {Boolean} disable (true | false) default false
28943 * @cfg {Boolean} preventDefault (true | false) default true
28944 * @cfg {String} icon Font awesome icon
28945 * @cfg {String} pos Submenu align to (left | right) default right
28949 * Create a new Item
28950 * @param {Object} config The config object
28954 Roo.bootstrap.menu.Item = function(config){
28955 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28959 * Fires when the mouse is hovering over this menu
28960 * @param {Roo.bootstrap.menu.Item} this
28961 * @param {Roo.EventObject} e
28966 * Fires when the mouse exits this menu
28967 * @param {Roo.bootstrap.menu.Item} this
28968 * @param {Roo.EventObject} e
28974 * The raw click event for the entire grid.
28975 * @param {Roo.EventObject} e
28981 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
28986 preventDefault: true,
28991 getAutoCreate : function()
28996 cls : 'roo-menu-item-text',
29004 cls : 'fa ' + this.icon
29013 href : this.href || '#',
29020 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
29024 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
29026 if(this.pos == 'left'){
29027 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
29034 initEvents : function()
29036 this.el.on('mouseover', this.onMouseOver, this);
29037 this.el.on('mouseout', this.onMouseOut, this);
29039 this.el.select('a', true).first().on('click', this.onClick, this);
29043 onClick : function(e)
29045 if(this.preventDefault){
29046 e.preventDefault();
29049 this.fireEvent("click", this, e);
29052 onMouseOver : function(e)
29054 if(this.submenu && this.pos == 'left'){
29055 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29058 this.fireEvent("mouseover", this, e);
29061 onMouseOut : function(e)
29063 this.fireEvent("mouseout", this, e);
29075 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29078 * @class Roo.bootstrap.menu.Separator
29079 * @extends Roo.bootstrap.Component
29080 * Bootstrap Separator class
29083 * Create a new Separator
29084 * @param {Object} config The config object
29088 Roo.bootstrap.menu.Separator = function(config){
29089 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29092 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
29094 getAutoCreate : function(){
29097 cls: 'dropdown-divider divider'
29115 * @class Roo.bootstrap.Tooltip
29116 * Bootstrap Tooltip class
29117 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29118 * to determine which dom element triggers the tooltip.
29120 * It needs to add support for additional attributes like tooltip-position
29123 * Create a new Toolti
29124 * @param {Object} config The config object
29127 Roo.bootstrap.Tooltip = function(config){
29128 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29130 this.alignment = Roo.bootstrap.Tooltip.alignment;
29132 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29133 this.alignment = config.alignment;
29138 Roo.apply(Roo.bootstrap.Tooltip, {
29140 * @function init initialize tooltip monitoring.
29144 currentTip : false,
29145 currentRegion : false,
29151 Roo.get(document).on('mouseover', this.enter ,this);
29152 Roo.get(document).on('mouseout', this.leave, this);
29155 this.currentTip = new Roo.bootstrap.Tooltip();
29158 enter : function(ev)
29160 var dom = ev.getTarget();
29162 //Roo.log(['enter',dom]);
29163 var el = Roo.fly(dom);
29164 if (this.currentEl) {
29166 //Roo.log(this.currentEl);
29167 //Roo.log(this.currentEl.contains(dom));
29168 if (this.currentEl == el) {
29171 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29177 if (this.currentTip.el) {
29178 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29182 if(!el || el.dom == document){
29188 if (!el.attr('tooltip')) {
29189 pel = el.findParent("[tooltip]");
29191 bindEl = Roo.get(pel);
29197 // you can not look for children, as if el is the body.. then everythign is the child..
29198 if (!pel && !el.attr('tooltip')) { //
29199 if (!el.select("[tooltip]").elements.length) {
29202 // is the mouse over this child...?
29203 bindEl = el.select("[tooltip]").first();
29204 var xy = ev.getXY();
29205 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29206 //Roo.log("not in region.");
29209 //Roo.log("child element over..");
29212 this.currentEl = el;
29213 this.currentTip.bind(bindEl);
29214 this.currentRegion = Roo.lib.Region.getRegion(dom);
29215 this.currentTip.enter();
29218 leave : function(ev)
29220 var dom = ev.getTarget();
29221 //Roo.log(['leave',dom]);
29222 if (!this.currentEl) {
29227 if (dom != this.currentEl.dom) {
29230 var xy = ev.getXY();
29231 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
29234 // only activate leave if mouse cursor is outside... bounding box..
29239 if (this.currentTip) {
29240 this.currentTip.leave();
29242 //Roo.log('clear currentEl');
29243 this.currentEl = false;
29248 'left' : ['r-l', [-2,0], 'right'],
29249 'right' : ['l-r', [2,0], 'left'],
29250 'bottom' : ['t-b', [0,2], 'top'],
29251 'top' : [ 'b-t', [0,-2], 'bottom']
29257 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
29262 delay : null, // can be { show : 300 , hide: 500}
29266 hoverState : null, //???
29268 placement : 'bottom',
29272 getAutoCreate : function(){
29279 cls : 'tooltip-arrow arrow'
29282 cls : 'tooltip-inner'
29289 bind : function(el)
29294 initEvents : function()
29296 this.arrowEl = this.el.select('.arrow', true).first();
29297 this.innerEl = this.el.select('.tooltip-inner', true).first();
29300 enter : function () {
29302 if (this.timeout != null) {
29303 clearTimeout(this.timeout);
29306 this.hoverState = 'in';
29307 //Roo.log("enter - show");
29308 if (!this.delay || !this.delay.show) {
29313 this.timeout = setTimeout(function () {
29314 if (_t.hoverState == 'in') {
29317 }, this.delay.show);
29321 clearTimeout(this.timeout);
29323 this.hoverState = 'out';
29324 if (!this.delay || !this.delay.hide) {
29330 this.timeout = setTimeout(function () {
29331 //Roo.log("leave - timeout");
29333 if (_t.hoverState == 'out') {
29335 Roo.bootstrap.Tooltip.currentEl = false;
29340 show : function (msg)
29343 this.render(document.body);
29346 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29348 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29350 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29352 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29353 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29355 var placement = typeof this.placement == 'function' ?
29356 this.placement.call(this, this.el, on_el) :
29359 var autoToken = /\s?auto?\s?/i;
29360 var autoPlace = autoToken.test(placement);
29362 placement = placement.replace(autoToken, '') || 'top';
29366 //this.el.setXY([0,0]);
29368 //this.el.dom.style.display='block';
29370 //this.el.appendTo(on_el);
29372 var p = this.getPosition();
29373 var box = this.el.getBox();
29379 var align = this.alignment[placement];
29381 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29383 if(placement == 'top' || placement == 'bottom'){
29385 placement = 'right';
29388 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29389 placement = 'left';
29392 var scroll = Roo.select('body', true).first().getScroll();
29394 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29398 align = this.alignment[placement];
29400 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29404 var elems = document.getElementsByTagName('div');
29405 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29406 for (var i = 0; i < elems.length; i++) {
29407 var zindex = Number.parseInt(
29408 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29411 if (zindex > highest) {
29418 this.el.dom.style.zIndex = highest;
29420 this.el.alignTo(this.bindEl, align[0],align[1]);
29421 //var arrow = this.el.select('.arrow',true).first();
29422 //arrow.set(align[2],
29424 this.el.addClass(placement);
29425 this.el.addClass("bs-tooltip-"+ placement);
29427 this.el.addClass('in fade show');
29429 this.hoverState = null;
29431 if (this.el.hasClass('fade')) {
29446 //this.el.setXY([0,0]);
29447 this.el.removeClass(['show', 'in']);
29463 * @class Roo.bootstrap.LocationPicker
29464 * @extends Roo.bootstrap.Component
29465 * Bootstrap LocationPicker class
29466 * @cfg {Number} latitude Position when init default 0
29467 * @cfg {Number} longitude Position when init default 0
29468 * @cfg {Number} zoom default 15
29469 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29470 * @cfg {Boolean} mapTypeControl default false
29471 * @cfg {Boolean} disableDoubleClickZoom default false
29472 * @cfg {Boolean} scrollwheel default true
29473 * @cfg {Boolean} streetViewControl default false
29474 * @cfg {Number} radius default 0
29475 * @cfg {String} locationName
29476 * @cfg {Boolean} draggable default true
29477 * @cfg {Boolean} enableAutocomplete default false
29478 * @cfg {Boolean} enableReverseGeocode default true
29479 * @cfg {String} markerTitle
29482 * Create a new LocationPicker
29483 * @param {Object} config The config object
29487 Roo.bootstrap.LocationPicker = function(config){
29489 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29494 * Fires when the picker initialized.
29495 * @param {Roo.bootstrap.LocationPicker} this
29496 * @param {Google Location} location
29500 * @event positionchanged
29501 * Fires when the picker position changed.
29502 * @param {Roo.bootstrap.LocationPicker} this
29503 * @param {Google Location} location
29505 positionchanged : true,
29508 * Fires when the map resize.
29509 * @param {Roo.bootstrap.LocationPicker} this
29514 * Fires when the map show.
29515 * @param {Roo.bootstrap.LocationPicker} this
29520 * Fires when the map hide.
29521 * @param {Roo.bootstrap.LocationPicker} this
29526 * Fires when click the map.
29527 * @param {Roo.bootstrap.LocationPicker} this
29528 * @param {Map event} e
29532 * @event mapRightClick
29533 * Fires when right click the map.
29534 * @param {Roo.bootstrap.LocationPicker} this
29535 * @param {Map event} e
29537 mapRightClick : true,
29539 * @event markerClick
29540 * Fires when click the marker.
29541 * @param {Roo.bootstrap.LocationPicker} this
29542 * @param {Map event} e
29544 markerClick : true,
29546 * @event markerRightClick
29547 * Fires when right click the marker.
29548 * @param {Roo.bootstrap.LocationPicker} this
29549 * @param {Map event} e
29551 markerRightClick : true,
29553 * @event OverlayViewDraw
29554 * Fires when OverlayView Draw
29555 * @param {Roo.bootstrap.LocationPicker} this
29557 OverlayViewDraw : true,
29559 * @event OverlayViewOnAdd
29560 * Fires when OverlayView Draw
29561 * @param {Roo.bootstrap.LocationPicker} this
29563 OverlayViewOnAdd : true,
29565 * @event OverlayViewOnRemove
29566 * Fires when OverlayView Draw
29567 * @param {Roo.bootstrap.LocationPicker} this
29569 OverlayViewOnRemove : true,
29571 * @event OverlayViewShow
29572 * Fires when OverlayView Draw
29573 * @param {Roo.bootstrap.LocationPicker} this
29574 * @param {Pixel} cpx
29576 OverlayViewShow : true,
29578 * @event OverlayViewHide
29579 * Fires when OverlayView Draw
29580 * @param {Roo.bootstrap.LocationPicker} this
29582 OverlayViewHide : true,
29584 * @event loadexception
29585 * Fires when load google lib failed.
29586 * @param {Roo.bootstrap.LocationPicker} this
29588 loadexception : true
29593 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
29595 gMapContext: false,
29601 mapTypeControl: false,
29602 disableDoubleClickZoom: false,
29604 streetViewControl: false,
29608 enableAutocomplete: false,
29609 enableReverseGeocode: true,
29612 getAutoCreate: function()
29617 cls: 'roo-location-picker'
29623 initEvents: function(ct, position)
29625 if(!this.el.getWidth() || this.isApplied()){
29629 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29634 initial: function()
29636 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29637 this.fireEvent('loadexception', this);
29641 if(!this.mapTypeId){
29642 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29645 this.gMapContext = this.GMapContext();
29647 this.initOverlayView();
29649 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29653 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29654 _this.setPosition(_this.gMapContext.marker.position);
29657 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29658 _this.fireEvent('mapClick', this, event);
29662 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29663 _this.fireEvent('mapRightClick', this, event);
29667 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29668 _this.fireEvent('markerClick', this, event);
29672 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29673 _this.fireEvent('markerRightClick', this, event);
29677 this.setPosition(this.gMapContext.location);
29679 this.fireEvent('initial', this, this.gMapContext.location);
29682 initOverlayView: function()
29686 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29690 _this.fireEvent('OverlayViewDraw', _this);
29695 _this.fireEvent('OverlayViewOnAdd', _this);
29698 onRemove: function()
29700 _this.fireEvent('OverlayViewOnRemove', _this);
29703 show: function(cpx)
29705 _this.fireEvent('OverlayViewShow', _this, cpx);
29710 _this.fireEvent('OverlayViewHide', _this);
29716 fromLatLngToContainerPixel: function(event)
29718 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29721 isApplied: function()
29723 return this.getGmapContext() == false ? false : true;
29726 getGmapContext: function()
29728 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29731 GMapContext: function()
29733 var position = new google.maps.LatLng(this.latitude, this.longitude);
29735 var _map = new google.maps.Map(this.el.dom, {
29738 mapTypeId: this.mapTypeId,
29739 mapTypeControl: this.mapTypeControl,
29740 disableDoubleClickZoom: this.disableDoubleClickZoom,
29741 scrollwheel: this.scrollwheel,
29742 streetViewControl: this.streetViewControl,
29743 locationName: this.locationName,
29744 draggable: this.draggable,
29745 enableAutocomplete: this.enableAutocomplete,
29746 enableReverseGeocode: this.enableReverseGeocode
29749 var _marker = new google.maps.Marker({
29750 position: position,
29752 title: this.markerTitle,
29753 draggable: this.draggable
29760 location: position,
29761 radius: this.radius,
29762 locationName: this.locationName,
29763 addressComponents: {
29764 formatted_address: null,
29765 addressLine1: null,
29766 addressLine2: null,
29768 streetNumber: null,
29772 stateOrProvince: null
29775 domContainer: this.el.dom,
29776 geodecoder: new google.maps.Geocoder()
29780 drawCircle: function(center, radius, options)
29782 if (this.gMapContext.circle != null) {
29783 this.gMapContext.circle.setMap(null);
29787 options = Roo.apply({}, options, {
29788 strokeColor: "#0000FF",
29789 strokeOpacity: .35,
29791 fillColor: "#0000FF",
29795 options.map = this.gMapContext.map;
29796 options.radius = radius;
29797 options.center = center;
29798 this.gMapContext.circle = new google.maps.Circle(options);
29799 return this.gMapContext.circle;
29805 setPosition: function(location)
29807 this.gMapContext.location = location;
29808 this.gMapContext.marker.setPosition(location);
29809 this.gMapContext.map.panTo(location);
29810 this.drawCircle(location, this.gMapContext.radius, {});
29814 if (this.gMapContext.settings.enableReverseGeocode) {
29815 this.gMapContext.geodecoder.geocode({
29816 latLng: this.gMapContext.location
29817 }, function(results, status) {
29819 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29820 _this.gMapContext.locationName = results[0].formatted_address;
29821 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29823 _this.fireEvent('positionchanged', this, location);
29830 this.fireEvent('positionchanged', this, location);
29835 google.maps.event.trigger(this.gMapContext.map, "resize");
29837 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29839 this.fireEvent('resize', this);
29842 setPositionByLatLng: function(latitude, longitude)
29844 this.setPosition(new google.maps.LatLng(latitude, longitude));
29847 getCurrentPosition: function()
29850 latitude: this.gMapContext.location.lat(),
29851 longitude: this.gMapContext.location.lng()
29855 getAddressName: function()
29857 return this.gMapContext.locationName;
29860 getAddressComponents: function()
29862 return this.gMapContext.addressComponents;
29865 address_component_from_google_geocode: function(address_components)
29869 for (var i = 0; i < address_components.length; i++) {
29870 var component = address_components[i];
29871 if (component.types.indexOf("postal_code") >= 0) {
29872 result.postalCode = component.short_name;
29873 } else if (component.types.indexOf("street_number") >= 0) {
29874 result.streetNumber = component.short_name;
29875 } else if (component.types.indexOf("route") >= 0) {
29876 result.streetName = component.short_name;
29877 } else if (component.types.indexOf("neighborhood") >= 0) {
29878 result.city = component.short_name;
29879 } else if (component.types.indexOf("locality") >= 0) {
29880 result.city = component.short_name;
29881 } else if (component.types.indexOf("sublocality") >= 0) {
29882 result.district = component.short_name;
29883 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29884 result.stateOrProvince = component.short_name;
29885 } else if (component.types.indexOf("country") >= 0) {
29886 result.country = component.short_name;
29890 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29891 result.addressLine2 = "";
29895 setZoomLevel: function(zoom)
29897 this.gMapContext.map.setZoom(zoom);
29910 this.fireEvent('show', this);
29921 this.fireEvent('hide', this);
29926 Roo.apply(Roo.bootstrap.LocationPicker, {
29928 OverlayView : function(map, options)
29930 options = options || {};
29937 * @class Roo.bootstrap.Alert
29938 * @extends Roo.bootstrap.Component
29939 * Bootstrap Alert class - shows an alert area box
29941 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29942 Enter a valid email address
29945 * @cfg {String} title The title of alert
29946 * @cfg {String} html The content of alert
29947 * @cfg {String} weight (success|info|warning|danger) Weight of the message
29948 * @cfg {String} fa font-awesomeicon
29949 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
29950 * @cfg {Boolean} close true to show a x closer
29954 * Create a new alert
29955 * @param {Object} config The config object
29959 Roo.bootstrap.Alert = function(config){
29960 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29964 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
29970 faicon: false, // BC
29974 getAutoCreate : function()
29986 style : this.close ? '' : 'display:none'
29990 cls : 'roo-alert-icon'
29995 cls : 'roo-alert-title',
30000 cls : 'roo-alert-text',
30007 cfg.cn[0].cls += ' fa ' + this.faicon;
30010 cfg.cn[0].cls += ' fa ' + this.fa;
30014 cfg.cls += ' alert-' + this.weight;
30020 initEvents: function()
30022 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30023 this.titleEl = this.el.select('.roo-alert-title',true).first();
30024 this.iconEl = this.el.select('.roo-alert-icon',true).first();
30025 this.htmlEl = this.el.select('.roo-alert-text',true).first();
30026 if (this.seconds > 0) {
30027 this.hide.defer(this.seconds, this);
30031 * Set the Title Message HTML
30032 * @param {String} html
30034 setTitle : function(str)
30036 this.titleEl.dom.innerHTML = str;
30040 * Set the Body Message HTML
30041 * @param {String} html
30043 setHtml : function(str)
30045 this.htmlEl.dom.innerHTML = str;
30048 * Set the Weight of the alert
30049 * @param {String} (success|info|warning|danger) weight
30052 setWeight : function(weight)
30055 this.el.removeClass('alert-' + this.weight);
30058 this.weight = weight;
30060 this.el.addClass('alert-' + this.weight);
30063 * Set the Icon of the alert
30064 * @param {String} see fontawsome names (name without the 'fa-' bit)
30066 setIcon : function(icon)
30069 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30072 this.faicon = icon;
30074 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30099 * @class Roo.bootstrap.UploadCropbox
30100 * @extends Roo.bootstrap.Component
30101 * Bootstrap UploadCropbox class
30102 * @cfg {String} emptyText show when image has been loaded
30103 * @cfg {String} rotateNotify show when image too small to rotate
30104 * @cfg {Number} errorTimeout default 3000
30105 * @cfg {Number} minWidth default 300
30106 * @cfg {Number} minHeight default 300
30107 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30108 * @cfg {Boolean} isDocument (true|false) default false
30109 * @cfg {String} url action url
30110 * @cfg {String} paramName default 'imageUpload'
30111 * @cfg {String} method default POST
30112 * @cfg {Boolean} loadMask (true|false) default true
30113 * @cfg {Boolean} loadingText default 'Loading...'
30116 * Create a new UploadCropbox
30117 * @param {Object} config The config object
30120 Roo.bootstrap.UploadCropbox = function(config){
30121 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30125 * @event beforeselectfile
30126 * Fire before select file
30127 * @param {Roo.bootstrap.UploadCropbox} this
30129 "beforeselectfile" : true,
30132 * Fire after initEvent
30133 * @param {Roo.bootstrap.UploadCropbox} this
30138 * Fire after initEvent
30139 * @param {Roo.bootstrap.UploadCropbox} this
30140 * @param {String} data
30145 * Fire when preparing the file data
30146 * @param {Roo.bootstrap.UploadCropbox} this
30147 * @param {Object} file
30152 * Fire when get exception
30153 * @param {Roo.bootstrap.UploadCropbox} this
30154 * @param {XMLHttpRequest} xhr
30156 "exception" : true,
30158 * @event beforeloadcanvas
30159 * Fire before load the canvas
30160 * @param {Roo.bootstrap.UploadCropbox} this
30161 * @param {String} src
30163 "beforeloadcanvas" : true,
30166 * Fire when trash image
30167 * @param {Roo.bootstrap.UploadCropbox} this
30172 * Fire when download the image
30173 * @param {Roo.bootstrap.UploadCropbox} this
30177 * @event footerbuttonclick
30178 * Fire when footerbuttonclick
30179 * @param {Roo.bootstrap.UploadCropbox} this
30180 * @param {String} type
30182 "footerbuttonclick" : true,
30186 * @param {Roo.bootstrap.UploadCropbox} this
30191 * Fire when rotate the image
30192 * @param {Roo.bootstrap.UploadCropbox} this
30193 * @param {String} pos
30198 * Fire when inspect the file
30199 * @param {Roo.bootstrap.UploadCropbox} this
30200 * @param {Object} file
30205 * Fire when xhr upload the file
30206 * @param {Roo.bootstrap.UploadCropbox} this
30207 * @param {Object} data
30212 * Fire when arrange the file data
30213 * @param {Roo.bootstrap.UploadCropbox} this
30214 * @param {Object} formData
30219 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30222 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
30224 emptyText : 'Click to upload image',
30225 rotateNotify : 'Image is too small to rotate',
30226 errorTimeout : 3000,
30240 cropType : 'image/jpeg',
30242 canvasLoaded : false,
30243 isDocument : false,
30245 paramName : 'imageUpload',
30247 loadingText : 'Loading...',
30250 getAutoCreate : function()
30254 cls : 'roo-upload-cropbox',
30258 cls : 'roo-upload-cropbox-selector',
30263 cls : 'roo-upload-cropbox-body',
30264 style : 'cursor:pointer',
30268 cls : 'roo-upload-cropbox-preview'
30272 cls : 'roo-upload-cropbox-thumb'
30276 cls : 'roo-upload-cropbox-empty-notify',
30277 html : this.emptyText
30281 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30282 html : this.rotateNotify
30288 cls : 'roo-upload-cropbox-footer',
30291 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30301 onRender : function(ct, position)
30303 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30305 if (this.buttons.length) {
30307 Roo.each(this.buttons, function(bb) {
30309 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30311 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30317 this.maskEl = this.el;
30321 initEvents : function()
30323 this.urlAPI = (window.createObjectURL && window) ||
30324 (window.URL && URL.revokeObjectURL && URL) ||
30325 (window.webkitURL && webkitURL);
30327 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30328 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30330 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30331 this.selectorEl.hide();
30333 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30334 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30336 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30337 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30338 this.thumbEl.hide();
30340 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30341 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30343 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30344 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30345 this.errorEl.hide();
30347 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30348 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30349 this.footerEl.hide();
30351 this.setThumbBoxSize();
30357 this.fireEvent('initial', this);
30364 window.addEventListener("resize", function() { _this.resize(); } );
30366 this.bodyEl.on('click', this.beforeSelectFile, this);
30369 this.bodyEl.on('touchstart', this.onTouchStart, this);
30370 this.bodyEl.on('touchmove', this.onTouchMove, this);
30371 this.bodyEl.on('touchend', this.onTouchEnd, this);
30375 this.bodyEl.on('mousedown', this.onMouseDown, this);
30376 this.bodyEl.on('mousemove', this.onMouseMove, this);
30377 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30378 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30379 Roo.get(document).on('mouseup', this.onMouseUp, this);
30382 this.selectorEl.on('change', this.onFileSelected, this);
30388 this.baseScale = 1;
30390 this.baseRotate = 1;
30391 this.dragable = false;
30392 this.pinching = false;
30395 this.cropData = false;
30396 this.notifyEl.dom.innerHTML = this.emptyText;
30398 this.selectorEl.dom.value = '';
30402 resize : function()
30404 if(this.fireEvent('resize', this) != false){
30405 this.setThumbBoxPosition();
30406 this.setCanvasPosition();
30410 onFooterButtonClick : function(e, el, o, type)
30413 case 'rotate-left' :
30414 this.onRotateLeft(e);
30416 case 'rotate-right' :
30417 this.onRotateRight(e);
30420 this.beforeSelectFile(e);
30435 this.fireEvent('footerbuttonclick', this, type);
30438 beforeSelectFile : function(e)
30440 e.preventDefault();
30442 if(this.fireEvent('beforeselectfile', this) != false){
30443 this.selectorEl.dom.click();
30447 onFileSelected : function(e)
30449 e.preventDefault();
30451 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30455 var file = this.selectorEl.dom.files[0];
30457 if(this.fireEvent('inspect', this, file) != false){
30458 this.prepare(file);
30463 trash : function(e)
30465 this.fireEvent('trash', this);
30468 download : function(e)
30470 this.fireEvent('download', this);
30473 loadCanvas : function(src)
30475 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30479 this.imageEl = document.createElement('img');
30483 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30485 this.imageEl.src = src;
30489 onLoadCanvas : function()
30491 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30492 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30494 this.bodyEl.un('click', this.beforeSelectFile, this);
30496 this.notifyEl.hide();
30497 this.thumbEl.show();
30498 this.footerEl.show();
30500 this.baseRotateLevel();
30502 if(this.isDocument){
30503 this.setThumbBoxSize();
30506 this.setThumbBoxPosition();
30508 this.baseScaleLevel();
30514 this.canvasLoaded = true;
30517 this.maskEl.unmask();
30522 setCanvasPosition : function()
30524 if(!this.canvasEl){
30528 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30529 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30531 this.previewEl.setLeft(pw);
30532 this.previewEl.setTop(ph);
30536 onMouseDown : function(e)
30540 this.dragable = true;
30541 this.pinching = false;
30543 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30544 this.dragable = false;
30548 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30549 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30553 onMouseMove : function(e)
30557 if(!this.canvasLoaded){
30561 if (!this.dragable){
30565 var minX = Math.ceil(this.thumbEl.getLeft(true));
30566 var minY = Math.ceil(this.thumbEl.getTop(true));
30568 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30569 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30571 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30572 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30574 x = x - this.mouseX;
30575 y = y - this.mouseY;
30577 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30578 var bgY = Math.ceil(y + this.previewEl.getTop(true));
30580 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30581 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30583 this.previewEl.setLeft(bgX);
30584 this.previewEl.setTop(bgY);
30586 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30587 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30590 onMouseUp : function(e)
30594 this.dragable = false;
30597 onMouseWheel : function(e)
30601 this.startScale = this.scale;
30603 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30605 if(!this.zoomable()){
30606 this.scale = this.startScale;
30615 zoomable : function()
30617 var minScale = this.thumbEl.getWidth() / this.minWidth;
30619 if(this.minWidth < this.minHeight){
30620 minScale = this.thumbEl.getHeight() / this.minHeight;
30623 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30624 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30628 (this.rotate == 0 || this.rotate == 180) &&
30630 width > this.imageEl.OriginWidth ||
30631 height > this.imageEl.OriginHeight ||
30632 (width < this.minWidth && height < this.minHeight)
30640 (this.rotate == 90 || this.rotate == 270) &&
30642 width > this.imageEl.OriginWidth ||
30643 height > this.imageEl.OriginHeight ||
30644 (width < this.minHeight && height < this.minWidth)
30651 !this.isDocument &&
30652 (this.rotate == 0 || this.rotate == 180) &&
30654 width < this.minWidth ||
30655 width > this.imageEl.OriginWidth ||
30656 height < this.minHeight ||
30657 height > this.imageEl.OriginHeight
30664 !this.isDocument &&
30665 (this.rotate == 90 || this.rotate == 270) &&
30667 width < this.minHeight ||
30668 width > this.imageEl.OriginWidth ||
30669 height < this.minWidth ||
30670 height > this.imageEl.OriginHeight
30680 onRotateLeft : function(e)
30682 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30684 var minScale = this.thumbEl.getWidth() / this.minWidth;
30686 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30687 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30689 this.startScale = this.scale;
30691 while (this.getScaleLevel() < minScale){
30693 this.scale = this.scale + 1;
30695 if(!this.zoomable()){
30700 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30701 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30706 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30713 this.scale = this.startScale;
30715 this.onRotateFail();
30720 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30722 if(this.isDocument){
30723 this.setThumbBoxSize();
30724 this.setThumbBoxPosition();
30725 this.setCanvasPosition();
30730 this.fireEvent('rotate', this, 'left');
30734 onRotateRight : function(e)
30736 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30738 var minScale = this.thumbEl.getWidth() / this.minWidth;
30740 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30741 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30743 this.startScale = this.scale;
30745 while (this.getScaleLevel() < minScale){
30747 this.scale = this.scale + 1;
30749 if(!this.zoomable()){
30754 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30755 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30760 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30767 this.scale = this.startScale;
30769 this.onRotateFail();
30774 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30776 if(this.isDocument){
30777 this.setThumbBoxSize();
30778 this.setThumbBoxPosition();
30779 this.setCanvasPosition();
30784 this.fireEvent('rotate', this, 'right');
30787 onRotateFail : function()
30789 this.errorEl.show(true);
30793 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30798 this.previewEl.dom.innerHTML = '';
30800 var canvasEl = document.createElement("canvas");
30802 var contextEl = canvasEl.getContext("2d");
30804 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30805 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30806 var center = this.imageEl.OriginWidth / 2;
30808 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30809 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30810 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30811 center = this.imageEl.OriginHeight / 2;
30814 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30816 contextEl.translate(center, center);
30817 contextEl.rotate(this.rotate * Math.PI / 180);
30819 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30821 this.canvasEl = document.createElement("canvas");
30823 this.contextEl = this.canvasEl.getContext("2d");
30825 switch (this.rotate) {
30828 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30829 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30831 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30836 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30837 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30839 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30840 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);
30844 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30849 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30850 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30852 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30853 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);
30857 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);
30862 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30863 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30865 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30866 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30870 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);
30877 this.previewEl.appendChild(this.canvasEl);
30879 this.setCanvasPosition();
30884 if(!this.canvasLoaded){
30888 var imageCanvas = document.createElement("canvas");
30890 var imageContext = imageCanvas.getContext("2d");
30892 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30893 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30895 var center = imageCanvas.width / 2;
30897 imageContext.translate(center, center);
30899 imageContext.rotate(this.rotate * Math.PI / 180);
30901 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30903 var canvas = document.createElement("canvas");
30905 var context = canvas.getContext("2d");
30907 canvas.width = this.minWidth;
30908 canvas.height = this.minHeight;
30910 switch (this.rotate) {
30913 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30914 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30916 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30917 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30919 var targetWidth = this.minWidth - 2 * x;
30920 var targetHeight = this.minHeight - 2 * y;
30924 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30925 scale = targetWidth / width;
30928 if(x > 0 && y == 0){
30929 scale = targetHeight / height;
30932 if(x > 0 && y > 0){
30933 scale = targetWidth / width;
30935 if(width < height){
30936 scale = targetHeight / height;
30940 context.scale(scale, scale);
30942 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30943 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30945 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30946 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30948 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30953 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30954 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30956 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30957 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30959 var targetWidth = this.minWidth - 2 * x;
30960 var targetHeight = this.minHeight - 2 * y;
30964 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30965 scale = targetWidth / width;
30968 if(x > 0 && y == 0){
30969 scale = targetHeight / height;
30972 if(x > 0 && y > 0){
30973 scale = targetWidth / width;
30975 if(width < height){
30976 scale = targetHeight / height;
30980 context.scale(scale, scale);
30982 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30983 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30985 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30986 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30988 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30990 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30995 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30996 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30998 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30999 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31001 var targetWidth = this.minWidth - 2 * x;
31002 var targetHeight = this.minHeight - 2 * y;
31006 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31007 scale = targetWidth / width;
31010 if(x > 0 && y == 0){
31011 scale = targetHeight / height;
31014 if(x > 0 && y > 0){
31015 scale = targetWidth / width;
31017 if(width < height){
31018 scale = targetHeight / height;
31022 context.scale(scale, scale);
31024 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31025 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31027 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31028 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31030 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31031 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31033 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31038 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31039 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31041 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31042 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31044 var targetWidth = this.minWidth - 2 * x;
31045 var targetHeight = this.minHeight - 2 * y;
31049 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31050 scale = targetWidth / width;
31053 if(x > 0 && y == 0){
31054 scale = targetHeight / height;
31057 if(x > 0 && y > 0){
31058 scale = targetWidth / width;
31060 if(width < height){
31061 scale = targetHeight / height;
31065 context.scale(scale, scale);
31067 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31068 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31070 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31071 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31073 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31075 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31082 this.cropData = canvas.toDataURL(this.cropType);
31084 if(this.fireEvent('crop', this, this.cropData) !== false){
31085 this.process(this.file, this.cropData);
31092 setThumbBoxSize : function()
31096 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31097 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31098 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31100 this.minWidth = width;
31101 this.minHeight = height;
31103 if(this.rotate == 90 || this.rotate == 270){
31104 this.minWidth = height;
31105 this.minHeight = width;
31110 width = Math.ceil(this.minWidth * height / this.minHeight);
31112 if(this.minWidth > this.minHeight){
31114 height = Math.ceil(this.minHeight * width / this.minWidth);
31117 this.thumbEl.setStyle({
31118 width : width + 'px',
31119 height : height + 'px'
31126 setThumbBoxPosition : function()
31128 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31129 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31131 this.thumbEl.setLeft(x);
31132 this.thumbEl.setTop(y);
31136 baseRotateLevel : function()
31138 this.baseRotate = 1;
31141 typeof(this.exif) != 'undefined' &&
31142 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31143 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31145 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31148 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31152 baseScaleLevel : function()
31156 if(this.isDocument){
31158 if(this.baseRotate == 6 || this.baseRotate == 8){
31160 height = this.thumbEl.getHeight();
31161 this.baseScale = height / this.imageEl.OriginWidth;
31163 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31164 width = this.thumbEl.getWidth();
31165 this.baseScale = width / this.imageEl.OriginHeight;
31171 height = this.thumbEl.getHeight();
31172 this.baseScale = height / this.imageEl.OriginHeight;
31174 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31175 width = this.thumbEl.getWidth();
31176 this.baseScale = width / this.imageEl.OriginWidth;
31182 if(this.baseRotate == 6 || this.baseRotate == 8){
31184 width = this.thumbEl.getHeight();
31185 this.baseScale = width / this.imageEl.OriginHeight;
31187 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31188 height = this.thumbEl.getWidth();
31189 this.baseScale = height / this.imageEl.OriginHeight;
31192 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31193 height = this.thumbEl.getWidth();
31194 this.baseScale = height / this.imageEl.OriginHeight;
31196 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31197 width = this.thumbEl.getHeight();
31198 this.baseScale = width / this.imageEl.OriginWidth;
31205 width = this.thumbEl.getWidth();
31206 this.baseScale = width / this.imageEl.OriginWidth;
31208 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31209 height = this.thumbEl.getHeight();
31210 this.baseScale = height / this.imageEl.OriginHeight;
31213 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31215 height = this.thumbEl.getHeight();
31216 this.baseScale = height / this.imageEl.OriginHeight;
31218 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31219 width = this.thumbEl.getWidth();
31220 this.baseScale = width / this.imageEl.OriginWidth;
31228 getScaleLevel : function()
31230 return this.baseScale * Math.pow(1.1, this.scale);
31233 onTouchStart : function(e)
31235 if(!this.canvasLoaded){
31236 this.beforeSelectFile(e);
31240 var touches = e.browserEvent.touches;
31246 if(touches.length == 1){
31247 this.onMouseDown(e);
31251 if(touches.length != 2){
31257 for(var i = 0, finger; finger = touches[i]; i++){
31258 coords.push(finger.pageX, finger.pageY);
31261 var x = Math.pow(coords[0] - coords[2], 2);
31262 var y = Math.pow(coords[1] - coords[3], 2);
31264 this.startDistance = Math.sqrt(x + y);
31266 this.startScale = this.scale;
31268 this.pinching = true;
31269 this.dragable = false;
31273 onTouchMove : function(e)
31275 if(!this.pinching && !this.dragable){
31279 var touches = e.browserEvent.touches;
31286 this.onMouseMove(e);
31292 for(var i = 0, finger; finger = touches[i]; i++){
31293 coords.push(finger.pageX, finger.pageY);
31296 var x = Math.pow(coords[0] - coords[2], 2);
31297 var y = Math.pow(coords[1] - coords[3], 2);
31299 this.endDistance = Math.sqrt(x + y);
31301 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31303 if(!this.zoomable()){
31304 this.scale = this.startScale;
31312 onTouchEnd : function(e)
31314 this.pinching = false;
31315 this.dragable = false;
31319 process : function(file, crop)
31322 this.maskEl.mask(this.loadingText);
31325 this.xhr = new XMLHttpRequest();
31327 file.xhr = this.xhr;
31329 this.xhr.open(this.method, this.url, true);
31332 "Accept": "application/json",
31333 "Cache-Control": "no-cache",
31334 "X-Requested-With": "XMLHttpRequest"
31337 for (var headerName in headers) {
31338 var headerValue = headers[headerName];
31340 this.xhr.setRequestHeader(headerName, headerValue);
31346 this.xhr.onload = function()
31348 _this.xhrOnLoad(_this.xhr);
31351 this.xhr.onerror = function()
31353 _this.xhrOnError(_this.xhr);
31356 var formData = new FormData();
31358 formData.append('returnHTML', 'NO');
31361 formData.append('crop', crop);
31364 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31365 formData.append(this.paramName, file, file.name);
31368 if(typeof(file.filename) != 'undefined'){
31369 formData.append('filename', file.filename);
31372 if(typeof(file.mimetype) != 'undefined'){
31373 formData.append('mimetype', file.mimetype);
31376 if(this.fireEvent('arrange', this, formData) != false){
31377 this.xhr.send(formData);
31381 xhrOnLoad : function(xhr)
31384 this.maskEl.unmask();
31387 if (xhr.readyState !== 4) {
31388 this.fireEvent('exception', this, xhr);
31392 var response = Roo.decode(xhr.responseText);
31394 if(!response.success){
31395 this.fireEvent('exception', this, xhr);
31399 var response = Roo.decode(xhr.responseText);
31401 this.fireEvent('upload', this, response);
31405 xhrOnError : function()
31408 this.maskEl.unmask();
31411 Roo.log('xhr on error');
31413 var response = Roo.decode(xhr.responseText);
31419 prepare : function(file)
31422 this.maskEl.mask(this.loadingText);
31428 if(typeof(file) === 'string'){
31429 this.loadCanvas(file);
31433 if(!file || !this.urlAPI){
31438 this.cropType = file.type;
31442 if(this.fireEvent('prepare', this, this.file) != false){
31444 var reader = new FileReader();
31446 reader.onload = function (e) {
31447 if (e.target.error) {
31448 Roo.log(e.target.error);
31452 var buffer = e.target.result,
31453 dataView = new DataView(buffer),
31455 maxOffset = dataView.byteLength - 4,
31459 if (dataView.getUint16(0) === 0xffd8) {
31460 while (offset < maxOffset) {
31461 markerBytes = dataView.getUint16(offset);
31463 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31464 markerLength = dataView.getUint16(offset + 2) + 2;
31465 if (offset + markerLength > dataView.byteLength) {
31466 Roo.log('Invalid meta data: Invalid segment size.');
31470 if(markerBytes == 0xffe1){
31471 _this.parseExifData(
31478 offset += markerLength;
31488 var url = _this.urlAPI.createObjectURL(_this.file);
31490 _this.loadCanvas(url);
31495 reader.readAsArrayBuffer(this.file);
31501 parseExifData : function(dataView, offset, length)
31503 var tiffOffset = offset + 10,
31507 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31508 // No Exif data, might be XMP data instead
31512 // Check for the ASCII code for "Exif" (0x45786966):
31513 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31514 // No Exif data, might be XMP data instead
31517 if (tiffOffset + 8 > dataView.byteLength) {
31518 Roo.log('Invalid Exif data: Invalid segment size.');
31521 // Check for the two null bytes:
31522 if (dataView.getUint16(offset + 8) !== 0x0000) {
31523 Roo.log('Invalid Exif data: Missing byte alignment offset.');
31526 // Check the byte alignment:
31527 switch (dataView.getUint16(tiffOffset)) {
31529 littleEndian = true;
31532 littleEndian = false;
31535 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31538 // Check for the TIFF tag marker (0x002A):
31539 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31540 Roo.log('Invalid Exif data: Missing TIFF marker.');
31543 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31544 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31546 this.parseExifTags(
31549 tiffOffset + dirOffset,
31554 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31559 if (dirOffset + 6 > dataView.byteLength) {
31560 Roo.log('Invalid Exif data: Invalid directory offset.');
31563 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31564 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31565 if (dirEndOffset + 4 > dataView.byteLength) {
31566 Roo.log('Invalid Exif data: Invalid directory size.');
31569 for (i = 0; i < tagsNumber; i += 1) {
31573 dirOffset + 2 + 12 * i, // tag offset
31577 // Return the offset to the next directory:
31578 return dataView.getUint32(dirEndOffset, littleEndian);
31581 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
31583 var tag = dataView.getUint16(offset, littleEndian);
31585 this.exif[tag] = this.getExifValue(
31589 dataView.getUint16(offset + 2, littleEndian), // tag type
31590 dataView.getUint32(offset + 4, littleEndian), // tag length
31595 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31597 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31606 Roo.log('Invalid Exif data: Invalid tag type.');
31610 tagSize = tagType.size * length;
31611 // Determine if the value is contained in the dataOffset bytes,
31612 // or if the value at the dataOffset is a pointer to the actual data:
31613 dataOffset = tagSize > 4 ?
31614 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31615 if (dataOffset + tagSize > dataView.byteLength) {
31616 Roo.log('Invalid Exif data: Invalid data offset.');
31619 if (length === 1) {
31620 return tagType.getValue(dataView, dataOffset, littleEndian);
31623 for (i = 0; i < length; i += 1) {
31624 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31627 if (tagType.ascii) {
31629 // Concatenate the chars:
31630 for (i = 0; i < values.length; i += 1) {
31632 // Ignore the terminating NULL byte(s):
31633 if (c === '\u0000') {
31645 Roo.apply(Roo.bootstrap.UploadCropbox, {
31647 'Orientation': 0x0112
31651 1: 0, //'top-left',
31653 3: 180, //'bottom-right',
31654 // 4: 'bottom-left',
31656 6: 90, //'right-top',
31657 // 7: 'right-bottom',
31658 8: 270 //'left-bottom'
31662 // byte, 8-bit unsigned int:
31664 getValue: function (dataView, dataOffset) {
31665 return dataView.getUint8(dataOffset);
31669 // ascii, 8-bit byte:
31671 getValue: function (dataView, dataOffset) {
31672 return String.fromCharCode(dataView.getUint8(dataOffset));
31677 // short, 16 bit int:
31679 getValue: function (dataView, dataOffset, littleEndian) {
31680 return dataView.getUint16(dataOffset, littleEndian);
31684 // long, 32 bit int:
31686 getValue: function (dataView, dataOffset, littleEndian) {
31687 return dataView.getUint32(dataOffset, littleEndian);
31691 // rational = two long values, first is numerator, second is denominator:
31693 getValue: function (dataView, dataOffset, littleEndian) {
31694 return dataView.getUint32(dataOffset, littleEndian) /
31695 dataView.getUint32(dataOffset + 4, littleEndian);
31699 // slong, 32 bit signed int:
31701 getValue: function (dataView, dataOffset, littleEndian) {
31702 return dataView.getInt32(dataOffset, littleEndian);
31706 // srational, two slongs, first is numerator, second is denominator:
31708 getValue: function (dataView, dataOffset, littleEndian) {
31709 return dataView.getInt32(dataOffset, littleEndian) /
31710 dataView.getInt32(dataOffset + 4, littleEndian);
31720 cls : 'btn-group roo-upload-cropbox-rotate-left',
31721 action : 'rotate-left',
31725 cls : 'btn btn-default',
31726 html : '<i class="fa fa-undo"></i>'
31732 cls : 'btn-group roo-upload-cropbox-picture',
31733 action : 'picture',
31737 cls : 'btn btn-default',
31738 html : '<i class="fa fa-picture-o"></i>'
31744 cls : 'btn-group roo-upload-cropbox-rotate-right',
31745 action : 'rotate-right',
31749 cls : 'btn btn-default',
31750 html : '<i class="fa fa-repeat"></i>'
31758 cls : 'btn-group roo-upload-cropbox-rotate-left',
31759 action : 'rotate-left',
31763 cls : 'btn btn-default',
31764 html : '<i class="fa fa-undo"></i>'
31770 cls : 'btn-group roo-upload-cropbox-download',
31771 action : 'download',
31775 cls : 'btn btn-default',
31776 html : '<i class="fa fa-download"></i>'
31782 cls : 'btn-group roo-upload-cropbox-crop',
31787 cls : 'btn btn-default',
31788 html : '<i class="fa fa-crop"></i>'
31794 cls : 'btn-group roo-upload-cropbox-trash',
31799 cls : 'btn btn-default',
31800 html : '<i class="fa fa-trash"></i>'
31806 cls : 'btn-group roo-upload-cropbox-rotate-right',
31807 action : 'rotate-right',
31811 cls : 'btn btn-default',
31812 html : '<i class="fa fa-repeat"></i>'
31820 cls : 'btn-group roo-upload-cropbox-rotate-left',
31821 action : 'rotate-left',
31825 cls : 'btn btn-default',
31826 html : '<i class="fa fa-undo"></i>'
31832 cls : 'btn-group roo-upload-cropbox-rotate-right',
31833 action : 'rotate-right',
31837 cls : 'btn btn-default',
31838 html : '<i class="fa fa-repeat"></i>'
31851 * @class Roo.bootstrap.DocumentManager
31852 * @extends Roo.bootstrap.Component
31853 * Bootstrap DocumentManager class
31854 * @cfg {String} paramName default 'imageUpload'
31855 * @cfg {String} toolTipName default 'filename'
31856 * @cfg {String} method default POST
31857 * @cfg {String} url action url
31858 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31859 * @cfg {Boolean} multiple multiple upload default true
31860 * @cfg {Number} thumbSize default 300
31861 * @cfg {String} fieldLabel
31862 * @cfg {Number} labelWidth default 4
31863 * @cfg {String} labelAlign (left|top) default left
31864 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31865 * @cfg {Number} labellg set the width of label (1-12)
31866 * @cfg {Number} labelmd set the width of label (1-12)
31867 * @cfg {Number} labelsm set the width of label (1-12)
31868 * @cfg {Number} labelxs set the width of label (1-12)
31871 * Create a new DocumentManager
31872 * @param {Object} config The config object
31875 Roo.bootstrap.DocumentManager = function(config){
31876 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31879 this.delegates = [];
31884 * Fire when initial the DocumentManager
31885 * @param {Roo.bootstrap.DocumentManager} this
31890 * inspect selected file
31891 * @param {Roo.bootstrap.DocumentManager} this
31892 * @param {File} file
31897 * Fire when xhr load exception
31898 * @param {Roo.bootstrap.DocumentManager} this
31899 * @param {XMLHttpRequest} xhr
31901 "exception" : true,
31903 * @event afterupload
31904 * Fire when xhr load exception
31905 * @param {Roo.bootstrap.DocumentManager} this
31906 * @param {XMLHttpRequest} xhr
31908 "afterupload" : true,
31911 * prepare the form data
31912 * @param {Roo.bootstrap.DocumentManager} this
31913 * @param {Object} formData
31918 * Fire when remove the file
31919 * @param {Roo.bootstrap.DocumentManager} this
31920 * @param {Object} file
31925 * Fire after refresh the file
31926 * @param {Roo.bootstrap.DocumentManager} this
31931 * Fire after click the image
31932 * @param {Roo.bootstrap.DocumentManager} this
31933 * @param {Object} file
31938 * Fire when upload a image and editable set to true
31939 * @param {Roo.bootstrap.DocumentManager} this
31940 * @param {Object} file
31944 * @event beforeselectfile
31945 * Fire before select file
31946 * @param {Roo.bootstrap.DocumentManager} this
31948 "beforeselectfile" : true,
31951 * Fire before process file
31952 * @param {Roo.bootstrap.DocumentManager} this
31953 * @param {Object} file
31957 * @event previewrendered
31958 * Fire when preview rendered
31959 * @param {Roo.bootstrap.DocumentManager} this
31960 * @param {Object} file
31962 "previewrendered" : true,
31965 "previewResize" : true
31970 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
31979 paramName : 'imageUpload',
31980 toolTipName : 'filename',
31983 labelAlign : 'left',
31993 getAutoCreate : function()
31995 var managerWidget = {
31997 cls : 'roo-document-manager',
32001 cls : 'roo-document-manager-selector',
32006 cls : 'roo-document-manager-uploader',
32010 cls : 'roo-document-manager-upload-btn',
32011 html : '<i class="fa fa-plus"></i>'
32022 cls : 'column col-md-12',
32027 if(this.fieldLabel.length){
32032 cls : 'column col-md-12',
32033 html : this.fieldLabel
32037 cls : 'column col-md-12',
32042 if(this.labelAlign == 'left'){
32047 html : this.fieldLabel
32056 if(this.labelWidth > 12){
32057 content[0].style = "width: " + this.labelWidth + 'px';
32060 if(this.labelWidth < 13 && this.labelmd == 0){
32061 this.labelmd = this.labelWidth;
32064 if(this.labellg > 0){
32065 content[0].cls += ' col-lg-' + this.labellg;
32066 content[1].cls += ' col-lg-' + (12 - this.labellg);
32069 if(this.labelmd > 0){
32070 content[0].cls += ' col-md-' + this.labelmd;
32071 content[1].cls += ' col-md-' + (12 - this.labelmd);
32074 if(this.labelsm > 0){
32075 content[0].cls += ' col-sm-' + this.labelsm;
32076 content[1].cls += ' col-sm-' + (12 - this.labelsm);
32079 if(this.labelxs > 0){
32080 content[0].cls += ' col-xs-' + this.labelxs;
32081 content[1].cls += ' col-xs-' + (12 - this.labelxs);
32089 cls : 'row clearfix',
32097 initEvents : function()
32099 this.managerEl = this.el.select('.roo-document-manager', true).first();
32100 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32102 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32103 this.selectorEl.hide();
32106 this.selectorEl.attr('multiple', 'multiple');
32109 this.selectorEl.on('change', this.onFileSelected, this);
32111 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32112 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32114 this.uploader.on('click', this.onUploaderClick, this);
32116 this.renderProgressDialog();
32120 window.addEventListener("resize", function() { _this.refresh(); } );
32122 this.fireEvent('initial', this);
32125 renderProgressDialog : function()
32129 this.progressDialog = new Roo.bootstrap.Modal({
32130 cls : 'roo-document-manager-progress-dialog',
32131 allow_close : false,
32142 btnclick : function() {
32143 _this.uploadCancel();
32149 this.progressDialog.render(Roo.get(document.body));
32151 this.progress = new Roo.bootstrap.Progress({
32152 cls : 'roo-document-manager-progress',
32157 this.progress.render(this.progressDialog.getChildContainer());
32159 this.progressBar = new Roo.bootstrap.ProgressBar({
32160 cls : 'roo-document-manager-progress-bar',
32163 aria_valuemax : 12,
32167 this.progressBar.render(this.progress.getChildContainer());
32170 onUploaderClick : function(e)
32172 e.preventDefault();
32174 if(this.fireEvent('beforeselectfile', this) != false){
32175 this.selectorEl.dom.click();
32180 onFileSelected : function(e)
32182 e.preventDefault();
32184 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32188 Roo.each(this.selectorEl.dom.files, function(file){
32189 if(this.fireEvent('inspect', this, file) != false){
32190 this.files.push(file);
32200 this.selectorEl.dom.value = '';
32202 if(!this.files || !this.files.length){
32206 if(this.boxes > 0 && this.files.length > this.boxes){
32207 this.files = this.files.slice(0, this.boxes);
32210 this.uploader.show();
32212 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32213 this.uploader.hide();
32222 Roo.each(this.files, function(file){
32224 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32225 var f = this.renderPreview(file);
32230 if(file.type.indexOf('image') != -1){
32231 this.delegates.push(
32233 _this.process(file);
32234 }).createDelegate(this)
32242 _this.process(file);
32243 }).createDelegate(this)
32248 this.files = files;
32250 this.delegates = this.delegates.concat(docs);
32252 if(!this.delegates.length){
32257 this.progressBar.aria_valuemax = this.delegates.length;
32264 arrange : function()
32266 if(!this.delegates.length){
32267 this.progressDialog.hide();
32272 var delegate = this.delegates.shift();
32274 this.progressDialog.show();
32276 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32278 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32283 refresh : function()
32285 this.uploader.show();
32287 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32288 this.uploader.hide();
32291 Roo.isTouch ? this.closable(false) : this.closable(true);
32293 this.fireEvent('refresh', this);
32296 onRemove : function(e, el, o)
32298 e.preventDefault();
32300 this.fireEvent('remove', this, o);
32304 remove : function(o)
32308 Roo.each(this.files, function(file){
32309 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32318 this.files = files;
32325 Roo.each(this.files, function(file){
32330 file.target.remove();
32339 onClick : function(e, el, o)
32341 e.preventDefault();
32343 this.fireEvent('click', this, o);
32347 closable : function(closable)
32349 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32351 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32363 xhrOnLoad : function(xhr)
32365 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32369 if (xhr.readyState !== 4) {
32371 this.fireEvent('exception', this, xhr);
32375 var response = Roo.decode(xhr.responseText);
32377 if(!response.success){
32379 this.fireEvent('exception', this, xhr);
32383 var file = this.renderPreview(response.data);
32385 this.files.push(file);
32389 this.fireEvent('afterupload', this, xhr);
32393 xhrOnError : function(xhr)
32395 Roo.log('xhr on error');
32397 var response = Roo.decode(xhr.responseText);
32404 process : function(file)
32406 if(this.fireEvent('process', this, file) !== false){
32407 if(this.editable && file.type.indexOf('image') != -1){
32408 this.fireEvent('edit', this, file);
32412 this.uploadStart(file, false);
32419 uploadStart : function(file, crop)
32421 this.xhr = new XMLHttpRequest();
32423 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32428 file.xhr = this.xhr;
32430 this.managerEl.createChild({
32432 cls : 'roo-document-manager-loading',
32436 tooltip : file.name,
32437 cls : 'roo-document-manager-thumb',
32438 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32444 this.xhr.open(this.method, this.url, true);
32447 "Accept": "application/json",
32448 "Cache-Control": "no-cache",
32449 "X-Requested-With": "XMLHttpRequest"
32452 for (var headerName in headers) {
32453 var headerValue = headers[headerName];
32455 this.xhr.setRequestHeader(headerName, headerValue);
32461 this.xhr.onload = function()
32463 _this.xhrOnLoad(_this.xhr);
32466 this.xhr.onerror = function()
32468 _this.xhrOnError(_this.xhr);
32471 var formData = new FormData();
32473 formData.append('returnHTML', 'NO');
32476 formData.append('crop', crop);
32479 formData.append(this.paramName, file, file.name);
32486 if(this.fireEvent('prepare', this, formData, options) != false){
32488 if(options.manually){
32492 this.xhr.send(formData);
32496 this.uploadCancel();
32499 uploadCancel : function()
32505 this.delegates = [];
32507 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32514 renderPreview : function(file)
32516 if(typeof(file.target) != 'undefined' && file.target){
32520 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32522 var previewEl = this.managerEl.createChild({
32524 cls : 'roo-document-manager-preview',
32528 tooltip : file[this.toolTipName],
32529 cls : 'roo-document-manager-thumb',
32530 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32535 html : '<i class="fa fa-times-circle"></i>'
32540 var close = previewEl.select('button.close', true).first();
32542 close.on('click', this.onRemove, this, file);
32544 file.target = previewEl;
32546 var image = previewEl.select('img', true).first();
32550 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32552 image.on('click', this.onClick, this, file);
32554 this.fireEvent('previewrendered', this, file);
32560 onPreviewLoad : function(file, image)
32562 if(typeof(file.target) == 'undefined' || !file.target){
32566 var width = image.dom.naturalWidth || image.dom.width;
32567 var height = image.dom.naturalHeight || image.dom.height;
32569 if(!this.previewResize) {
32573 if(width > height){
32574 file.target.addClass('wide');
32578 file.target.addClass('tall');
32583 uploadFromSource : function(file, crop)
32585 this.xhr = new XMLHttpRequest();
32587 this.managerEl.createChild({
32589 cls : 'roo-document-manager-loading',
32593 tooltip : file.name,
32594 cls : 'roo-document-manager-thumb',
32595 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32601 this.xhr.open(this.method, this.url, true);
32604 "Accept": "application/json",
32605 "Cache-Control": "no-cache",
32606 "X-Requested-With": "XMLHttpRequest"
32609 for (var headerName in headers) {
32610 var headerValue = headers[headerName];
32612 this.xhr.setRequestHeader(headerName, headerValue);
32618 this.xhr.onload = function()
32620 _this.xhrOnLoad(_this.xhr);
32623 this.xhr.onerror = function()
32625 _this.xhrOnError(_this.xhr);
32628 var formData = new FormData();
32630 formData.append('returnHTML', 'NO');
32632 formData.append('crop', crop);
32634 if(typeof(file.filename) != 'undefined'){
32635 formData.append('filename', file.filename);
32638 if(typeof(file.mimetype) != 'undefined'){
32639 formData.append('mimetype', file.mimetype);
32644 if(this.fireEvent('prepare', this, formData) != false){
32645 this.xhr.send(formData);
32655 * @class Roo.bootstrap.DocumentViewer
32656 * @extends Roo.bootstrap.Component
32657 * Bootstrap DocumentViewer class
32658 * @cfg {Boolean} showDownload (true|false) show download button (default true)
32659 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32662 * Create a new DocumentViewer
32663 * @param {Object} config The config object
32666 Roo.bootstrap.DocumentViewer = function(config){
32667 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32672 * Fire after initEvent
32673 * @param {Roo.bootstrap.DocumentViewer} this
32679 * @param {Roo.bootstrap.DocumentViewer} this
32684 * Fire after download button
32685 * @param {Roo.bootstrap.DocumentViewer} this
32690 * Fire after trash button
32691 * @param {Roo.bootstrap.DocumentViewer} this
32698 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
32700 showDownload : true,
32704 getAutoCreate : function()
32708 cls : 'roo-document-viewer',
32712 cls : 'roo-document-viewer-body',
32716 cls : 'roo-document-viewer-thumb',
32720 cls : 'roo-document-viewer-image'
32728 cls : 'roo-document-viewer-footer',
32731 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32735 cls : 'btn-group roo-document-viewer-download',
32739 cls : 'btn btn-default',
32740 html : '<i class="fa fa-download"></i>'
32746 cls : 'btn-group roo-document-viewer-trash',
32750 cls : 'btn btn-default',
32751 html : '<i class="fa fa-trash"></i>'
32764 initEvents : function()
32766 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32767 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32769 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32770 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32772 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32773 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32775 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32776 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32778 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32779 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32781 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32782 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32784 this.bodyEl.on('click', this.onClick, this);
32785 this.downloadBtn.on('click', this.onDownload, this);
32786 this.trashBtn.on('click', this.onTrash, this);
32788 this.downloadBtn.hide();
32789 this.trashBtn.hide();
32791 if(this.showDownload){
32792 this.downloadBtn.show();
32795 if(this.showTrash){
32796 this.trashBtn.show();
32799 if(!this.showDownload && !this.showTrash) {
32800 this.footerEl.hide();
32805 initial : function()
32807 this.fireEvent('initial', this);
32811 onClick : function(e)
32813 e.preventDefault();
32815 this.fireEvent('click', this);
32818 onDownload : function(e)
32820 e.preventDefault();
32822 this.fireEvent('download', this);
32825 onTrash : function(e)
32827 e.preventDefault();
32829 this.fireEvent('trash', this);
32841 * @class Roo.bootstrap.NavProgressBar
32842 * @extends Roo.bootstrap.Component
32843 * Bootstrap NavProgressBar class
32846 * Create a new nav progress bar
32847 * @param {Object} config The config object
32850 Roo.bootstrap.NavProgressBar = function(config){
32851 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32853 this.bullets = this.bullets || [];
32855 // Roo.bootstrap.NavProgressBar.register(this);
32859 * Fires when the active item changes
32860 * @param {Roo.bootstrap.NavProgressBar} this
32861 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32862 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
32869 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
32874 getAutoCreate : function()
32876 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32880 cls : 'roo-navigation-bar-group',
32884 cls : 'roo-navigation-top-bar'
32888 cls : 'roo-navigation-bullets-bar',
32892 cls : 'roo-navigation-bar'
32899 cls : 'roo-navigation-bottom-bar'
32909 initEvents: function()
32914 onRender : function(ct, position)
32916 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32918 if(this.bullets.length){
32919 Roo.each(this.bullets, function(b){
32928 addItem : function(cfg)
32930 var item = new Roo.bootstrap.NavProgressItem(cfg);
32932 item.parentId = this.id;
32933 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32936 var top = new Roo.bootstrap.Element({
32938 cls : 'roo-navigation-bar-text'
32941 var bottom = new Roo.bootstrap.Element({
32943 cls : 'roo-navigation-bar-text'
32946 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32947 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32949 var topText = new Roo.bootstrap.Element({
32951 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32954 var bottomText = new Roo.bootstrap.Element({
32956 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32959 topText.onRender(top.el, null);
32960 bottomText.onRender(bottom.el, null);
32963 item.bottomEl = bottom;
32966 this.barItems.push(item);
32971 getActive : function()
32973 var active = false;
32975 Roo.each(this.barItems, function(v){
32977 if (!v.isActive()) {
32989 setActiveItem : function(item)
32993 Roo.each(this.barItems, function(v){
32994 if (v.rid == item.rid) {
32998 if (v.isActive()) {
32999 v.setActive(false);
33004 item.setActive(true);
33006 this.fireEvent('changed', this, item, prev);
33009 getBarItem: function(rid)
33013 Roo.each(this.barItems, function(e) {
33014 if (e.rid != rid) {
33025 indexOfItem : function(item)
33029 Roo.each(this.barItems, function(v, i){
33031 if (v.rid != item.rid) {
33042 setActiveNext : function()
33044 var i = this.indexOfItem(this.getActive());
33046 if (i > this.barItems.length) {
33050 this.setActiveItem(this.barItems[i+1]);
33053 setActivePrev : function()
33055 var i = this.indexOfItem(this.getActive());
33061 this.setActiveItem(this.barItems[i-1]);
33064 format : function()
33066 if(!this.barItems.length){
33070 var width = 100 / this.barItems.length;
33072 Roo.each(this.barItems, function(i){
33073 i.el.setStyle('width', width + '%');
33074 i.topEl.el.setStyle('width', width + '%');
33075 i.bottomEl.el.setStyle('width', width + '%');
33084 * Nav Progress Item
33089 * @class Roo.bootstrap.NavProgressItem
33090 * @extends Roo.bootstrap.Component
33091 * Bootstrap NavProgressItem class
33092 * @cfg {String} rid the reference id
33093 * @cfg {Boolean} active (true|false) Is item active default false
33094 * @cfg {Boolean} disabled (true|false) Is item active default false
33095 * @cfg {String} html
33096 * @cfg {String} position (top|bottom) text position default bottom
33097 * @cfg {String} icon show icon instead of number
33100 * Create a new NavProgressItem
33101 * @param {Object} config The config object
33103 Roo.bootstrap.NavProgressItem = function(config){
33104 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33109 * The raw click event for the entire grid.
33110 * @param {Roo.bootstrap.NavProgressItem} this
33111 * @param {Roo.EventObject} e
33118 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
33124 position : 'bottom',
33127 getAutoCreate : function()
33129 var iconCls = 'roo-navigation-bar-item-icon';
33131 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33135 cls: 'roo-navigation-bar-item',
33145 cfg.cls += ' active';
33148 cfg.cls += ' disabled';
33154 disable : function()
33156 this.setDisabled(true);
33159 enable : function()
33161 this.setDisabled(false);
33164 initEvents: function()
33166 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33168 this.iconEl.on('click', this.onClick, this);
33171 onClick : function(e)
33173 e.preventDefault();
33179 if(this.fireEvent('click', this, e) === false){
33183 this.parent().setActiveItem(this);
33186 isActive: function ()
33188 return this.active;
33191 setActive : function(state)
33193 if(this.active == state){
33197 this.active = state;
33200 this.el.addClass('active');
33204 this.el.removeClass('active');
33209 setDisabled : function(state)
33211 if(this.disabled == state){
33215 this.disabled = state;
33218 this.el.addClass('disabled');
33222 this.el.removeClass('disabled');
33225 tooltipEl : function()
33227 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33240 * @class Roo.bootstrap.FieldLabel
33241 * @extends Roo.bootstrap.Component
33242 * Bootstrap FieldLabel class
33243 * @cfg {String} html contents of the element
33244 * @cfg {String} tag tag of the element default label
33245 * @cfg {String} cls class of the element
33246 * @cfg {String} target label target
33247 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33248 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33249 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33250 * @cfg {String} iconTooltip default "This field is required"
33251 * @cfg {String} indicatorpos (left|right) default left
33254 * Create a new FieldLabel
33255 * @param {Object} config The config object
33258 Roo.bootstrap.FieldLabel = function(config){
33259 Roo.bootstrap.Element.superclass.constructor.call(this, config);
33264 * Fires after the field has been marked as invalid.
33265 * @param {Roo.form.FieldLabel} this
33266 * @param {String} msg The validation message
33271 * Fires after the field has been validated with no errors.
33272 * @param {Roo.form.FieldLabel} this
33278 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
33285 invalidClass : 'has-warning',
33286 validClass : 'has-success',
33287 iconTooltip : 'This field is required',
33288 indicatorpos : 'left',
33290 getAutoCreate : function(){
33293 if (!this.allowBlank) {
33299 cls : 'roo-bootstrap-field-label ' + this.cls,
33304 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33305 tooltip : this.iconTooltip
33314 if(this.indicatorpos == 'right'){
33317 cls : 'roo-bootstrap-field-label ' + this.cls,
33326 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33327 tooltip : this.iconTooltip
33336 initEvents: function()
33338 Roo.bootstrap.Element.superclass.initEvents.call(this);
33340 this.indicator = this.indicatorEl();
33342 if(this.indicator){
33343 this.indicator.removeClass('visible');
33344 this.indicator.addClass('invisible');
33347 Roo.bootstrap.FieldLabel.register(this);
33350 indicatorEl : function()
33352 var indicator = this.el.select('i.roo-required-indicator',true).first();
33363 * Mark this field as valid
33365 markValid : function()
33367 if(this.indicator){
33368 this.indicator.removeClass('visible');
33369 this.indicator.addClass('invisible');
33371 if (Roo.bootstrap.version == 3) {
33372 this.el.removeClass(this.invalidClass);
33373 this.el.addClass(this.validClass);
33375 this.el.removeClass('is-invalid');
33376 this.el.addClass('is-valid');
33380 this.fireEvent('valid', this);
33384 * Mark this field as invalid
33385 * @param {String} msg The validation message
33387 markInvalid : function(msg)
33389 if(this.indicator){
33390 this.indicator.removeClass('invisible');
33391 this.indicator.addClass('visible');
33393 if (Roo.bootstrap.version == 3) {
33394 this.el.removeClass(this.validClass);
33395 this.el.addClass(this.invalidClass);
33397 this.el.removeClass('is-valid');
33398 this.el.addClass('is-invalid');
33402 this.fireEvent('invalid', this, msg);
33408 Roo.apply(Roo.bootstrap.FieldLabel, {
33413 * register a FieldLabel Group
33414 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33416 register : function(label)
33418 if(this.groups.hasOwnProperty(label.target)){
33422 this.groups[label.target] = label;
33426 * fetch a FieldLabel Group based on the target
33427 * @param {string} target
33428 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33430 get: function(target) {
33431 if (typeof(this.groups[target]) == 'undefined') {
33435 return this.groups[target] ;
33444 * page DateSplitField.
33450 * @class Roo.bootstrap.DateSplitField
33451 * @extends Roo.bootstrap.Component
33452 * Bootstrap DateSplitField class
33453 * @cfg {string} fieldLabel - the label associated
33454 * @cfg {Number} labelWidth set the width of label (0-12)
33455 * @cfg {String} labelAlign (top|left)
33456 * @cfg {Boolean} dayAllowBlank (true|false) default false
33457 * @cfg {Boolean} monthAllowBlank (true|false) default false
33458 * @cfg {Boolean} yearAllowBlank (true|false) default false
33459 * @cfg {string} dayPlaceholder
33460 * @cfg {string} monthPlaceholder
33461 * @cfg {string} yearPlaceholder
33462 * @cfg {string} dayFormat default 'd'
33463 * @cfg {string} monthFormat default 'm'
33464 * @cfg {string} yearFormat default 'Y'
33465 * @cfg {Number} labellg set the width of label (1-12)
33466 * @cfg {Number} labelmd set the width of label (1-12)
33467 * @cfg {Number} labelsm set the width of label (1-12)
33468 * @cfg {Number} labelxs set the width of label (1-12)
33472 * Create a new DateSplitField
33473 * @param {Object} config The config object
33476 Roo.bootstrap.DateSplitField = function(config){
33477 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33483 * getting the data of years
33484 * @param {Roo.bootstrap.DateSplitField} this
33485 * @param {Object} years
33490 * getting the data of days
33491 * @param {Roo.bootstrap.DateSplitField} this
33492 * @param {Object} days
33497 * Fires after the field has been marked as invalid.
33498 * @param {Roo.form.Field} this
33499 * @param {String} msg The validation message
33504 * Fires after the field has been validated with no errors.
33505 * @param {Roo.form.Field} this
33511 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33514 labelAlign : 'top',
33516 dayAllowBlank : false,
33517 monthAllowBlank : false,
33518 yearAllowBlank : false,
33519 dayPlaceholder : '',
33520 monthPlaceholder : '',
33521 yearPlaceholder : '',
33525 isFormField : true,
33531 getAutoCreate : function()
33535 cls : 'row roo-date-split-field-group',
33540 cls : 'form-hidden-field roo-date-split-field-group-value',
33546 var labelCls = 'col-md-12';
33547 var contentCls = 'col-md-4';
33549 if(this.fieldLabel){
33553 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33557 html : this.fieldLabel
33562 if(this.labelAlign == 'left'){
33564 if(this.labelWidth > 12){
33565 label.style = "width: " + this.labelWidth + 'px';
33568 if(this.labelWidth < 13 && this.labelmd == 0){
33569 this.labelmd = this.labelWidth;
33572 if(this.labellg > 0){
33573 labelCls = ' col-lg-' + this.labellg;
33574 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33577 if(this.labelmd > 0){
33578 labelCls = ' col-md-' + this.labelmd;
33579 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33582 if(this.labelsm > 0){
33583 labelCls = ' col-sm-' + this.labelsm;
33584 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33587 if(this.labelxs > 0){
33588 labelCls = ' col-xs-' + this.labelxs;
33589 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33593 label.cls += ' ' + labelCls;
33595 cfg.cn.push(label);
33598 Roo.each(['day', 'month', 'year'], function(t){
33601 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33608 inputEl: function ()
33610 return this.el.select('.roo-date-split-field-group-value', true).first();
33613 onRender : function(ct, position)
33617 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33619 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33621 this.dayField = new Roo.bootstrap.ComboBox({
33622 allowBlank : this.dayAllowBlank,
33623 alwaysQuery : true,
33624 displayField : 'value',
33627 forceSelection : true,
33629 placeholder : this.dayPlaceholder,
33630 selectOnFocus : true,
33631 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33632 triggerAction : 'all',
33634 valueField : 'value',
33635 store : new Roo.data.SimpleStore({
33636 data : (function() {
33638 _this.fireEvent('days', _this, days);
33641 fields : [ 'value' ]
33644 select : function (_self, record, index)
33646 _this.setValue(_this.getValue());
33651 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33653 this.monthField = new Roo.bootstrap.MonthField({
33654 after : '<i class=\"fa fa-calendar\"></i>',
33655 allowBlank : this.monthAllowBlank,
33656 placeholder : this.monthPlaceholder,
33659 render : function (_self)
33661 this.el.select('span.input-group-addon', true).first().on('click', function(e){
33662 e.preventDefault();
33666 select : function (_self, oldvalue, newvalue)
33668 _this.setValue(_this.getValue());
33673 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33675 this.yearField = new Roo.bootstrap.ComboBox({
33676 allowBlank : this.yearAllowBlank,
33677 alwaysQuery : true,
33678 displayField : 'value',
33681 forceSelection : true,
33683 placeholder : this.yearPlaceholder,
33684 selectOnFocus : true,
33685 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33686 triggerAction : 'all',
33688 valueField : 'value',
33689 store : new Roo.data.SimpleStore({
33690 data : (function() {
33692 _this.fireEvent('years', _this, years);
33695 fields : [ 'value' ]
33698 select : function (_self, record, index)
33700 _this.setValue(_this.getValue());
33705 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33708 setValue : function(v, format)
33710 this.inputEl.dom.value = v;
33712 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33714 var d = Date.parseDate(v, f);
33721 this.setDay(d.format(this.dayFormat));
33722 this.setMonth(d.format(this.monthFormat));
33723 this.setYear(d.format(this.yearFormat));
33730 setDay : function(v)
33732 this.dayField.setValue(v);
33733 this.inputEl.dom.value = this.getValue();
33738 setMonth : function(v)
33740 this.monthField.setValue(v, true);
33741 this.inputEl.dom.value = this.getValue();
33746 setYear : function(v)
33748 this.yearField.setValue(v);
33749 this.inputEl.dom.value = this.getValue();
33754 getDay : function()
33756 return this.dayField.getValue();
33759 getMonth : function()
33761 return this.monthField.getValue();
33764 getYear : function()
33766 return this.yearField.getValue();
33769 getValue : function()
33771 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33773 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33783 this.inputEl.dom.value = '';
33788 validate : function()
33790 var d = this.dayField.validate();
33791 var m = this.monthField.validate();
33792 var y = this.yearField.validate();
33797 (!this.dayAllowBlank && !d) ||
33798 (!this.monthAllowBlank && !m) ||
33799 (!this.yearAllowBlank && !y)
33804 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33813 this.markInvalid();
33818 markValid : function()
33821 var label = this.el.select('label', true).first();
33822 var icon = this.el.select('i.fa-star', true).first();
33828 this.fireEvent('valid', this);
33832 * Mark this field as invalid
33833 * @param {String} msg The validation message
33835 markInvalid : function(msg)
33838 var label = this.el.select('label', true).first();
33839 var icon = this.el.select('i.fa-star', true).first();
33841 if(label && !icon){
33842 this.el.select('.roo-date-split-field-label', true).createChild({
33844 cls : 'text-danger fa fa-lg fa-star',
33845 tooltip : 'This field is required',
33846 style : 'margin-right:5px;'
33850 this.fireEvent('invalid', this, msg);
33853 clearInvalid : function()
33855 var label = this.el.select('label', true).first();
33856 var icon = this.el.select('i.fa-star', true).first();
33862 this.fireEvent('valid', this);
33865 getName: function()
33875 * http://masonry.desandro.com
33877 * The idea is to render all the bricks based on vertical width...
33879 * The original code extends 'outlayer' - we might need to use that....
33885 * @class Roo.bootstrap.LayoutMasonry
33886 * @extends Roo.bootstrap.Component
33887 * Bootstrap Layout Masonry class
33890 * Create a new Element
33891 * @param {Object} config The config object
33894 Roo.bootstrap.LayoutMasonry = function(config){
33896 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33900 Roo.bootstrap.LayoutMasonry.register(this);
33906 * Fire after layout the items
33907 * @param {Roo.bootstrap.LayoutMasonry} this
33908 * @param {Roo.EventObject} e
33915 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
33918 * @cfg {Boolean} isLayoutInstant = no animation?
33920 isLayoutInstant : false, // needed?
33923 * @cfg {Number} boxWidth width of the columns
33928 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
33933 * @cfg {Number} padWidth padding below box..
33938 * @cfg {Number} gutter gutter width..
33943 * @cfg {Number} maxCols maximum number of columns
33949 * @cfg {Boolean} isAutoInitial defalut true
33951 isAutoInitial : true,
33956 * @cfg {Boolean} isHorizontal defalut false
33958 isHorizontal : false,
33960 currentSize : null,
33966 bricks: null, //CompositeElement
33970 _isLayoutInited : false,
33972 // isAlternative : false, // only use for vertical layout...
33975 * @cfg {Number} alternativePadWidth padding below box..
33977 alternativePadWidth : 50,
33979 selectedBrick : [],
33981 getAutoCreate : function(){
33983 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33987 cls: 'blog-masonary-wrapper ' + this.cls,
33989 cls : 'mas-boxes masonary'
33996 getChildContainer: function( )
33998 if (this.boxesEl) {
33999 return this.boxesEl;
34002 this.boxesEl = this.el.select('.mas-boxes').first();
34004 return this.boxesEl;
34008 initEvents : function()
34012 if(this.isAutoInitial){
34013 Roo.log('hook children rendered');
34014 this.on('childrenrendered', function() {
34015 Roo.log('children rendered');
34021 initial : function()
34023 this.selectedBrick = [];
34025 this.currentSize = this.el.getBox(true);
34027 Roo.EventManager.onWindowResize(this.resize, this);
34029 if(!this.isAutoInitial){
34037 //this.layout.defer(500,this);
34041 resize : function()
34043 var cs = this.el.getBox(true);
34046 this.currentSize.width == cs.width &&
34047 this.currentSize.x == cs.x &&
34048 this.currentSize.height == cs.height &&
34049 this.currentSize.y == cs.y
34051 Roo.log("no change in with or X or Y");
34055 this.currentSize = cs;
34061 layout : function()
34063 this._resetLayout();
34065 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34067 this.layoutItems( isInstant );
34069 this._isLayoutInited = true;
34071 this.fireEvent('layout', this);
34075 _resetLayout : function()
34077 if(this.isHorizontal){
34078 this.horizontalMeasureColumns();
34082 this.verticalMeasureColumns();
34086 verticalMeasureColumns : function()
34088 this.getContainerWidth();
34090 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34091 // this.colWidth = Math.floor(this.containerWidth * 0.8);
34095 var boxWidth = this.boxWidth + this.padWidth;
34097 if(this.containerWidth < this.boxWidth){
34098 boxWidth = this.containerWidth
34101 var containerWidth = this.containerWidth;
34103 var cols = Math.floor(containerWidth / boxWidth);
34105 this.cols = Math.max( cols, 1 );
34107 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34109 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34111 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34113 this.colWidth = boxWidth + avail - this.padWidth;
34115 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34116 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
34119 horizontalMeasureColumns : function()
34121 this.getContainerWidth();
34123 var boxWidth = this.boxWidth;
34125 if(this.containerWidth < boxWidth){
34126 boxWidth = this.containerWidth;
34129 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34131 this.el.setHeight(boxWidth);
34135 getContainerWidth : function()
34137 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34140 layoutItems : function( isInstant )
34142 Roo.log(this.bricks);
34144 var items = Roo.apply([], this.bricks);
34146 if(this.isHorizontal){
34147 this._horizontalLayoutItems( items , isInstant );
34151 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34152 // this._verticalAlternativeLayoutItems( items , isInstant );
34156 this._verticalLayoutItems( items , isInstant );
34160 _verticalLayoutItems : function ( items , isInstant)
34162 if ( !items || !items.length ) {
34167 ['xs', 'xs', 'xs', 'tall'],
34168 ['xs', 'xs', 'tall'],
34169 ['xs', 'xs', 'sm'],
34170 ['xs', 'xs', 'xs'],
34176 ['sm', 'xs', 'xs'],
34180 ['tall', 'xs', 'xs', 'xs'],
34181 ['tall', 'xs', 'xs'],
34193 Roo.each(items, function(item, k){
34195 switch (item.size) {
34196 // these layouts take up a full box,
34207 boxes.push([item]);
34230 var filterPattern = function(box, length)
34238 var pattern = box.slice(0, length);
34242 Roo.each(pattern, function(i){
34243 format.push(i.size);
34246 Roo.each(standard, function(s){
34248 if(String(s) != String(format)){
34257 if(!match && length == 1){
34262 filterPattern(box, length - 1);
34266 queue.push(pattern);
34268 box = box.slice(length, box.length);
34270 filterPattern(box, 4);
34276 Roo.each(boxes, function(box, k){
34282 if(box.length == 1){
34287 filterPattern(box, 4);
34291 this._processVerticalLayoutQueue( queue, isInstant );
34295 // _verticalAlternativeLayoutItems : function( items , isInstant )
34297 // if ( !items || !items.length ) {
34301 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
34305 _horizontalLayoutItems : function ( items , isInstant)
34307 if ( !items || !items.length || items.length < 3) {
34313 var eItems = items.slice(0, 3);
34315 items = items.slice(3, items.length);
34318 ['xs', 'xs', 'xs', 'wide'],
34319 ['xs', 'xs', 'wide'],
34320 ['xs', 'xs', 'sm'],
34321 ['xs', 'xs', 'xs'],
34327 ['sm', 'xs', 'xs'],
34331 ['wide', 'xs', 'xs', 'xs'],
34332 ['wide', 'xs', 'xs'],
34345 Roo.each(items, function(item, k){
34347 switch (item.size) {
34358 boxes.push([item]);
34382 var filterPattern = function(box, length)
34390 var pattern = box.slice(0, length);
34394 Roo.each(pattern, function(i){
34395 format.push(i.size);
34398 Roo.each(standard, function(s){
34400 if(String(s) != String(format)){
34409 if(!match && length == 1){
34414 filterPattern(box, length - 1);
34418 queue.push(pattern);
34420 box = box.slice(length, box.length);
34422 filterPattern(box, 4);
34428 Roo.each(boxes, function(box, k){
34434 if(box.length == 1){
34439 filterPattern(box, 4);
34446 var pos = this.el.getBox(true);
34450 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34452 var hit_end = false;
34454 Roo.each(queue, function(box){
34458 Roo.each(box, function(b){
34460 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34470 Roo.each(box, function(b){
34472 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34475 mx = Math.max(mx, b.x);
34479 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34483 Roo.each(box, function(b){
34485 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34499 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34502 /** Sets position of item in DOM
34503 * @param {Element} item
34504 * @param {Number} x - horizontal position
34505 * @param {Number} y - vertical position
34506 * @param {Boolean} isInstant - disables transitions
34508 _processVerticalLayoutQueue : function( queue, isInstant )
34510 var pos = this.el.getBox(true);
34515 for (var i = 0; i < this.cols; i++){
34519 Roo.each(queue, function(box, k){
34521 var col = k % this.cols;
34523 Roo.each(box, function(b,kk){
34525 b.el.position('absolute');
34527 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34528 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34530 if(b.size == 'md-left' || b.size == 'md-right'){
34531 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34532 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34535 b.el.setWidth(width);
34536 b.el.setHeight(height);
34538 b.el.select('iframe',true).setSize(width,height);
34542 for (var i = 0; i < this.cols; i++){
34544 if(maxY[i] < maxY[col]){
34549 col = Math.min(col, i);
34553 x = pos.x + col * (this.colWidth + this.padWidth);
34557 var positions = [];
34559 switch (box.length){
34561 positions = this.getVerticalOneBoxColPositions(x, y, box);
34564 positions = this.getVerticalTwoBoxColPositions(x, y, box);
34567 positions = this.getVerticalThreeBoxColPositions(x, y, box);
34570 positions = this.getVerticalFourBoxColPositions(x, y, box);
34576 Roo.each(box, function(b,kk){
34578 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34580 var sz = b.el.getSize();
34582 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34590 for (var i = 0; i < this.cols; i++){
34591 mY = Math.max(mY, maxY[i]);
34594 this.el.setHeight(mY - pos.y);
34598 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34600 // var pos = this.el.getBox(true);
34603 // var maxX = pos.right;
34605 // var maxHeight = 0;
34607 // Roo.each(items, function(item, k){
34611 // item.el.position('absolute');
34613 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34615 // item.el.setWidth(width);
34617 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34619 // item.el.setHeight(height);
34622 // item.el.setXY([x, y], isInstant ? false : true);
34624 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34627 // y = y + height + this.alternativePadWidth;
34629 // maxHeight = maxHeight + height + this.alternativePadWidth;
34633 // this.el.setHeight(maxHeight);
34637 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34639 var pos = this.el.getBox(true);
34644 var maxX = pos.right;
34646 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34648 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34650 Roo.each(queue, function(box, k){
34652 Roo.each(box, function(b, kk){
34654 b.el.position('absolute');
34656 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34657 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34659 if(b.size == 'md-left' || b.size == 'md-right'){
34660 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34661 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34664 b.el.setWidth(width);
34665 b.el.setHeight(height);
34673 var positions = [];
34675 switch (box.length){
34677 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34680 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34683 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34686 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34692 Roo.each(box, function(b,kk){
34694 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34696 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34704 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34706 Roo.each(eItems, function(b,k){
34708 b.size = (k == 0) ? 'sm' : 'xs';
34709 b.x = (k == 0) ? 2 : 1;
34710 b.y = (k == 0) ? 2 : 1;
34712 b.el.position('absolute');
34714 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34716 b.el.setWidth(width);
34718 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34720 b.el.setHeight(height);
34724 var positions = [];
34727 x : maxX - this.unitWidth * 2 - this.gutter,
34732 x : maxX - this.unitWidth,
34733 y : minY + (this.unitWidth + this.gutter) * 2
34737 x : maxX - this.unitWidth * 3 - this.gutter * 2,
34741 Roo.each(eItems, function(b,k){
34743 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34749 getVerticalOneBoxColPositions : function(x, y, box)
34753 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34755 if(box[0].size == 'md-left'){
34759 if(box[0].size == 'md-right'){
34764 x : x + (this.unitWidth + this.gutter) * rand,
34771 getVerticalTwoBoxColPositions : function(x, y, box)
34775 if(box[0].size == 'xs'){
34779 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34783 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34797 x : x + (this.unitWidth + this.gutter) * 2,
34798 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34805 getVerticalThreeBoxColPositions : function(x, y, box)
34809 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34817 x : x + (this.unitWidth + this.gutter) * 1,
34822 x : x + (this.unitWidth + this.gutter) * 2,
34830 if(box[0].size == 'xs' && box[1].size == 'xs'){
34839 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34843 x : x + (this.unitWidth + this.gutter) * 1,
34857 x : x + (this.unitWidth + this.gutter) * 2,
34862 x : x + (this.unitWidth + this.gutter) * 2,
34863 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34870 getVerticalFourBoxColPositions : function(x, y, box)
34874 if(box[0].size == 'xs'){
34883 y : y + (this.unitHeight + this.gutter) * 1
34888 y : y + (this.unitHeight + this.gutter) * 2
34892 x : x + (this.unitWidth + this.gutter) * 1,
34906 x : x + (this.unitWidth + this.gutter) * 2,
34911 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34912 y : y + (this.unitHeight + this.gutter) * 1
34916 x : x + (this.unitWidth + this.gutter) * 2,
34917 y : y + (this.unitWidth + this.gutter) * 2
34924 getHorizontalOneBoxColPositions : function(maxX, minY, box)
34928 if(box[0].size == 'md-left'){
34930 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34937 if(box[0].size == 'md-right'){
34939 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34940 y : minY + (this.unitWidth + this.gutter) * 1
34946 var rand = Math.floor(Math.random() * (4 - box[0].y));
34949 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34950 y : minY + (this.unitWidth + this.gutter) * rand
34957 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34961 if(box[0].size == 'xs'){
34964 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34969 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34970 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34978 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34983 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34984 y : minY + (this.unitWidth + this.gutter) * 2
34991 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34995 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34998 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35003 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35004 y : minY + (this.unitWidth + this.gutter) * 1
35008 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35009 y : minY + (this.unitWidth + this.gutter) * 2
35016 if(box[0].size == 'xs' && box[1].size == 'xs'){
35019 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35024 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35029 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35030 y : minY + (this.unitWidth + this.gutter) * 1
35038 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35043 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35044 y : minY + (this.unitWidth + this.gutter) * 2
35048 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35049 y : minY + (this.unitWidth + this.gutter) * 2
35056 getHorizontalFourBoxColPositions : function(maxX, minY, box)
35060 if(box[0].size == 'xs'){
35063 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35068 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35073 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),
35078 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35079 y : minY + (this.unitWidth + this.gutter) * 1
35087 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35092 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35093 y : minY + (this.unitWidth + this.gutter) * 2
35097 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35098 y : minY + (this.unitWidth + this.gutter) * 2
35102 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),
35103 y : minY + (this.unitWidth + this.gutter) * 2
35111 * remove a Masonry Brick
35112 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35114 removeBrick : function(brick_id)
35120 for (var i = 0; i<this.bricks.length; i++) {
35121 if (this.bricks[i].id == brick_id) {
35122 this.bricks.splice(i,1);
35123 this.el.dom.removeChild(Roo.get(brick_id).dom);
35130 * adds a Masonry Brick
35131 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35133 addBrick : function(cfg)
35135 var cn = new Roo.bootstrap.MasonryBrick(cfg);
35136 //this.register(cn);
35137 cn.parentId = this.id;
35138 cn.render(this.el);
35143 * register a Masonry Brick
35144 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35147 register : function(brick)
35149 this.bricks.push(brick);
35150 brick.masonryId = this.id;
35154 * clear all the Masonry Brick
35156 clearAll : function()
35159 //this.getChildContainer().dom.innerHTML = "";
35160 this.el.dom.innerHTML = '';
35163 getSelected : function()
35165 if (!this.selectedBrick) {
35169 return this.selectedBrick;
35173 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35177 * register a Masonry Layout
35178 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35181 register : function(layout)
35183 this.groups[layout.id] = layout;
35186 * fetch a Masonry Layout based on the masonry layout ID
35187 * @param {string} the masonry layout to add
35188 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35191 get: function(layout_id) {
35192 if (typeof(this.groups[layout_id]) == 'undefined') {
35195 return this.groups[layout_id] ;
35207 * http://masonry.desandro.com
35209 * The idea is to render all the bricks based on vertical width...
35211 * The original code extends 'outlayer' - we might need to use that....
35217 * @class Roo.bootstrap.LayoutMasonryAuto
35218 * @extends Roo.bootstrap.Component
35219 * Bootstrap Layout Masonry class
35222 * Create a new Element
35223 * @param {Object} config The config object
35226 Roo.bootstrap.LayoutMasonryAuto = function(config){
35227 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35230 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
35233 * @cfg {Boolean} isFitWidth - resize the width..
35235 isFitWidth : false, // options..
35237 * @cfg {Boolean} isOriginLeft = left align?
35239 isOriginLeft : true,
35241 * @cfg {Boolean} isOriginTop = top align?
35243 isOriginTop : false,
35245 * @cfg {Boolean} isLayoutInstant = no animation?
35247 isLayoutInstant : false, // needed?
35249 * @cfg {Boolean} isResizingContainer = not sure if this is used..
35251 isResizingContainer : true,
35253 * @cfg {Number} columnWidth width of the columns
35259 * @cfg {Number} maxCols maximum number of columns
35264 * @cfg {Number} padHeight padding below box..
35270 * @cfg {Boolean} isAutoInitial defalut true
35273 isAutoInitial : true,
35279 initialColumnWidth : 0,
35280 currentSize : null,
35282 colYs : null, // array.
35289 bricks: null, //CompositeElement
35290 cols : 0, // array?
35291 // element : null, // wrapped now this.el
35292 _isLayoutInited : null,
35295 getAutoCreate : function(){
35299 cls: 'blog-masonary-wrapper ' + this.cls,
35301 cls : 'mas-boxes masonary'
35308 getChildContainer: function( )
35310 if (this.boxesEl) {
35311 return this.boxesEl;
35314 this.boxesEl = this.el.select('.mas-boxes').first();
35316 return this.boxesEl;
35320 initEvents : function()
35324 if(this.isAutoInitial){
35325 Roo.log('hook children rendered');
35326 this.on('childrenrendered', function() {
35327 Roo.log('children rendered');
35334 initial : function()
35336 this.reloadItems();
35338 this.currentSize = this.el.getBox(true);
35340 /// was window resize... - let's see if this works..
35341 Roo.EventManager.onWindowResize(this.resize, this);
35343 if(!this.isAutoInitial){
35348 this.layout.defer(500,this);
35351 reloadItems: function()
35353 this.bricks = this.el.select('.masonry-brick', true);
35355 this.bricks.each(function(b) {
35356 //Roo.log(b.getSize());
35357 if (!b.attr('originalwidth')) {
35358 b.attr('originalwidth', b.getSize().width);
35363 Roo.log(this.bricks.elements.length);
35366 resize : function()
35369 var cs = this.el.getBox(true);
35371 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35372 Roo.log("no change in with or X");
35375 this.currentSize = cs;
35379 layout : function()
35382 this._resetLayout();
35383 //this._manageStamps();
35385 // don't animate first layout
35386 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35387 this.layoutItems( isInstant );
35389 // flag for initalized
35390 this._isLayoutInited = true;
35393 layoutItems : function( isInstant )
35395 //var items = this._getItemsForLayout( this.items );
35396 // original code supports filtering layout items.. we just ignore it..
35398 this._layoutItems( this.bricks , isInstant );
35400 this._postLayout();
35402 _layoutItems : function ( items , isInstant)
35404 //this.fireEvent( 'layout', this, items );
35407 if ( !items || !items.elements.length ) {
35408 // no items, emit event with empty array
35413 items.each(function(item) {
35414 Roo.log("layout item");
35416 // get x/y object from method
35417 var position = this._getItemLayoutPosition( item );
35419 position.item = item;
35420 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35421 queue.push( position );
35424 this._processLayoutQueue( queue );
35426 /** Sets position of item in DOM
35427 * @param {Element} item
35428 * @param {Number} x - horizontal position
35429 * @param {Number} y - vertical position
35430 * @param {Boolean} isInstant - disables transitions
35432 _processLayoutQueue : function( queue )
35434 for ( var i=0, len = queue.length; i < len; i++ ) {
35435 var obj = queue[i];
35436 obj.item.position('absolute');
35437 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35443 * Any logic you want to do after each layout,
35444 * i.e. size the container
35446 _postLayout : function()
35448 this.resizeContainer();
35451 resizeContainer : function()
35453 if ( !this.isResizingContainer ) {
35456 var size = this._getContainerSize();
35458 this.el.setSize(size.width,size.height);
35459 this.boxesEl.setSize(size.width,size.height);
35465 _resetLayout : function()
35467 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35468 this.colWidth = this.el.getWidth();
35469 //this.gutter = this.el.getWidth();
35471 this.measureColumns();
35477 this.colYs.push( 0 );
35483 measureColumns : function()
35485 this.getContainerWidth();
35486 // if columnWidth is 0, default to outerWidth of first item
35487 if ( !this.columnWidth ) {
35488 var firstItem = this.bricks.first();
35489 Roo.log(firstItem);
35490 this.columnWidth = this.containerWidth;
35491 if (firstItem && firstItem.attr('originalwidth') ) {
35492 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35494 // columnWidth fall back to item of first element
35495 Roo.log("set column width?");
35496 this.initialColumnWidth = this.columnWidth ;
35498 // if first elem has no width, default to size of container
35503 if (this.initialColumnWidth) {
35504 this.columnWidth = this.initialColumnWidth;
35509 // column width is fixed at the top - however if container width get's smaller we should
35512 // this bit calcs how man columns..
35514 var columnWidth = this.columnWidth += this.gutter;
35516 // calculate columns
35517 var containerWidth = this.containerWidth + this.gutter;
35519 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35520 // fix rounding errors, typically with gutters
35521 var excess = columnWidth - containerWidth % columnWidth;
35524 // if overshoot is less than a pixel, round up, otherwise floor it
35525 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35526 cols = Math[ mathMethod ]( cols );
35527 this.cols = Math.max( cols, 1 );
35528 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35530 // padding positioning..
35531 var totalColWidth = this.cols * this.columnWidth;
35532 var padavail = this.containerWidth - totalColWidth;
35533 // so for 2 columns - we need 3 'pads'
35535 var padNeeded = (1+this.cols) * this.padWidth;
35537 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35539 this.columnWidth += padExtra
35540 //this.padWidth = Math.floor(padavail / ( this.cols));
35542 // adjust colum width so that padding is fixed??
35544 // we have 3 columns ... total = width * 3
35545 // we have X left over... that should be used by
35547 //if (this.expandC) {
35555 getContainerWidth : function()
35557 /* // container is parent if fit width
35558 var container = this.isFitWidth ? this.element.parentNode : this.element;
35559 // check that this.size and size are there
35560 // IE8 triggers resize on body size change, so they might not be
35562 var size = getSize( container ); //FIXME
35563 this.containerWidth = size && size.innerWidth; //FIXME
35566 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
35570 _getItemLayoutPosition : function( item ) // what is item?
35572 // we resize the item to our columnWidth..
35574 item.setWidth(this.columnWidth);
35575 item.autoBoxAdjust = false;
35577 var sz = item.getSize();
35579 // how many columns does this brick span
35580 var remainder = this.containerWidth % this.columnWidth;
35582 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35583 // round if off by 1 pixel, otherwise use ceil
35584 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
35585 colSpan = Math.min( colSpan, this.cols );
35587 // normally this should be '1' as we dont' currently allow multi width columns..
35589 var colGroup = this._getColGroup( colSpan );
35590 // get the minimum Y value from the columns
35591 var minimumY = Math.min.apply( Math, colGroup );
35592 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35594 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35596 // position the brick
35598 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35599 y: this.currentSize.y + minimumY + this.padHeight
35603 // apply setHeight to necessary columns
35604 var setHeight = minimumY + sz.height + this.padHeight;
35605 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35607 var setSpan = this.cols + 1 - colGroup.length;
35608 for ( var i = 0; i < setSpan; i++ ) {
35609 this.colYs[ shortColIndex + i ] = setHeight ;
35616 * @param {Number} colSpan - number of columns the element spans
35617 * @returns {Array} colGroup
35619 _getColGroup : function( colSpan )
35621 if ( colSpan < 2 ) {
35622 // if brick spans only one column, use all the column Ys
35627 // how many different places could this brick fit horizontally
35628 var groupCount = this.cols + 1 - colSpan;
35629 // for each group potential horizontal position
35630 for ( var i = 0; i < groupCount; i++ ) {
35631 // make an array of colY values for that one group
35632 var groupColYs = this.colYs.slice( i, i + colSpan );
35633 // and get the max value of the array
35634 colGroup[i] = Math.max.apply( Math, groupColYs );
35639 _manageStamp : function( stamp )
35641 var stampSize = stamp.getSize();
35642 var offset = stamp.getBox();
35643 // get the columns that this stamp affects
35644 var firstX = this.isOriginLeft ? offset.x : offset.right;
35645 var lastX = firstX + stampSize.width;
35646 var firstCol = Math.floor( firstX / this.columnWidth );
35647 firstCol = Math.max( 0, firstCol );
35649 var lastCol = Math.floor( lastX / this.columnWidth );
35650 // lastCol should not go over if multiple of columnWidth #425
35651 lastCol -= lastX % this.columnWidth ? 0 : 1;
35652 lastCol = Math.min( this.cols - 1, lastCol );
35654 // set colYs to bottom of the stamp
35655 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35658 for ( var i = firstCol; i <= lastCol; i++ ) {
35659 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35664 _getContainerSize : function()
35666 this.maxY = Math.max.apply( Math, this.colYs );
35671 if ( this.isFitWidth ) {
35672 size.width = this._getContainerFitWidth();
35678 _getContainerFitWidth : function()
35680 var unusedCols = 0;
35681 // count unused columns
35684 if ( this.colYs[i] !== 0 ) {
35689 // fit container to columns that have been used
35690 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35693 needsResizeLayout : function()
35695 var previousWidth = this.containerWidth;
35696 this.getContainerWidth();
35697 return previousWidth !== this.containerWidth;
35712 * @class Roo.bootstrap.MasonryBrick
35713 * @extends Roo.bootstrap.Component
35714 * Bootstrap MasonryBrick class
35717 * Create a new MasonryBrick
35718 * @param {Object} config The config object
35721 Roo.bootstrap.MasonryBrick = function(config){
35723 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35725 Roo.bootstrap.MasonryBrick.register(this);
35731 * When a MasonryBrick is clcik
35732 * @param {Roo.bootstrap.MasonryBrick} this
35733 * @param {Roo.EventObject} e
35739 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
35742 * @cfg {String} title
35746 * @cfg {String} html
35750 * @cfg {String} bgimage
35754 * @cfg {String} videourl
35758 * @cfg {String} cls
35762 * @cfg {String} href
35766 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35771 * @cfg {String} placetitle (center|bottom)
35776 * @cfg {Boolean} isFitContainer defalut true
35778 isFitContainer : true,
35781 * @cfg {Boolean} preventDefault defalut false
35783 preventDefault : false,
35786 * @cfg {Boolean} inverse defalut false
35788 maskInverse : false,
35790 getAutoCreate : function()
35792 if(!this.isFitContainer){
35793 return this.getSplitAutoCreate();
35796 var cls = 'masonry-brick masonry-brick-full';
35798 if(this.href.length){
35799 cls += ' masonry-brick-link';
35802 if(this.bgimage.length){
35803 cls += ' masonry-brick-image';
35806 if(this.maskInverse){
35807 cls += ' mask-inverse';
35810 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35811 cls += ' enable-mask';
35815 cls += ' masonry-' + this.size + '-brick';
35818 if(this.placetitle.length){
35820 switch (this.placetitle) {
35822 cls += ' masonry-center-title';
35825 cls += ' masonry-bottom-title';
35832 if(!this.html.length && !this.bgimage.length){
35833 cls += ' masonry-center-title';
35836 if(!this.html.length && this.bgimage.length){
35837 cls += ' masonry-bottom-title';
35842 cls += ' ' + this.cls;
35846 tag: (this.href.length) ? 'a' : 'div',
35851 cls: 'masonry-brick-mask'
35855 cls: 'masonry-brick-paragraph',
35861 if(this.href.length){
35862 cfg.href = this.href;
35865 var cn = cfg.cn[1].cn;
35867 if(this.title.length){
35870 cls: 'masonry-brick-title',
35875 if(this.html.length){
35878 cls: 'masonry-brick-text',
35883 if (!this.title.length && !this.html.length) {
35884 cfg.cn[1].cls += ' hide';
35887 if(this.bgimage.length){
35890 cls: 'masonry-brick-image-view',
35895 if(this.videourl.length){
35896 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35897 // youtube support only?
35900 cls: 'masonry-brick-image-view',
35903 allowfullscreen : true
35911 getSplitAutoCreate : function()
35913 var cls = 'masonry-brick masonry-brick-split';
35915 if(this.href.length){
35916 cls += ' masonry-brick-link';
35919 if(this.bgimage.length){
35920 cls += ' masonry-brick-image';
35924 cls += ' masonry-' + this.size + '-brick';
35927 switch (this.placetitle) {
35929 cls += ' masonry-center-title';
35932 cls += ' masonry-bottom-title';
35935 if(!this.bgimage.length){
35936 cls += ' masonry-center-title';
35939 if(this.bgimage.length){
35940 cls += ' masonry-bottom-title';
35946 cls += ' ' + this.cls;
35950 tag: (this.href.length) ? 'a' : 'div',
35955 cls: 'masonry-brick-split-head',
35959 cls: 'masonry-brick-paragraph',
35966 cls: 'masonry-brick-split-body',
35972 if(this.href.length){
35973 cfg.href = this.href;
35976 if(this.title.length){
35977 cfg.cn[0].cn[0].cn.push({
35979 cls: 'masonry-brick-title',
35984 if(this.html.length){
35985 cfg.cn[1].cn.push({
35987 cls: 'masonry-brick-text',
35992 if(this.bgimage.length){
35993 cfg.cn[0].cn.push({
35995 cls: 'masonry-brick-image-view',
36000 if(this.videourl.length){
36001 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36002 // youtube support only?
36003 cfg.cn[0].cn.cn.push({
36005 cls: 'masonry-brick-image-view',
36008 allowfullscreen : true
36015 initEvents: function()
36017 switch (this.size) {
36050 this.el.on('touchstart', this.onTouchStart, this);
36051 this.el.on('touchmove', this.onTouchMove, this);
36052 this.el.on('touchend', this.onTouchEnd, this);
36053 this.el.on('contextmenu', this.onContextMenu, this);
36055 this.el.on('mouseenter' ,this.enter, this);
36056 this.el.on('mouseleave', this.leave, this);
36057 this.el.on('click', this.onClick, this);
36060 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36061 this.parent().bricks.push(this);
36066 onClick: function(e, el)
36068 var time = this.endTimer - this.startTimer;
36069 // Roo.log(e.preventDefault());
36072 e.preventDefault();
36077 if(!this.preventDefault){
36081 e.preventDefault();
36083 if (this.activeClass != '') {
36084 this.selectBrick();
36087 this.fireEvent('click', this, e);
36090 enter: function(e, el)
36092 e.preventDefault();
36094 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36098 if(this.bgimage.length && this.html.length){
36099 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36103 leave: function(e, el)
36105 e.preventDefault();
36107 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36111 if(this.bgimage.length && this.html.length){
36112 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36116 onTouchStart: function(e, el)
36118 // e.preventDefault();
36120 this.touchmoved = false;
36122 if(!this.isFitContainer){
36126 if(!this.bgimage.length || !this.html.length){
36130 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36132 this.timer = new Date().getTime();
36136 onTouchMove: function(e, el)
36138 this.touchmoved = true;
36141 onContextMenu : function(e,el)
36143 e.preventDefault();
36144 e.stopPropagation();
36148 onTouchEnd: function(e, el)
36150 // e.preventDefault();
36152 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36159 if(!this.bgimage.length || !this.html.length){
36161 if(this.href.length){
36162 window.location.href = this.href;
36168 if(!this.isFitContainer){
36172 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36174 window.location.href = this.href;
36177 //selection on single brick only
36178 selectBrick : function() {
36180 if (!this.parentId) {
36184 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36185 var index = m.selectedBrick.indexOf(this.id);
36188 m.selectedBrick.splice(index,1);
36189 this.el.removeClass(this.activeClass);
36193 for(var i = 0; i < m.selectedBrick.length; i++) {
36194 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36195 b.el.removeClass(b.activeClass);
36198 m.selectedBrick = [];
36200 m.selectedBrick.push(this.id);
36201 this.el.addClass(this.activeClass);
36205 isSelected : function(){
36206 return this.el.hasClass(this.activeClass);
36211 Roo.apply(Roo.bootstrap.MasonryBrick, {
36214 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36216 * register a Masonry Brick
36217 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36220 register : function(brick)
36222 //this.groups[brick.id] = brick;
36223 this.groups.add(brick.id, brick);
36226 * fetch a masonry brick based on the masonry brick ID
36227 * @param {string} the masonry brick to add
36228 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36231 get: function(brick_id)
36233 // if (typeof(this.groups[brick_id]) == 'undefined') {
36236 // return this.groups[brick_id] ;
36238 if(this.groups.key(brick_id)) {
36239 return this.groups.key(brick_id);
36257 * @class Roo.bootstrap.Brick
36258 * @extends Roo.bootstrap.Component
36259 * Bootstrap Brick class
36262 * Create a new Brick
36263 * @param {Object} config The config object
36266 Roo.bootstrap.Brick = function(config){
36267 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36273 * When a Brick is click
36274 * @param {Roo.bootstrap.Brick} this
36275 * @param {Roo.EventObject} e
36281 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
36284 * @cfg {String} title
36288 * @cfg {String} html
36292 * @cfg {String} bgimage
36296 * @cfg {String} cls
36300 * @cfg {String} href
36304 * @cfg {String} video
36308 * @cfg {Boolean} square
36312 getAutoCreate : function()
36314 var cls = 'roo-brick';
36316 if(this.href.length){
36317 cls += ' roo-brick-link';
36320 if(this.bgimage.length){
36321 cls += ' roo-brick-image';
36324 if(!this.html.length && !this.bgimage.length){
36325 cls += ' roo-brick-center-title';
36328 if(!this.html.length && this.bgimage.length){
36329 cls += ' roo-brick-bottom-title';
36333 cls += ' ' + this.cls;
36337 tag: (this.href.length) ? 'a' : 'div',
36342 cls: 'roo-brick-paragraph',
36348 if(this.href.length){
36349 cfg.href = this.href;
36352 var cn = cfg.cn[0].cn;
36354 if(this.title.length){
36357 cls: 'roo-brick-title',
36362 if(this.html.length){
36365 cls: 'roo-brick-text',
36372 if(this.bgimage.length){
36375 cls: 'roo-brick-image-view',
36383 initEvents: function()
36385 if(this.title.length || this.html.length){
36386 this.el.on('mouseenter' ,this.enter, this);
36387 this.el.on('mouseleave', this.leave, this);
36390 Roo.EventManager.onWindowResize(this.resize, this);
36392 if(this.bgimage.length){
36393 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36394 this.imageEl.on('load', this.onImageLoad, this);
36401 onImageLoad : function()
36406 resize : function()
36408 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36410 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36412 if(this.bgimage.length){
36413 var image = this.el.select('.roo-brick-image-view', true).first();
36415 image.setWidth(paragraph.getWidth());
36418 image.setHeight(paragraph.getWidth());
36421 this.el.setHeight(image.getHeight());
36422 paragraph.setHeight(image.getHeight());
36428 enter: function(e, el)
36430 e.preventDefault();
36432 if(this.bgimage.length){
36433 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36434 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36438 leave: function(e, el)
36440 e.preventDefault();
36442 if(this.bgimage.length){
36443 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36444 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36459 * @class Roo.bootstrap.NumberField
36460 * @extends Roo.bootstrap.Input
36461 * Bootstrap NumberField class
36467 * Create a new NumberField
36468 * @param {Object} config The config object
36471 Roo.bootstrap.NumberField = function(config){
36472 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36475 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36478 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36480 allowDecimals : true,
36482 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36484 decimalSeparator : ".",
36486 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36488 decimalPrecision : 2,
36490 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36492 allowNegative : true,
36495 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36499 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36501 minValue : Number.NEGATIVE_INFINITY,
36503 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36505 maxValue : Number.MAX_VALUE,
36507 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36509 minText : "The minimum value for this field is {0}",
36511 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36513 maxText : "The maximum value for this field is {0}",
36515 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36516 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36518 nanText : "{0} is not a valid number",
36520 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36522 thousandsDelimiter : false,
36524 * @cfg {String} valueAlign alignment of value
36526 valueAlign : "left",
36528 getAutoCreate : function()
36530 var hiddenInput = {
36534 cls: 'hidden-number-input'
36538 hiddenInput.name = this.name;
36543 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36545 this.name = hiddenInput.name;
36547 if(cfg.cn.length > 0) {
36548 cfg.cn.push(hiddenInput);
36555 initEvents : function()
36557 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36559 var allowed = "0123456789";
36561 if(this.allowDecimals){
36562 allowed += this.decimalSeparator;
36565 if(this.allowNegative){
36569 if(this.thousandsDelimiter) {
36573 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36575 var keyPress = function(e){
36577 var k = e.getKey();
36579 var c = e.getCharCode();
36582 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36583 allowed.indexOf(String.fromCharCode(c)) === -1
36589 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36593 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36598 this.el.on("keypress", keyPress, this);
36601 validateValue : function(value)
36604 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36608 var num = this.parseValue(value);
36611 this.markInvalid(String.format(this.nanText, value));
36615 if(num < this.minValue){
36616 this.markInvalid(String.format(this.minText, this.minValue));
36620 if(num > this.maxValue){
36621 this.markInvalid(String.format(this.maxText, this.maxValue));
36628 getValue : function()
36630 var v = this.hiddenEl().getValue();
36632 return this.fixPrecision(this.parseValue(v));
36635 parseValue : function(value)
36637 if(this.thousandsDelimiter) {
36639 r = new RegExp(",", "g");
36640 value = value.replace(r, "");
36643 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36644 return isNaN(value) ? '' : value;
36647 fixPrecision : function(value)
36649 if(this.thousandsDelimiter) {
36651 r = new RegExp(",", "g");
36652 value = value.replace(r, "");
36655 var nan = isNaN(value);
36657 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36658 return nan ? '' : value;
36660 return parseFloat(value).toFixed(this.decimalPrecision);
36663 setValue : function(v)
36665 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36671 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36673 this.inputEl().dom.value = (v == '') ? '' :
36674 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36676 if(!this.allowZero && v === '0') {
36677 this.hiddenEl().dom.value = '';
36678 this.inputEl().dom.value = '';
36685 decimalPrecisionFcn : function(v)
36687 return Math.floor(v);
36690 beforeBlur : function()
36692 var v = this.parseValue(this.getRawValue());
36694 if(v || v === 0 || v === ''){
36699 hiddenEl : function()
36701 return this.el.select('input.hidden-number-input',true).first();
36713 * @class Roo.bootstrap.DocumentSlider
36714 * @extends Roo.bootstrap.Component
36715 * Bootstrap DocumentSlider class
36718 * Create a new DocumentViewer
36719 * @param {Object} config The config object
36722 Roo.bootstrap.DocumentSlider = function(config){
36723 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36730 * Fire after initEvent
36731 * @param {Roo.bootstrap.DocumentSlider} this
36736 * Fire after update
36737 * @param {Roo.bootstrap.DocumentSlider} this
36743 * @param {Roo.bootstrap.DocumentSlider} this
36749 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
36755 getAutoCreate : function()
36759 cls : 'roo-document-slider',
36763 cls : 'roo-document-slider-header',
36767 cls : 'roo-document-slider-header-title'
36773 cls : 'roo-document-slider-body',
36777 cls : 'roo-document-slider-prev',
36781 cls : 'fa fa-chevron-left'
36787 cls : 'roo-document-slider-thumb',
36791 cls : 'roo-document-slider-image'
36797 cls : 'roo-document-slider-next',
36801 cls : 'fa fa-chevron-right'
36813 initEvents : function()
36815 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36816 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36818 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36819 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36821 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36822 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36824 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36825 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36827 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36828 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36830 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36831 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36833 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36834 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36836 this.thumbEl.on('click', this.onClick, this);
36838 this.prevIndicator.on('click', this.prev, this);
36840 this.nextIndicator.on('click', this.next, this);
36844 initial : function()
36846 if(this.files.length){
36847 this.indicator = 1;
36851 this.fireEvent('initial', this);
36854 update : function()
36856 this.imageEl.attr('src', this.files[this.indicator - 1]);
36858 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36860 this.prevIndicator.show();
36862 if(this.indicator == 1){
36863 this.prevIndicator.hide();
36866 this.nextIndicator.show();
36868 if(this.indicator == this.files.length){
36869 this.nextIndicator.hide();
36872 this.thumbEl.scrollTo('top');
36874 this.fireEvent('update', this);
36877 onClick : function(e)
36879 e.preventDefault();
36881 this.fireEvent('click', this);
36886 e.preventDefault();
36888 this.indicator = Math.max(1, this.indicator - 1);
36895 e.preventDefault();
36897 this.indicator = Math.min(this.files.length, this.indicator + 1);
36911 * @class Roo.bootstrap.RadioSet
36912 * @extends Roo.bootstrap.Input
36913 * Bootstrap RadioSet class
36914 * @cfg {String} indicatorpos (left|right) default left
36915 * @cfg {Boolean} inline (true|false) inline the element (default true)
36916 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36918 * Create a new RadioSet
36919 * @param {Object} config The config object
36922 Roo.bootstrap.RadioSet = function(config){
36924 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36928 Roo.bootstrap.RadioSet.register(this);
36933 * Fires when the element is checked or unchecked.
36934 * @param {Roo.bootstrap.RadioSet} this This radio
36935 * @param {Roo.bootstrap.Radio} item The checked item
36940 * Fires when the element is click.
36941 * @param {Roo.bootstrap.RadioSet} this This radio set
36942 * @param {Roo.bootstrap.Radio} item The checked item
36943 * @param {Roo.EventObject} e The event object
36950 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
36958 indicatorpos : 'left',
36960 getAutoCreate : function()
36964 cls : 'roo-radio-set-label',
36968 html : this.fieldLabel
36972 if (Roo.bootstrap.version == 3) {
36975 if(this.indicatorpos == 'left'){
36978 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36979 tooltip : 'This field is required'
36984 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36985 tooltip : 'This field is required'
36991 cls : 'roo-radio-set-items'
36994 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36996 if (align === 'left' && this.fieldLabel.length) {
36999 cls : "roo-radio-set-right",
37005 if(this.labelWidth > 12){
37006 label.style = "width: " + this.labelWidth + 'px';
37009 if(this.labelWidth < 13 && this.labelmd == 0){
37010 this.labelmd = this.labelWidth;
37013 if(this.labellg > 0){
37014 label.cls += ' col-lg-' + this.labellg;
37015 items.cls += ' col-lg-' + (12 - this.labellg);
37018 if(this.labelmd > 0){
37019 label.cls += ' col-md-' + this.labelmd;
37020 items.cls += ' col-md-' + (12 - this.labelmd);
37023 if(this.labelsm > 0){
37024 label.cls += ' col-sm-' + this.labelsm;
37025 items.cls += ' col-sm-' + (12 - this.labelsm);
37028 if(this.labelxs > 0){
37029 label.cls += ' col-xs-' + this.labelxs;
37030 items.cls += ' col-xs-' + (12 - this.labelxs);
37036 cls : 'roo-radio-set',
37040 cls : 'roo-radio-set-input',
37043 value : this.value ? this.value : ''
37050 if(this.weight.length){
37051 cfg.cls += ' roo-radio-' + this.weight;
37055 cfg.cls += ' roo-radio-set-inline';
37059 ['xs','sm','md','lg'].map(function(size){
37060 if (settings[size]) {
37061 cfg.cls += ' col-' + size + '-' + settings[size];
37069 initEvents : function()
37071 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37072 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37074 if(!this.fieldLabel.length){
37075 this.labelEl.hide();
37078 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37079 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37081 this.indicator = this.indicatorEl();
37083 if(this.indicator){
37084 this.indicator.addClass('invisible');
37087 this.originalValue = this.getValue();
37091 inputEl: function ()
37093 return this.el.select('.roo-radio-set-input', true).first();
37096 getChildContainer : function()
37098 return this.itemsEl;
37101 register : function(item)
37103 this.radioes.push(item);
37107 validate : function()
37109 if(this.getVisibilityEl().hasClass('hidden')){
37115 Roo.each(this.radioes, function(i){
37124 if(this.allowBlank) {
37128 if(this.disabled || valid){
37133 this.markInvalid();
37138 markValid : function()
37140 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37141 this.indicatorEl().removeClass('visible');
37142 this.indicatorEl().addClass('invisible');
37146 if (Roo.bootstrap.version == 3) {
37147 this.el.removeClass([this.invalidClass, this.validClass]);
37148 this.el.addClass(this.validClass);
37150 this.el.removeClass(['is-invalid','is-valid']);
37151 this.el.addClass(['is-valid']);
37153 this.fireEvent('valid', this);
37156 markInvalid : function(msg)
37158 if(this.allowBlank || this.disabled){
37162 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37163 this.indicatorEl().removeClass('invisible');
37164 this.indicatorEl().addClass('visible');
37166 if (Roo.bootstrap.version == 3) {
37167 this.el.removeClass([this.invalidClass, this.validClass]);
37168 this.el.addClass(this.invalidClass);
37170 this.el.removeClass(['is-invalid','is-valid']);
37171 this.el.addClass(['is-invalid']);
37174 this.fireEvent('invalid', this, msg);
37178 setValue : function(v, suppressEvent)
37180 if(this.value === v){
37187 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37190 Roo.each(this.radioes, function(i){
37192 i.el.removeClass('checked');
37195 Roo.each(this.radioes, function(i){
37197 if(i.value === v || i.value.toString() === v.toString()){
37199 i.el.addClass('checked');
37201 if(suppressEvent !== true){
37202 this.fireEvent('check', this, i);
37213 clearInvalid : function(){
37215 if(!this.el || this.preventMark){
37219 this.el.removeClass([this.invalidClass]);
37221 this.fireEvent('valid', this);
37226 Roo.apply(Roo.bootstrap.RadioSet, {
37230 register : function(set)
37232 this.groups[set.name] = set;
37235 get: function(name)
37237 if (typeof(this.groups[name]) == 'undefined') {
37241 return this.groups[name] ;
37247 * Ext JS Library 1.1.1
37248 * Copyright(c) 2006-2007, Ext JS, LLC.
37250 * Originally Released Under LGPL - original licence link has changed is not relivant.
37253 * <script type="text/javascript">
37258 * @class Roo.bootstrap.SplitBar
37259 * @extends Roo.util.Observable
37260 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37264 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37265 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37266 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37267 split.minSize = 100;
37268 split.maxSize = 600;
37269 split.animate = true;
37270 split.on('moved', splitterMoved);
37273 * Create a new SplitBar
37274 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
37275 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
37276 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37277 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
37278 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37279 position of the SplitBar).
37281 Roo.bootstrap.SplitBar = function(cfg){
37286 // dragElement : elm
37287 // resizingElement: el,
37289 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37290 // placement : Roo.bootstrap.SplitBar.LEFT ,
37291 // existingProxy ???
37294 this.el = Roo.get(cfg.dragElement, true);
37295 this.el.dom.unselectable = "on";
37297 this.resizingEl = Roo.get(cfg.resizingElement, true);
37301 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37302 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37305 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37308 * The minimum size of the resizing element. (Defaults to 0)
37314 * The maximum size of the resizing element. (Defaults to 2000)
37317 this.maxSize = 2000;
37320 * Whether to animate the transition to the new size
37323 this.animate = false;
37326 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37329 this.useShim = false;
37334 if(!cfg.existingProxy){
37336 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37338 this.proxy = Roo.get(cfg.existingProxy).dom;
37341 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37344 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37347 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37350 this.dragSpecs = {};
37353 * @private The adapter to use to positon and resize elements
37355 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37356 this.adapter.init(this);
37358 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37360 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37361 this.el.addClass("roo-splitbar-h");
37364 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37365 this.el.addClass("roo-splitbar-v");
37371 * Fires when the splitter is moved (alias for {@link #event-moved})
37372 * @param {Roo.bootstrap.SplitBar} this
37373 * @param {Number} newSize the new width or height
37378 * Fires when the splitter is moved
37379 * @param {Roo.bootstrap.SplitBar} this
37380 * @param {Number} newSize the new width or height
37384 * @event beforeresize
37385 * Fires before the splitter is dragged
37386 * @param {Roo.bootstrap.SplitBar} this
37388 "beforeresize" : true,
37390 "beforeapply" : true
37393 Roo.util.Observable.call(this);
37396 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37397 onStartProxyDrag : function(x, y){
37398 this.fireEvent("beforeresize", this);
37400 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37402 o.enableDisplayMode("block");
37403 // all splitbars share the same overlay
37404 Roo.bootstrap.SplitBar.prototype.overlay = o;
37406 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37407 this.overlay.show();
37408 Roo.get(this.proxy).setDisplayed("block");
37409 var size = this.adapter.getElementSize(this);
37410 this.activeMinSize = this.getMinimumSize();;
37411 this.activeMaxSize = this.getMaximumSize();;
37412 var c1 = size - this.activeMinSize;
37413 var c2 = Math.max(this.activeMaxSize - size, 0);
37414 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37415 this.dd.resetConstraints();
37416 this.dd.setXConstraint(
37417 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37418 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37420 this.dd.setYConstraint(0, 0);
37422 this.dd.resetConstraints();
37423 this.dd.setXConstraint(0, 0);
37424 this.dd.setYConstraint(
37425 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37426 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37429 this.dragSpecs.startSize = size;
37430 this.dragSpecs.startPoint = [x, y];
37431 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37435 * @private Called after the drag operation by the DDProxy
37437 onEndProxyDrag : function(e){
37438 Roo.get(this.proxy).setDisplayed(false);
37439 var endPoint = Roo.lib.Event.getXY(e);
37441 this.overlay.hide();
37444 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37445 newSize = this.dragSpecs.startSize +
37446 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37447 endPoint[0] - this.dragSpecs.startPoint[0] :
37448 this.dragSpecs.startPoint[0] - endPoint[0]
37451 newSize = this.dragSpecs.startSize +
37452 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37453 endPoint[1] - this.dragSpecs.startPoint[1] :
37454 this.dragSpecs.startPoint[1] - endPoint[1]
37457 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37458 if(newSize != this.dragSpecs.startSize){
37459 if(this.fireEvent('beforeapply', this, newSize) !== false){
37460 this.adapter.setElementSize(this, newSize);
37461 this.fireEvent("moved", this, newSize);
37462 this.fireEvent("resize", this, newSize);
37468 * Get the adapter this SplitBar uses
37469 * @return The adapter object
37471 getAdapter : function(){
37472 return this.adapter;
37476 * Set the adapter this SplitBar uses
37477 * @param {Object} adapter A SplitBar adapter object
37479 setAdapter : function(adapter){
37480 this.adapter = adapter;
37481 this.adapter.init(this);
37485 * Gets the minimum size for the resizing element
37486 * @return {Number} The minimum size
37488 getMinimumSize : function(){
37489 return this.minSize;
37493 * Sets the minimum size for the resizing element
37494 * @param {Number} minSize The minimum size
37496 setMinimumSize : function(minSize){
37497 this.minSize = minSize;
37501 * Gets the maximum size for the resizing element
37502 * @return {Number} The maximum size
37504 getMaximumSize : function(){
37505 return this.maxSize;
37509 * Sets the maximum size for the resizing element
37510 * @param {Number} maxSize The maximum size
37512 setMaximumSize : function(maxSize){
37513 this.maxSize = maxSize;
37517 * Sets the initialize size for the resizing element
37518 * @param {Number} size The initial size
37520 setCurrentSize : function(size){
37521 var oldAnimate = this.animate;
37522 this.animate = false;
37523 this.adapter.setElementSize(this, size);
37524 this.animate = oldAnimate;
37528 * Destroy this splitbar.
37529 * @param {Boolean} removeEl True to remove the element
37531 destroy : function(removeEl){
37533 this.shim.remove();
37536 this.proxy.parentNode.removeChild(this.proxy);
37544 * @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.
37546 Roo.bootstrap.SplitBar.createProxy = function(dir){
37547 var proxy = new Roo.Element(document.createElement("div"));
37548 proxy.unselectable();
37549 var cls = 'roo-splitbar-proxy';
37550 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37551 document.body.appendChild(proxy.dom);
37556 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37557 * Default Adapter. It assumes the splitter and resizing element are not positioned
37558 * elements and only gets/sets the width of the element. Generally used for table based layouts.
37560 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37563 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37564 // do nothing for now
37565 init : function(s){
37569 * Called before drag operations to get the current size of the resizing element.
37570 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37572 getElementSize : function(s){
37573 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37574 return s.resizingEl.getWidth();
37576 return s.resizingEl.getHeight();
37581 * Called after drag operations to set the size of the resizing element.
37582 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37583 * @param {Number} newSize The new size to set
37584 * @param {Function} onComplete A function to be invoked when resizing is complete
37586 setElementSize : function(s, newSize, onComplete){
37587 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37589 s.resizingEl.setWidth(newSize);
37591 onComplete(s, newSize);
37594 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37599 s.resizingEl.setHeight(newSize);
37601 onComplete(s, newSize);
37604 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37611 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37612 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37613 * Adapter that moves the splitter element to align with the resized sizing element.
37614 * Used with an absolute positioned SplitBar.
37615 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37616 * document.body, make sure you assign an id to the body element.
37618 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37619 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37620 this.container = Roo.get(container);
37623 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37624 init : function(s){
37625 this.basic.init(s);
37628 getElementSize : function(s){
37629 return this.basic.getElementSize(s);
37632 setElementSize : function(s, newSize, onComplete){
37633 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37636 moveSplitter : function(s){
37637 var yes = Roo.bootstrap.SplitBar;
37638 switch(s.placement){
37640 s.el.setX(s.resizingEl.getRight());
37643 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37646 s.el.setY(s.resizingEl.getBottom());
37649 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37656 * Orientation constant - Create a vertical SplitBar
37660 Roo.bootstrap.SplitBar.VERTICAL = 1;
37663 * Orientation constant - Create a horizontal SplitBar
37667 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37670 * Placement constant - The resizing element is to the left of the splitter element
37674 Roo.bootstrap.SplitBar.LEFT = 1;
37677 * Placement constant - The resizing element is to the right of the splitter element
37681 Roo.bootstrap.SplitBar.RIGHT = 2;
37684 * Placement constant - The resizing element is positioned above the splitter element
37688 Roo.bootstrap.SplitBar.TOP = 3;
37691 * Placement constant - The resizing element is positioned under splitter element
37695 Roo.bootstrap.SplitBar.BOTTOM = 4;
37696 Roo.namespace("Roo.bootstrap.layout");/*
37698 * Ext JS Library 1.1.1
37699 * Copyright(c) 2006-2007, Ext JS, LLC.
37701 * Originally Released Under LGPL - original licence link has changed is not relivant.
37704 * <script type="text/javascript">
37708 * @class Roo.bootstrap.layout.Manager
37709 * @extends Roo.bootstrap.Component
37710 * Base class for layout managers.
37712 Roo.bootstrap.layout.Manager = function(config)
37714 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37720 /** false to disable window resize monitoring @type Boolean */
37721 this.monitorWindowResize = true;
37726 * Fires when a layout is performed.
37727 * @param {Roo.LayoutManager} this
37731 * @event regionresized
37732 * Fires when the user resizes a region.
37733 * @param {Roo.LayoutRegion} region The resized region
37734 * @param {Number} newSize The new size (width for east/west, height for north/south)
37736 "regionresized" : true,
37738 * @event regioncollapsed
37739 * Fires when a region is collapsed.
37740 * @param {Roo.LayoutRegion} region The collapsed region
37742 "regioncollapsed" : true,
37744 * @event regionexpanded
37745 * Fires when a region is expanded.
37746 * @param {Roo.LayoutRegion} region The expanded region
37748 "regionexpanded" : true
37750 this.updating = false;
37753 this.el = Roo.get(config.el);
37759 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37764 monitorWindowResize : true,
37770 onRender : function(ct, position)
37773 this.el = Roo.get(ct);
37776 //this.fireEvent('render',this);
37780 initEvents: function()
37784 // ie scrollbar fix
37785 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37786 document.body.scroll = "no";
37787 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37788 this.el.position('relative');
37790 this.id = this.el.id;
37791 this.el.addClass("roo-layout-container");
37792 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37793 if(this.el.dom != document.body ) {
37794 this.el.on('resize', this.layout,this);
37795 this.el.on('show', this.layout,this);
37801 * Returns true if this layout is currently being updated
37802 * @return {Boolean}
37804 isUpdating : function(){
37805 return this.updating;
37809 * Suspend the LayoutManager from doing auto-layouts while
37810 * making multiple add or remove calls
37812 beginUpdate : function(){
37813 this.updating = true;
37817 * Restore auto-layouts and optionally disable the manager from performing a layout
37818 * @param {Boolean} noLayout true to disable a layout update
37820 endUpdate : function(noLayout){
37821 this.updating = false;
37827 layout: function(){
37831 onRegionResized : function(region, newSize){
37832 this.fireEvent("regionresized", region, newSize);
37836 onRegionCollapsed : function(region){
37837 this.fireEvent("regioncollapsed", region);
37840 onRegionExpanded : function(region){
37841 this.fireEvent("regionexpanded", region);
37845 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37846 * performs box-model adjustments.
37847 * @return {Object} The size as an object {width: (the width), height: (the height)}
37849 getViewSize : function()
37852 if(this.el.dom != document.body){
37853 size = this.el.getSize();
37855 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37857 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37858 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37863 * Returns the Element this layout is bound to.
37864 * @return {Roo.Element}
37866 getEl : function(){
37871 * Returns the specified region.
37872 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37873 * @return {Roo.LayoutRegion}
37875 getRegion : function(target){
37876 return this.regions[target.toLowerCase()];
37879 onWindowResize : function(){
37880 if(this.monitorWindowResize){
37887 * Ext JS Library 1.1.1
37888 * Copyright(c) 2006-2007, Ext JS, LLC.
37890 * Originally Released Under LGPL - original licence link has changed is not relivant.
37893 * <script type="text/javascript">
37896 * @class Roo.bootstrap.layout.Border
37897 * @extends Roo.bootstrap.layout.Manager
37898 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37899 * please see: examples/bootstrap/nested.html<br><br>
37901 <b>The container the layout is rendered into can be either the body element or any other element.
37902 If it is not the body element, the container needs to either be an absolute positioned element,
37903 or you will need to add "position:relative" to the css of the container. You will also need to specify
37904 the container size if it is not the body element.</b>
37907 * Create a new Border
37908 * @param {Object} config Configuration options
37910 Roo.bootstrap.layout.Border = function(config){
37911 config = config || {};
37912 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37916 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37917 if(config[region]){
37918 config[region].region = region;
37919 this.addRegion(config[region]);
37925 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
37927 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37929 parent : false, // this might point to a 'nest' or a ???
37932 * Creates and adds a new region if it doesn't already exist.
37933 * @param {String} target The target region key (north, south, east, west or center).
37934 * @param {Object} config The regions config object
37935 * @return {BorderLayoutRegion} The new region
37937 addRegion : function(config)
37939 if(!this.regions[config.region]){
37940 var r = this.factory(config);
37941 this.bindRegion(r);
37943 return this.regions[config.region];
37947 bindRegion : function(r){
37948 this.regions[r.config.region] = r;
37950 r.on("visibilitychange", this.layout, this);
37951 r.on("paneladded", this.layout, this);
37952 r.on("panelremoved", this.layout, this);
37953 r.on("invalidated", this.layout, this);
37954 r.on("resized", this.onRegionResized, this);
37955 r.on("collapsed", this.onRegionCollapsed, this);
37956 r.on("expanded", this.onRegionExpanded, this);
37960 * Performs a layout update.
37962 layout : function()
37964 if(this.updating) {
37968 // render all the rebions if they have not been done alreayd?
37969 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37970 if(this.regions[region] && !this.regions[region].bodyEl){
37971 this.regions[region].onRender(this.el)
37975 var size = this.getViewSize();
37976 var w = size.width;
37977 var h = size.height;
37982 //var x = 0, y = 0;
37984 var rs = this.regions;
37985 var north = rs["north"];
37986 var south = rs["south"];
37987 var west = rs["west"];
37988 var east = rs["east"];
37989 var center = rs["center"];
37990 //if(this.hideOnLayout){ // not supported anymore
37991 //c.el.setStyle("display", "none");
37993 if(north && north.isVisible()){
37994 var b = north.getBox();
37995 var m = north.getMargins();
37996 b.width = w - (m.left+m.right);
37999 centerY = b.height + b.y + m.bottom;
38000 centerH -= centerY;
38001 north.updateBox(this.safeBox(b));
38003 if(south && south.isVisible()){
38004 var b = south.getBox();
38005 var m = south.getMargins();
38006 b.width = w - (m.left+m.right);
38008 var totalHeight = (b.height + m.top + m.bottom);
38009 b.y = h - totalHeight + m.top;
38010 centerH -= totalHeight;
38011 south.updateBox(this.safeBox(b));
38013 if(west && west.isVisible()){
38014 var b = west.getBox();
38015 var m = west.getMargins();
38016 b.height = centerH - (m.top+m.bottom);
38018 b.y = centerY + m.top;
38019 var totalWidth = (b.width + m.left + m.right);
38020 centerX += totalWidth;
38021 centerW -= totalWidth;
38022 west.updateBox(this.safeBox(b));
38024 if(east && east.isVisible()){
38025 var b = east.getBox();
38026 var m = east.getMargins();
38027 b.height = centerH - (m.top+m.bottom);
38028 var totalWidth = (b.width + m.left + m.right);
38029 b.x = w - totalWidth + m.left;
38030 b.y = centerY + m.top;
38031 centerW -= totalWidth;
38032 east.updateBox(this.safeBox(b));
38035 var m = center.getMargins();
38037 x: centerX + m.left,
38038 y: centerY + m.top,
38039 width: centerW - (m.left+m.right),
38040 height: centerH - (m.top+m.bottom)
38042 //if(this.hideOnLayout){
38043 //center.el.setStyle("display", "block");
38045 center.updateBox(this.safeBox(centerBox));
38048 this.fireEvent("layout", this);
38052 safeBox : function(box){
38053 box.width = Math.max(0, box.width);
38054 box.height = Math.max(0, box.height);
38059 * Adds a ContentPanel (or subclass) to this layout.
38060 * @param {String} target The target region key (north, south, east, west or center).
38061 * @param {Roo.ContentPanel} panel The panel to add
38062 * @return {Roo.ContentPanel} The added panel
38064 add : function(target, panel){
38066 target = target.toLowerCase();
38067 return this.regions[target].add(panel);
38071 * Remove a ContentPanel (or subclass) to this layout.
38072 * @param {String} target The target region key (north, south, east, west or center).
38073 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38074 * @return {Roo.ContentPanel} The removed panel
38076 remove : function(target, panel){
38077 target = target.toLowerCase();
38078 return this.regions[target].remove(panel);
38082 * Searches all regions for a panel with the specified id
38083 * @param {String} panelId
38084 * @return {Roo.ContentPanel} The panel or null if it wasn't found
38086 findPanel : function(panelId){
38087 var rs = this.regions;
38088 for(var target in rs){
38089 if(typeof rs[target] != "function"){
38090 var p = rs[target].getPanel(panelId);
38100 * Searches all regions for a panel with the specified id and activates (shows) it.
38101 * @param {String/ContentPanel} panelId The panels id or the panel itself
38102 * @return {Roo.ContentPanel} The shown panel or null
38104 showPanel : function(panelId) {
38105 var rs = this.regions;
38106 for(var target in rs){
38107 var r = rs[target];
38108 if(typeof r != "function"){
38109 if(r.hasPanel(panelId)){
38110 return r.showPanel(panelId);
38118 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38119 * @param {Roo.state.Provider} provider (optional) An alternate state provider
38122 restoreState : function(provider){
38124 provider = Roo.state.Manager;
38126 var sm = new Roo.LayoutStateManager();
38127 sm.init(this, provider);
38133 * Adds a xtype elements to the layout.
38137 xtype : 'ContentPanel',
38144 xtype : 'NestedLayoutPanel',
38150 items : [ ... list of content panels or nested layout panels.. ]
38154 * @param {Object} cfg Xtype definition of item to add.
38156 addxtype : function(cfg)
38158 // basically accepts a pannel...
38159 // can accept a layout region..!?!?
38160 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38163 // theory? children can only be panels??
38165 //if (!cfg.xtype.match(/Panel$/)) {
38170 if (typeof(cfg.region) == 'undefined') {
38171 Roo.log("Failed to add Panel, region was not set");
38175 var region = cfg.region;
38181 xitems = cfg.items;
38186 if ( region == 'center') {
38187 Roo.log("Center: " + cfg.title);
38193 case 'Content': // ContentPanel (el, cfg)
38194 case 'Scroll': // ContentPanel (el, cfg)
38196 cfg.autoCreate = cfg.autoCreate || true;
38197 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38199 // var el = this.el.createChild();
38200 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38203 this.add(region, ret);
38207 case 'TreePanel': // our new panel!
38208 cfg.el = this.el.createChild();
38209 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38210 this.add(region, ret);
38215 // create a new Layout (which is a Border Layout...
38217 var clayout = cfg.layout;
38218 clayout.el = this.el.createChild();
38219 clayout.items = clayout.items || [];
38223 // replace this exitems with the clayout ones..
38224 xitems = clayout.items;
38226 // force background off if it's in center...
38227 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38228 cfg.background = false;
38230 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
38233 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38234 //console.log('adding nested layout panel ' + cfg.toSource());
38235 this.add(region, ret);
38236 nb = {}; /// find first...
38241 // needs grid and region
38243 //var el = this.getRegion(region).el.createChild();
38245 *var el = this.el.createChild();
38246 // create the grid first...
38247 cfg.grid.container = el;
38248 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38251 if (region == 'center' && this.active ) {
38252 cfg.background = false;
38255 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38257 this.add(region, ret);
38259 if (cfg.background) {
38260 // render grid on panel activation (if panel background)
38261 ret.on('activate', function(gp) {
38262 if (!gp.grid.rendered) {
38263 // gp.grid.render(el);
38267 // cfg.grid.render(el);
38273 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38274 // it was the old xcomponent building that caused this before.
38275 // espeically if border is the top element in the tree.
38285 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38287 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38288 this.add(region, ret);
38292 throw "Can not add '" + cfg.xtype + "' to Border";
38298 this.beginUpdate();
38302 Roo.each(xitems, function(i) {
38303 region = nb && i.region ? i.region : false;
38305 var add = ret.addxtype(i);
38308 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38309 if (!i.background) {
38310 abn[region] = nb[region] ;
38317 // make the last non-background panel active..
38318 //if (nb) { Roo.log(abn); }
38321 for(var r in abn) {
38322 region = this.getRegion(r);
38324 // tried using nb[r], but it does not work..
38326 region.showPanel(abn[r]);
38337 factory : function(cfg)
38340 var validRegions = Roo.bootstrap.layout.Border.regions;
38342 var target = cfg.region;
38345 var r = Roo.bootstrap.layout;
38349 return new r.North(cfg);
38351 return new r.South(cfg);
38353 return new r.East(cfg);
38355 return new r.West(cfg);
38357 return new r.Center(cfg);
38359 throw 'Layout region "'+target+'" not supported.';
38366 * Ext JS Library 1.1.1
38367 * Copyright(c) 2006-2007, Ext JS, LLC.
38369 * Originally Released Under LGPL - original licence link has changed is not relivant.
38372 * <script type="text/javascript">
38376 * @class Roo.bootstrap.layout.Basic
38377 * @extends Roo.util.Observable
38378 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38379 * and does not have a titlebar, tabs or any other features. All it does is size and position
38380 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38381 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38382 * @cfg {string} region the region that it inhabits..
38383 * @cfg {bool} skipConfig skip config?
38387 Roo.bootstrap.layout.Basic = function(config){
38389 this.mgr = config.mgr;
38391 this.position = config.region;
38393 var skipConfig = config.skipConfig;
38397 * @scope Roo.BasicLayoutRegion
38401 * @event beforeremove
38402 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38403 * @param {Roo.LayoutRegion} this
38404 * @param {Roo.ContentPanel} panel The panel
38405 * @param {Object} e The cancel event object
38407 "beforeremove" : true,
38409 * @event invalidated
38410 * Fires when the layout for this region is changed.
38411 * @param {Roo.LayoutRegion} this
38413 "invalidated" : true,
38415 * @event visibilitychange
38416 * Fires when this region is shown or hidden
38417 * @param {Roo.LayoutRegion} this
38418 * @param {Boolean} visibility true or false
38420 "visibilitychange" : true,
38422 * @event paneladded
38423 * Fires when a panel is added.
38424 * @param {Roo.LayoutRegion} this
38425 * @param {Roo.ContentPanel} panel The panel
38427 "paneladded" : true,
38429 * @event panelremoved
38430 * Fires when a panel is removed.
38431 * @param {Roo.LayoutRegion} this
38432 * @param {Roo.ContentPanel} panel The panel
38434 "panelremoved" : true,
38436 * @event beforecollapse
38437 * Fires when this region before collapse.
38438 * @param {Roo.LayoutRegion} this
38440 "beforecollapse" : true,
38443 * Fires when this region is collapsed.
38444 * @param {Roo.LayoutRegion} this
38446 "collapsed" : true,
38449 * Fires when this region is expanded.
38450 * @param {Roo.LayoutRegion} this
38455 * Fires when this region is slid into view.
38456 * @param {Roo.LayoutRegion} this
38458 "slideshow" : true,
38461 * Fires when this region slides out of view.
38462 * @param {Roo.LayoutRegion} this
38464 "slidehide" : true,
38466 * @event panelactivated
38467 * Fires when a panel is activated.
38468 * @param {Roo.LayoutRegion} this
38469 * @param {Roo.ContentPanel} panel The activated panel
38471 "panelactivated" : true,
38474 * Fires when the user resizes this region.
38475 * @param {Roo.LayoutRegion} this
38476 * @param {Number} newSize The new size (width for east/west, height for north/south)
38480 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38481 this.panels = new Roo.util.MixedCollection();
38482 this.panels.getKey = this.getPanelId.createDelegate(this);
38484 this.activePanel = null;
38485 // ensure listeners are added...
38487 if (config.listeners || config.events) {
38488 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38489 listeners : config.listeners || {},
38490 events : config.events || {}
38494 if(skipConfig !== true){
38495 this.applyConfig(config);
38499 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38501 getPanelId : function(p){
38505 applyConfig : function(config){
38506 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38507 this.config = config;
38512 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38513 * the width, for horizontal (north, south) the height.
38514 * @param {Number} newSize The new width or height
38516 resizeTo : function(newSize){
38517 var el = this.el ? this.el :
38518 (this.activePanel ? this.activePanel.getEl() : null);
38520 switch(this.position){
38523 el.setWidth(newSize);
38524 this.fireEvent("resized", this, newSize);
38528 el.setHeight(newSize);
38529 this.fireEvent("resized", this, newSize);
38535 getBox : function(){
38536 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38539 getMargins : function(){
38540 return this.margins;
38543 updateBox : function(box){
38545 var el = this.activePanel.getEl();
38546 el.dom.style.left = box.x + "px";
38547 el.dom.style.top = box.y + "px";
38548 this.activePanel.setSize(box.width, box.height);
38552 * Returns the container element for this region.
38553 * @return {Roo.Element}
38555 getEl : function(){
38556 return this.activePanel;
38560 * Returns true if this region is currently visible.
38561 * @return {Boolean}
38563 isVisible : function(){
38564 return this.activePanel ? true : false;
38567 setActivePanel : function(panel){
38568 panel = this.getPanel(panel);
38569 if(this.activePanel && this.activePanel != panel){
38570 this.activePanel.setActiveState(false);
38571 this.activePanel.getEl().setLeftTop(-10000,-10000);
38573 this.activePanel = panel;
38574 panel.setActiveState(true);
38576 panel.setSize(this.box.width, this.box.height);
38578 this.fireEvent("panelactivated", this, panel);
38579 this.fireEvent("invalidated");
38583 * Show the specified panel.
38584 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38585 * @return {Roo.ContentPanel} The shown panel or null
38587 showPanel : function(panel){
38588 panel = this.getPanel(panel);
38590 this.setActivePanel(panel);
38596 * Get the active panel for this region.
38597 * @return {Roo.ContentPanel} The active panel or null
38599 getActivePanel : function(){
38600 return this.activePanel;
38604 * Add the passed ContentPanel(s)
38605 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38606 * @return {Roo.ContentPanel} The panel added (if only one was added)
38608 add : function(panel){
38609 if(arguments.length > 1){
38610 for(var i = 0, len = arguments.length; i < len; i++) {
38611 this.add(arguments[i]);
38615 if(this.hasPanel(panel)){
38616 this.showPanel(panel);
38619 var el = panel.getEl();
38620 if(el.dom.parentNode != this.mgr.el.dom){
38621 this.mgr.el.dom.appendChild(el.dom);
38623 if(panel.setRegion){
38624 panel.setRegion(this);
38626 this.panels.add(panel);
38627 el.setStyle("position", "absolute");
38628 if(!panel.background){
38629 this.setActivePanel(panel);
38630 if(this.config.initialSize && this.panels.getCount()==1){
38631 this.resizeTo(this.config.initialSize);
38634 this.fireEvent("paneladded", this, panel);
38639 * Returns true if the panel is in this region.
38640 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38641 * @return {Boolean}
38643 hasPanel : function(panel){
38644 if(typeof panel == "object"){ // must be panel obj
38645 panel = panel.getId();
38647 return this.getPanel(panel) ? true : false;
38651 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38652 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38653 * @param {Boolean} preservePanel Overrides the config preservePanel option
38654 * @return {Roo.ContentPanel} The panel that was removed
38656 remove : function(panel, preservePanel){
38657 panel = this.getPanel(panel);
38662 this.fireEvent("beforeremove", this, panel, e);
38663 if(e.cancel === true){
38666 var panelId = panel.getId();
38667 this.panels.removeKey(panelId);
38672 * Returns the panel specified or null if it's not in this region.
38673 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38674 * @return {Roo.ContentPanel}
38676 getPanel : function(id){
38677 if(typeof id == "object"){ // must be panel obj
38680 return this.panels.get(id);
38684 * Returns this regions position (north/south/east/west/center).
38687 getPosition: function(){
38688 return this.position;
38692 * Ext JS Library 1.1.1
38693 * Copyright(c) 2006-2007, Ext JS, LLC.
38695 * Originally Released Under LGPL - original licence link has changed is not relivant.
38698 * <script type="text/javascript">
38702 * @class Roo.bootstrap.layout.Region
38703 * @extends Roo.bootstrap.layout.Basic
38704 * This class represents a region in a layout manager.
38706 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38707 * @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})
38708 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
38709 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
38710 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
38711 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
38712 * @cfg {String} title The title for the region (overrides panel titles)
38713 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
38714 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38715 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
38716 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38717 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
38718 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38719 * the space available, similar to FireFox 1.5 tabs (defaults to false)
38720 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
38721 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
38722 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
38724 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
38725 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
38726 * @cfg {Boolean} disableTabTips True to disable tab tooltips
38727 * @cfg {Number} width For East/West panels
38728 * @cfg {Number} height For North/South panels
38729 * @cfg {Boolean} split To show the splitter
38730 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
38732 * @cfg {string} cls Extra CSS classes to add to region
38734 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38735 * @cfg {string} region the region that it inhabits..
38738 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
38739 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
38741 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
38742 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
38743 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
38745 Roo.bootstrap.layout.Region = function(config)
38747 this.applyConfig(config);
38749 var mgr = config.mgr;
38750 var pos = config.region;
38751 config.skipConfig = true;
38752 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38755 this.onRender(mgr.el);
38758 this.visible = true;
38759 this.collapsed = false;
38760 this.unrendered_panels = [];
38763 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38765 position: '', // set by wrapper (eg. north/south etc..)
38766 unrendered_panels : null, // unrendered panels.
38768 tabPosition : false,
38770 mgr: false, // points to 'Border'
38773 createBody : function(){
38774 /** This region's body element
38775 * @type Roo.Element */
38776 this.bodyEl = this.el.createChild({
38778 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38782 onRender: function(ctr, pos)
38784 var dh = Roo.DomHelper;
38785 /** This region's container element
38786 * @type Roo.Element */
38787 this.el = dh.append(ctr.dom, {
38789 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38791 /** This region's title element
38792 * @type Roo.Element */
38794 this.titleEl = dh.append(this.el.dom, {
38796 unselectable: "on",
38797 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38799 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38800 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38804 this.titleEl.enableDisplayMode();
38805 /** This region's title text element
38806 * @type HTMLElement */
38807 this.titleTextEl = this.titleEl.dom.firstChild;
38808 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38810 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38811 this.closeBtn.enableDisplayMode();
38812 this.closeBtn.on("click", this.closeClicked, this);
38813 this.closeBtn.hide();
38815 this.createBody(this.config);
38816 if(this.config.hideWhenEmpty){
38818 this.on("paneladded", this.validateVisibility, this);
38819 this.on("panelremoved", this.validateVisibility, this);
38821 if(this.autoScroll){
38822 this.bodyEl.setStyle("overflow", "auto");
38824 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38826 //if(c.titlebar !== false){
38827 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38828 this.titleEl.hide();
38830 this.titleEl.show();
38831 if(this.config.title){
38832 this.titleTextEl.innerHTML = this.config.title;
38836 if(this.config.collapsed){
38837 this.collapse(true);
38839 if(this.config.hidden){
38843 if (this.unrendered_panels && this.unrendered_panels.length) {
38844 for (var i =0;i< this.unrendered_panels.length; i++) {
38845 this.add(this.unrendered_panels[i]);
38847 this.unrendered_panels = null;
38853 applyConfig : function(c)
38856 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38857 var dh = Roo.DomHelper;
38858 if(c.titlebar !== false){
38859 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38860 this.collapseBtn.on("click", this.collapse, this);
38861 this.collapseBtn.enableDisplayMode();
38863 if(c.showPin === true || this.showPin){
38864 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38865 this.stickBtn.enableDisplayMode();
38866 this.stickBtn.on("click", this.expand, this);
38867 this.stickBtn.hide();
38872 /** This region's collapsed element
38873 * @type Roo.Element */
38876 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38877 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38880 if(c.floatable !== false){
38881 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38882 this.collapsedEl.on("click", this.collapseClick, this);
38885 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38886 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38887 id: "message", unselectable: "on", style:{"float":"left"}});
38888 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38890 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38891 this.expandBtn.on("click", this.expand, this);
38895 if(this.collapseBtn){
38896 this.collapseBtn.setVisible(c.collapsible == true);
38899 this.cmargins = c.cmargins || this.cmargins ||
38900 (this.position == "west" || this.position == "east" ?
38901 {top: 0, left: 2, right:2, bottom: 0} :
38902 {top: 2, left: 0, right:0, bottom: 2});
38904 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38907 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38909 this.autoScroll = c.autoScroll || false;
38914 this.duration = c.duration || .30;
38915 this.slideDuration = c.slideDuration || .45;
38920 * Returns true if this region is currently visible.
38921 * @return {Boolean}
38923 isVisible : function(){
38924 return this.visible;
38928 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38929 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
38931 //setCollapsedTitle : function(title){
38932 // title = title || " ";
38933 // if(this.collapsedTitleTextEl){
38934 // this.collapsedTitleTextEl.innerHTML = title;
38938 getBox : function(){
38940 // if(!this.collapsed){
38941 b = this.el.getBox(false, true);
38943 // b = this.collapsedEl.getBox(false, true);
38948 getMargins : function(){
38949 return this.margins;
38950 //return this.collapsed ? this.cmargins : this.margins;
38953 highlight : function(){
38954 this.el.addClass("x-layout-panel-dragover");
38957 unhighlight : function(){
38958 this.el.removeClass("x-layout-panel-dragover");
38961 updateBox : function(box)
38963 if (!this.bodyEl) {
38964 return; // not rendered yet..
38968 if(!this.collapsed){
38969 this.el.dom.style.left = box.x + "px";
38970 this.el.dom.style.top = box.y + "px";
38971 this.updateBody(box.width, box.height);
38973 this.collapsedEl.dom.style.left = box.x + "px";
38974 this.collapsedEl.dom.style.top = box.y + "px";
38975 this.collapsedEl.setSize(box.width, box.height);
38978 this.tabs.autoSizeTabs();
38982 updateBody : function(w, h)
38985 this.el.setWidth(w);
38986 w -= this.el.getBorderWidth("rl");
38987 if(this.config.adjustments){
38988 w += this.config.adjustments[0];
38991 if(h !== null && h > 0){
38992 this.el.setHeight(h);
38993 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38994 h -= this.el.getBorderWidth("tb");
38995 if(this.config.adjustments){
38996 h += this.config.adjustments[1];
38998 this.bodyEl.setHeight(h);
39000 h = this.tabs.syncHeight(h);
39003 if(this.panelSize){
39004 w = w !== null ? w : this.panelSize.width;
39005 h = h !== null ? h : this.panelSize.height;
39007 if(this.activePanel){
39008 var el = this.activePanel.getEl();
39009 w = w !== null ? w : el.getWidth();
39010 h = h !== null ? h : el.getHeight();
39011 this.panelSize = {width: w, height: h};
39012 this.activePanel.setSize(w, h);
39014 if(Roo.isIE && this.tabs){
39015 this.tabs.el.repaint();
39020 * Returns the container element for this region.
39021 * @return {Roo.Element}
39023 getEl : function(){
39028 * Hides this region.
39031 //if(!this.collapsed){
39032 this.el.dom.style.left = "-2000px";
39035 // this.collapsedEl.dom.style.left = "-2000px";
39036 // this.collapsedEl.hide();
39038 this.visible = false;
39039 this.fireEvent("visibilitychange", this, false);
39043 * Shows this region if it was previously hidden.
39046 //if(!this.collapsed){
39049 // this.collapsedEl.show();
39051 this.visible = true;
39052 this.fireEvent("visibilitychange", this, true);
39055 closeClicked : function(){
39056 if(this.activePanel){
39057 this.remove(this.activePanel);
39061 collapseClick : function(e){
39063 e.stopPropagation();
39066 e.stopPropagation();
39072 * Collapses this region.
39073 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39076 collapse : function(skipAnim, skipCheck = false){
39077 if(this.collapsed) {
39081 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39083 this.collapsed = true;
39085 this.split.el.hide();
39087 if(this.config.animate && skipAnim !== true){
39088 this.fireEvent("invalidated", this);
39089 this.animateCollapse();
39091 this.el.setLocation(-20000,-20000);
39093 this.collapsedEl.show();
39094 this.fireEvent("collapsed", this);
39095 this.fireEvent("invalidated", this);
39101 animateCollapse : function(){
39106 * Expands this region if it was previously collapsed.
39107 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39108 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39111 expand : function(e, skipAnim){
39113 e.stopPropagation();
39115 if(!this.collapsed || this.el.hasActiveFx()) {
39119 this.afterSlideIn();
39122 this.collapsed = false;
39123 if(this.config.animate && skipAnim !== true){
39124 this.animateExpand();
39128 this.split.el.show();
39130 this.collapsedEl.setLocation(-2000,-2000);
39131 this.collapsedEl.hide();
39132 this.fireEvent("invalidated", this);
39133 this.fireEvent("expanded", this);
39137 animateExpand : function(){
39141 initTabs : function()
39143 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39145 var ts = new Roo.bootstrap.panel.Tabs({
39146 el: this.bodyEl.dom,
39148 tabPosition: this.tabPosition ? this.tabPosition : 'top',
39149 disableTooltips: this.config.disableTabTips,
39150 toolbar : this.config.toolbar
39153 if(this.config.hideTabs){
39154 ts.stripWrap.setDisplayed(false);
39157 ts.resizeTabs = this.config.resizeTabs === true;
39158 ts.minTabWidth = this.config.minTabWidth || 40;
39159 ts.maxTabWidth = this.config.maxTabWidth || 250;
39160 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39161 ts.monitorResize = false;
39162 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39163 ts.bodyEl.addClass('roo-layout-tabs-body');
39164 this.panels.each(this.initPanelAsTab, this);
39167 initPanelAsTab : function(panel){
39168 var ti = this.tabs.addTab(
39172 this.config.closeOnTab && panel.isClosable(),
39175 if(panel.tabTip !== undefined){
39176 ti.setTooltip(panel.tabTip);
39178 ti.on("activate", function(){
39179 this.setActivePanel(panel);
39182 if(this.config.closeOnTab){
39183 ti.on("beforeclose", function(t, e){
39185 this.remove(panel);
39189 panel.tabItem = ti;
39194 updatePanelTitle : function(panel, title)
39196 if(this.activePanel == panel){
39197 this.updateTitle(title);
39200 var ti = this.tabs.getTab(panel.getEl().id);
39202 if(panel.tabTip !== undefined){
39203 ti.setTooltip(panel.tabTip);
39208 updateTitle : function(title){
39209 if(this.titleTextEl && !this.config.title){
39210 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
39214 setActivePanel : function(panel)
39216 panel = this.getPanel(panel);
39217 if(this.activePanel && this.activePanel != panel){
39218 if(this.activePanel.setActiveState(false) === false){
39222 this.activePanel = panel;
39223 panel.setActiveState(true);
39224 if(this.panelSize){
39225 panel.setSize(this.panelSize.width, this.panelSize.height);
39228 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39230 this.updateTitle(panel.getTitle());
39232 this.fireEvent("invalidated", this);
39234 this.fireEvent("panelactivated", this, panel);
39238 * Shows the specified panel.
39239 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39240 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39242 showPanel : function(panel)
39244 panel = this.getPanel(panel);
39247 var tab = this.tabs.getTab(panel.getEl().id);
39248 if(tab.isHidden()){
39249 this.tabs.unhideTab(tab.id);
39253 this.setActivePanel(panel);
39260 * Get the active panel for this region.
39261 * @return {Roo.ContentPanel} The active panel or null
39263 getActivePanel : function(){
39264 return this.activePanel;
39267 validateVisibility : function(){
39268 if(this.panels.getCount() < 1){
39269 this.updateTitle(" ");
39270 this.closeBtn.hide();
39273 if(!this.isVisible()){
39280 * Adds the passed ContentPanel(s) to this region.
39281 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39282 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39284 add : function(panel)
39286 if(arguments.length > 1){
39287 for(var i = 0, len = arguments.length; i < len; i++) {
39288 this.add(arguments[i]);
39293 // if we have not been rendered yet, then we can not really do much of this..
39294 if (!this.bodyEl) {
39295 this.unrendered_panels.push(panel);
39302 if(this.hasPanel(panel)){
39303 this.showPanel(panel);
39306 panel.setRegion(this);
39307 this.panels.add(panel);
39308 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39309 // sinle panel - no tab...?? would it not be better to render it with the tabs,
39310 // and hide them... ???
39311 this.bodyEl.dom.appendChild(panel.getEl().dom);
39312 if(panel.background !== true){
39313 this.setActivePanel(panel);
39315 this.fireEvent("paneladded", this, panel);
39322 this.initPanelAsTab(panel);
39326 if(panel.background !== true){
39327 this.tabs.activate(panel.getEl().id);
39329 this.fireEvent("paneladded", this, panel);
39334 * Hides the tab for the specified panel.
39335 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39337 hidePanel : function(panel){
39338 if(this.tabs && (panel = this.getPanel(panel))){
39339 this.tabs.hideTab(panel.getEl().id);
39344 * Unhides the tab for a previously hidden panel.
39345 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39347 unhidePanel : function(panel){
39348 if(this.tabs && (panel = this.getPanel(panel))){
39349 this.tabs.unhideTab(panel.getEl().id);
39353 clearPanels : function(){
39354 while(this.panels.getCount() > 0){
39355 this.remove(this.panels.first());
39360 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39361 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39362 * @param {Boolean} preservePanel Overrides the config preservePanel option
39363 * @return {Roo.ContentPanel} The panel that was removed
39365 remove : function(panel, preservePanel)
39367 panel = this.getPanel(panel);
39372 this.fireEvent("beforeremove", this, panel, e);
39373 if(e.cancel === true){
39376 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39377 var panelId = panel.getId();
39378 this.panels.removeKey(panelId);
39380 document.body.appendChild(panel.getEl().dom);
39383 this.tabs.removeTab(panel.getEl().id);
39384 }else if (!preservePanel){
39385 this.bodyEl.dom.removeChild(panel.getEl().dom);
39387 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39388 var p = this.panels.first();
39389 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39390 tempEl.appendChild(p.getEl().dom);
39391 this.bodyEl.update("");
39392 this.bodyEl.dom.appendChild(p.getEl().dom);
39394 this.updateTitle(p.getTitle());
39396 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39397 this.setActivePanel(p);
39399 panel.setRegion(null);
39400 if(this.activePanel == panel){
39401 this.activePanel = null;
39403 if(this.config.autoDestroy !== false && preservePanel !== true){
39404 try{panel.destroy();}catch(e){}
39406 this.fireEvent("panelremoved", this, panel);
39411 * Returns the TabPanel component used by this region
39412 * @return {Roo.TabPanel}
39414 getTabs : function(){
39418 createTool : function(parentEl, className){
39419 var btn = Roo.DomHelper.append(parentEl, {
39421 cls: "x-layout-tools-button",
39424 cls: "roo-layout-tools-button-inner " + className,
39428 btn.addClassOnOver("roo-layout-tools-button-over");
39433 * Ext JS Library 1.1.1
39434 * Copyright(c) 2006-2007, Ext JS, LLC.
39436 * Originally Released Under LGPL - original licence link has changed is not relivant.
39439 * <script type="text/javascript">
39445 * @class Roo.SplitLayoutRegion
39446 * @extends Roo.LayoutRegion
39447 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39449 Roo.bootstrap.layout.Split = function(config){
39450 this.cursor = config.cursor;
39451 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39454 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39456 splitTip : "Drag to resize.",
39457 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39458 useSplitTips : false,
39460 applyConfig : function(config){
39461 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39464 onRender : function(ctr,pos) {
39466 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39467 if(!this.config.split){
39472 var splitEl = Roo.DomHelper.append(ctr.dom, {
39474 id: this.el.id + "-split",
39475 cls: "roo-layout-split roo-layout-split-"+this.position,
39478 /** The SplitBar for this region
39479 * @type Roo.SplitBar */
39480 // does not exist yet...
39481 Roo.log([this.position, this.orientation]);
39483 this.split = new Roo.bootstrap.SplitBar({
39484 dragElement : splitEl,
39485 resizingElement: this.el,
39486 orientation : this.orientation
39489 this.split.on("moved", this.onSplitMove, this);
39490 this.split.useShim = this.config.useShim === true;
39491 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39492 if(this.useSplitTips){
39493 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39495 //if(config.collapsible){
39496 // this.split.el.on("dblclick", this.collapse, this);
39499 if(typeof this.config.minSize != "undefined"){
39500 this.split.minSize = this.config.minSize;
39502 if(typeof this.config.maxSize != "undefined"){
39503 this.split.maxSize = this.config.maxSize;
39505 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39506 this.hideSplitter();
39511 getHMaxSize : function(){
39512 var cmax = this.config.maxSize || 10000;
39513 var center = this.mgr.getRegion("center");
39514 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39517 getVMaxSize : function(){
39518 var cmax = this.config.maxSize || 10000;
39519 var center = this.mgr.getRegion("center");
39520 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39523 onSplitMove : function(split, newSize){
39524 this.fireEvent("resized", this, newSize);
39528 * Returns the {@link Roo.SplitBar} for this region.
39529 * @return {Roo.SplitBar}
39531 getSplitBar : function(){
39536 this.hideSplitter();
39537 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39540 hideSplitter : function(){
39542 this.split.el.setLocation(-2000,-2000);
39543 this.split.el.hide();
39549 this.split.el.show();
39551 Roo.bootstrap.layout.Split.superclass.show.call(this);
39554 beforeSlide: function(){
39555 if(Roo.isGecko){// firefox overflow auto bug workaround
39556 this.bodyEl.clip();
39558 this.tabs.bodyEl.clip();
39560 if(this.activePanel){
39561 this.activePanel.getEl().clip();
39563 if(this.activePanel.beforeSlide){
39564 this.activePanel.beforeSlide();
39570 afterSlide : function(){
39571 if(Roo.isGecko){// firefox overflow auto bug workaround
39572 this.bodyEl.unclip();
39574 this.tabs.bodyEl.unclip();
39576 if(this.activePanel){
39577 this.activePanel.getEl().unclip();
39578 if(this.activePanel.afterSlide){
39579 this.activePanel.afterSlide();
39585 initAutoHide : function(){
39586 if(this.autoHide !== false){
39587 if(!this.autoHideHd){
39588 var st = new Roo.util.DelayedTask(this.slideIn, this);
39589 this.autoHideHd = {
39590 "mouseout": function(e){
39591 if(!e.within(this.el, true)){
39595 "mouseover" : function(e){
39601 this.el.on(this.autoHideHd);
39605 clearAutoHide : function(){
39606 if(this.autoHide !== false){
39607 this.el.un("mouseout", this.autoHideHd.mouseout);
39608 this.el.un("mouseover", this.autoHideHd.mouseover);
39612 clearMonitor : function(){
39613 Roo.get(document).un("click", this.slideInIf, this);
39616 // these names are backwards but not changed for compat
39617 slideOut : function(){
39618 if(this.isSlid || this.el.hasActiveFx()){
39621 this.isSlid = true;
39622 if(this.collapseBtn){
39623 this.collapseBtn.hide();
39625 this.closeBtnState = this.closeBtn.getStyle('display');
39626 this.closeBtn.hide();
39628 this.stickBtn.show();
39631 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39632 this.beforeSlide();
39633 this.el.setStyle("z-index", 10001);
39634 this.el.slideIn(this.getSlideAnchor(), {
39635 callback: function(){
39637 this.initAutoHide();
39638 Roo.get(document).on("click", this.slideInIf, this);
39639 this.fireEvent("slideshow", this);
39646 afterSlideIn : function(){
39647 this.clearAutoHide();
39648 this.isSlid = false;
39649 this.clearMonitor();
39650 this.el.setStyle("z-index", "");
39651 if(this.collapseBtn){
39652 this.collapseBtn.show();
39654 this.closeBtn.setStyle('display', this.closeBtnState);
39656 this.stickBtn.hide();
39658 this.fireEvent("slidehide", this);
39661 slideIn : function(cb){
39662 if(!this.isSlid || this.el.hasActiveFx()){
39666 this.isSlid = false;
39667 this.beforeSlide();
39668 this.el.slideOut(this.getSlideAnchor(), {
39669 callback: function(){
39670 this.el.setLeftTop(-10000, -10000);
39672 this.afterSlideIn();
39680 slideInIf : function(e){
39681 if(!e.within(this.el)){
39686 animateCollapse : function(){
39687 this.beforeSlide();
39688 this.el.setStyle("z-index", 20000);
39689 var anchor = this.getSlideAnchor();
39690 this.el.slideOut(anchor, {
39691 callback : function(){
39692 this.el.setStyle("z-index", "");
39693 this.collapsedEl.slideIn(anchor, {duration:.3});
39695 this.el.setLocation(-10000,-10000);
39697 this.fireEvent("collapsed", this);
39704 animateExpand : function(){
39705 this.beforeSlide();
39706 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39707 this.el.setStyle("z-index", 20000);
39708 this.collapsedEl.hide({
39711 this.el.slideIn(this.getSlideAnchor(), {
39712 callback : function(){
39713 this.el.setStyle("z-index", "");
39716 this.split.el.show();
39718 this.fireEvent("invalidated", this);
39719 this.fireEvent("expanded", this);
39747 getAnchor : function(){
39748 return this.anchors[this.position];
39751 getCollapseAnchor : function(){
39752 return this.canchors[this.position];
39755 getSlideAnchor : function(){
39756 return this.sanchors[this.position];
39759 getAlignAdj : function(){
39760 var cm = this.cmargins;
39761 switch(this.position){
39777 getExpandAdj : function(){
39778 var c = this.collapsedEl, cm = this.cmargins;
39779 switch(this.position){
39781 return [-(cm.right+c.getWidth()+cm.left), 0];
39784 return [cm.right+c.getWidth()+cm.left, 0];
39787 return [0, -(cm.top+cm.bottom+c.getHeight())];
39790 return [0, cm.top+cm.bottom+c.getHeight()];
39796 * Ext JS Library 1.1.1
39797 * Copyright(c) 2006-2007, Ext JS, LLC.
39799 * Originally Released Under LGPL - original licence link has changed is not relivant.
39802 * <script type="text/javascript">
39805 * These classes are private internal classes
39807 Roo.bootstrap.layout.Center = function(config){
39808 config.region = "center";
39809 Roo.bootstrap.layout.Region.call(this, config);
39810 this.visible = true;
39811 this.minWidth = config.minWidth || 20;
39812 this.minHeight = config.minHeight || 20;
39815 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39817 // center panel can't be hidden
39821 // center panel can't be hidden
39824 getMinWidth: function(){
39825 return this.minWidth;
39828 getMinHeight: function(){
39829 return this.minHeight;
39843 Roo.bootstrap.layout.North = function(config)
39845 config.region = 'north';
39846 config.cursor = 'n-resize';
39848 Roo.bootstrap.layout.Split.call(this, config);
39852 this.split.placement = Roo.bootstrap.SplitBar.TOP;
39853 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39854 this.split.el.addClass("roo-layout-split-v");
39856 //var size = config.initialSize || config.height;
39857 //if(this.el && typeof size != "undefined"){
39858 // this.el.setHeight(size);
39861 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39863 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39866 onRender : function(ctr, pos)
39868 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39869 var size = this.config.initialSize || this.config.height;
39870 if(this.el && typeof size != "undefined"){
39871 this.el.setHeight(size);
39876 getBox : function(){
39877 if(this.collapsed){
39878 return this.collapsedEl.getBox();
39880 var box = this.el.getBox();
39882 box.height += this.split.el.getHeight();
39887 updateBox : function(box){
39888 if(this.split && !this.collapsed){
39889 box.height -= this.split.el.getHeight();
39890 this.split.el.setLeft(box.x);
39891 this.split.el.setTop(box.y+box.height);
39892 this.split.el.setWidth(box.width);
39894 if(this.collapsed){
39895 this.updateBody(box.width, null);
39897 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39905 Roo.bootstrap.layout.South = function(config){
39906 config.region = 'south';
39907 config.cursor = 's-resize';
39908 Roo.bootstrap.layout.Split.call(this, config);
39910 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39911 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39912 this.split.el.addClass("roo-layout-split-v");
39917 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39918 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39920 onRender : function(ctr, pos)
39922 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39923 var size = this.config.initialSize || this.config.height;
39924 if(this.el && typeof size != "undefined"){
39925 this.el.setHeight(size);
39930 getBox : function(){
39931 if(this.collapsed){
39932 return this.collapsedEl.getBox();
39934 var box = this.el.getBox();
39936 var sh = this.split.el.getHeight();
39943 updateBox : function(box){
39944 if(this.split && !this.collapsed){
39945 var sh = this.split.el.getHeight();
39948 this.split.el.setLeft(box.x);
39949 this.split.el.setTop(box.y-sh);
39950 this.split.el.setWidth(box.width);
39952 if(this.collapsed){
39953 this.updateBody(box.width, null);
39955 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39959 Roo.bootstrap.layout.East = function(config){
39960 config.region = "east";
39961 config.cursor = "e-resize";
39962 Roo.bootstrap.layout.Split.call(this, config);
39964 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39965 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39966 this.split.el.addClass("roo-layout-split-h");
39970 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39971 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39973 onRender : function(ctr, pos)
39975 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39976 var size = this.config.initialSize || this.config.width;
39977 if(this.el && typeof size != "undefined"){
39978 this.el.setWidth(size);
39983 getBox : function(){
39984 if(this.collapsed){
39985 return this.collapsedEl.getBox();
39987 var box = this.el.getBox();
39989 var sw = this.split.el.getWidth();
39996 updateBox : function(box){
39997 if(this.split && !this.collapsed){
39998 var sw = this.split.el.getWidth();
40000 this.split.el.setLeft(box.x);
40001 this.split.el.setTop(box.y);
40002 this.split.el.setHeight(box.height);
40005 if(this.collapsed){
40006 this.updateBody(null, box.height);
40008 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40012 Roo.bootstrap.layout.West = function(config){
40013 config.region = "west";
40014 config.cursor = "w-resize";
40016 Roo.bootstrap.layout.Split.call(this, config);
40018 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40019 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40020 this.split.el.addClass("roo-layout-split-h");
40024 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40025 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40027 onRender: function(ctr, pos)
40029 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40030 var size = this.config.initialSize || this.config.width;
40031 if(typeof size != "undefined"){
40032 this.el.setWidth(size);
40036 getBox : function(){
40037 if(this.collapsed){
40038 return this.collapsedEl.getBox();
40040 var box = this.el.getBox();
40041 if (box.width == 0) {
40042 box.width = this.config.width; // kludge?
40045 box.width += this.split.el.getWidth();
40050 updateBox : function(box){
40051 if(this.split && !this.collapsed){
40052 var sw = this.split.el.getWidth();
40054 this.split.el.setLeft(box.x+box.width);
40055 this.split.el.setTop(box.y);
40056 this.split.el.setHeight(box.height);
40058 if(this.collapsed){
40059 this.updateBody(null, box.height);
40061 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40063 });Roo.namespace("Roo.bootstrap.panel");/*
40065 * Ext JS Library 1.1.1
40066 * Copyright(c) 2006-2007, Ext JS, LLC.
40068 * Originally Released Under LGPL - original licence link has changed is not relivant.
40071 * <script type="text/javascript">
40074 * @class Roo.ContentPanel
40075 * @extends Roo.util.Observable
40076 * A basic ContentPanel element.
40077 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
40078 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
40079 * @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
40080 * @cfg {Boolean} closable True if the panel can be closed/removed
40081 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
40082 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40083 * @cfg {Toolbar} toolbar A toolbar for this panel
40084 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
40085 * @cfg {String} title The title for this panel
40086 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40087 * @cfg {String} url Calls {@link #setUrl} with this value
40088 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40089 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
40090 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
40091 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
40092 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
40093 * @cfg {Boolean} badges render the badges
40094 * @cfg {String} cls extra classes to use
40095 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40098 * Create a new ContentPanel.
40099 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40100 * @param {String/Object} config A string to set only the title or a config object
40101 * @param {String} content (optional) Set the HTML content for this panel
40102 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40104 Roo.bootstrap.panel.Content = function( config){
40106 this.tpl = config.tpl || false;
40108 var el = config.el;
40109 var content = config.content;
40111 if(config.autoCreate){ // xtype is available if this is called from factory
40114 this.el = Roo.get(el);
40115 if(!this.el && config && config.autoCreate){
40116 if(typeof config.autoCreate == "object"){
40117 if(!config.autoCreate.id){
40118 config.autoCreate.id = config.id||el;
40120 this.el = Roo.DomHelper.append(document.body,
40121 config.autoCreate, true);
40125 cls: (config.cls || '') +
40126 (config.background ? ' bg-' + config.background : '') +
40127 " roo-layout-inactive-content",
40130 if (config.iframe) {
40134 style : 'border: 0px',
40135 src : 'about:blank'
40141 elcfg.html = config.html;
40145 this.el = Roo.DomHelper.append(document.body, elcfg , true);
40146 if (config.iframe) {
40147 this.iframeEl = this.el.select('iframe',true).first();
40152 this.closable = false;
40153 this.loaded = false;
40154 this.active = false;
40157 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40159 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40161 this.wrapEl = this.el; //this.el.wrap();
40163 if (config.toolbar.items) {
40164 ti = config.toolbar.items ;
40165 delete config.toolbar.items ;
40169 this.toolbar.render(this.wrapEl, 'before');
40170 for(var i =0;i < ti.length;i++) {
40171 // Roo.log(['add child', items[i]]);
40172 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40174 this.toolbar.items = nitems;
40175 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40176 delete config.toolbar;
40180 // xtype created footer. - not sure if will work as we normally have to render first..
40181 if (this.footer && !this.footer.el && this.footer.xtype) {
40182 if (!this.wrapEl) {
40183 this.wrapEl = this.el.wrap();
40186 this.footer.container = this.wrapEl.createChild();
40188 this.footer = Roo.factory(this.footer, Roo);
40193 if(typeof config == "string"){
40194 this.title = config;
40196 Roo.apply(this, config);
40200 this.resizeEl = Roo.get(this.resizeEl, true);
40202 this.resizeEl = this.el;
40204 // handle view.xtype
40212 * Fires when this panel is activated.
40213 * @param {Roo.ContentPanel} this
40217 * @event deactivate
40218 * Fires when this panel is activated.
40219 * @param {Roo.ContentPanel} this
40221 "deactivate" : true,
40225 * Fires when this panel is resized if fitToFrame is true.
40226 * @param {Roo.ContentPanel} this
40227 * @param {Number} width The width after any component adjustments
40228 * @param {Number} height The height after any component adjustments
40234 * Fires when this tab is created
40235 * @param {Roo.ContentPanel} this
40241 * Fires when this content is scrolled
40242 * @param {Roo.ContentPanel} this
40243 * @param {Event} scrollEvent
40254 if(this.autoScroll && !this.iframe){
40255 this.resizeEl.setStyle("overflow", "auto");
40256 this.resizeEl.on('scroll', this.onScroll, this);
40258 // fix randome scrolling
40259 //this.el.on('scroll', function() {
40260 // Roo.log('fix random scolling');
40261 // this.scrollTo('top',0);
40264 content = content || this.content;
40266 this.setContent(content);
40268 if(config && config.url){
40269 this.setUrl(this.url, this.params, this.loadOnce);
40274 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40276 if (this.view && typeof(this.view.xtype) != 'undefined') {
40277 this.view.el = this.el.appendChild(document.createElement("div"));
40278 this.view = Roo.factory(this.view);
40279 this.view.render && this.view.render(false, '');
40283 this.fireEvent('render', this);
40286 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40296 /* Resize Element - use this to work out scroll etc. */
40299 setRegion : function(region){
40300 this.region = region;
40301 this.setActiveClass(region && !this.background);
40305 setActiveClass: function(state)
40308 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40309 this.el.setStyle('position','relative');
40311 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40312 this.el.setStyle('position', 'absolute');
40317 * Returns the toolbar for this Panel if one was configured.
40318 * @return {Roo.Toolbar}
40320 getToolbar : function(){
40321 return this.toolbar;
40324 setActiveState : function(active)
40326 this.active = active;
40327 this.setActiveClass(active);
40329 if(this.fireEvent("deactivate", this) === false){
40334 this.fireEvent("activate", this);
40338 * Updates this panel's element (not for iframe)
40339 * @param {String} content The new content
40340 * @param {Boolean} loadScripts (optional) true to look for and process scripts
40342 setContent : function(content, loadScripts){
40347 this.el.update(content, loadScripts);
40350 ignoreResize : function(w, h){
40351 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40354 this.lastSize = {width: w, height: h};
40359 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40360 * @return {Roo.UpdateManager} The UpdateManager
40362 getUpdateManager : function(){
40366 return this.el.getUpdateManager();
40369 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40370 * Does not work with IFRAME contents
40371 * @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:
40374 url: "your-url.php",
40375 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40376 callback: yourFunction,
40377 scope: yourObject, //(optional scope)
40380 text: "Loading...",
40386 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40387 * 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.
40388 * @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}
40389 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40390 * @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.
40391 * @return {Roo.ContentPanel} this
40399 var um = this.el.getUpdateManager();
40400 um.update.apply(um, arguments);
40406 * 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.
40407 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40408 * @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)
40409 * @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)
40410 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40412 setUrl : function(url, params, loadOnce){
40414 this.iframeEl.dom.src = url;
40418 if(this.refreshDelegate){
40419 this.removeListener("activate", this.refreshDelegate);
40421 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40422 this.on("activate", this.refreshDelegate);
40423 return this.el.getUpdateManager();
40426 _handleRefresh : function(url, params, loadOnce){
40427 if(!loadOnce || !this.loaded){
40428 var updater = this.el.getUpdateManager();
40429 updater.update(url, params, this._setLoaded.createDelegate(this));
40433 _setLoaded : function(){
40434 this.loaded = true;
40438 * Returns this panel's id
40441 getId : function(){
40446 * Returns this panel's element - used by regiosn to add.
40447 * @return {Roo.Element}
40449 getEl : function(){
40450 return this.wrapEl || this.el;
40455 adjustForComponents : function(width, height)
40457 //Roo.log('adjustForComponents ');
40458 if(this.resizeEl != this.el){
40459 width -= this.el.getFrameWidth('lr');
40460 height -= this.el.getFrameWidth('tb');
40463 var te = this.toolbar.getEl();
40464 te.setWidth(width);
40465 height -= te.getHeight();
40468 var te = this.footer.getEl();
40469 te.setWidth(width);
40470 height -= te.getHeight();
40474 if(this.adjustments){
40475 width += this.adjustments[0];
40476 height += this.adjustments[1];
40478 return {"width": width, "height": height};
40481 setSize : function(width, height){
40482 if(this.fitToFrame && !this.ignoreResize(width, height)){
40483 if(this.fitContainer && this.resizeEl != this.el){
40484 this.el.setSize(width, height);
40486 var size = this.adjustForComponents(width, height);
40488 this.iframeEl.setSize(width,height);
40491 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40492 this.fireEvent('resize', this, size.width, size.height);
40499 * Returns this panel's title
40502 getTitle : function(){
40504 if (typeof(this.title) != 'object') {
40509 for (var k in this.title) {
40510 if (!this.title.hasOwnProperty(k)) {
40514 if (k.indexOf('-') >= 0) {
40515 var s = k.split('-');
40516 for (var i = 0; i<s.length; i++) {
40517 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40520 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40527 * Set this panel's title
40528 * @param {String} title
40530 setTitle : function(title){
40531 this.title = title;
40533 this.region.updatePanelTitle(this, title);
40538 * Returns true is this panel was configured to be closable
40539 * @return {Boolean}
40541 isClosable : function(){
40542 return this.closable;
40545 beforeSlide : function(){
40547 this.resizeEl.clip();
40550 afterSlide : function(){
40552 this.resizeEl.unclip();
40556 * Force a content refresh from the URL specified in the {@link #setUrl} method.
40557 * Will fail silently if the {@link #setUrl} method has not been called.
40558 * This does not activate the panel, just updates its content.
40560 refresh : function(){
40561 if(this.refreshDelegate){
40562 this.loaded = false;
40563 this.refreshDelegate();
40568 * Destroys this panel
40570 destroy : function(){
40571 this.el.removeAllListeners();
40572 var tempEl = document.createElement("span");
40573 tempEl.appendChild(this.el.dom);
40574 tempEl.innerHTML = "";
40580 * form - if the content panel contains a form - this is a reference to it.
40581 * @type {Roo.form.Form}
40585 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40586 * This contains a reference to it.
40592 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40602 * @param {Object} cfg Xtype definition of item to add.
40606 getChildContainer: function () {
40607 return this.getEl();
40611 onScroll : function(e)
40613 this.fireEvent('scroll', this, e);
40618 var ret = new Roo.factory(cfg);
40623 if (cfg.xtype.match(/^Form$/)) {
40626 //if (this.footer) {
40627 // el = this.footer.container.insertSibling(false, 'before');
40629 el = this.el.createChild();
40632 this.form = new Roo.form.Form(cfg);
40635 if ( this.form.allItems.length) {
40636 this.form.render(el.dom);
40640 // should only have one of theses..
40641 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40642 // views.. should not be just added - used named prop 'view''
40644 cfg.el = this.el.appendChild(document.createElement("div"));
40647 var ret = new Roo.factory(cfg);
40649 ret.render && ret.render(false, ''); // render blank..
40659 * @class Roo.bootstrap.panel.Grid
40660 * @extends Roo.bootstrap.panel.Content
40662 * Create a new GridPanel.
40663 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40664 * @param {Object} config A the config object
40670 Roo.bootstrap.panel.Grid = function(config)
40674 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40675 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40677 config.el = this.wrapper;
40678 //this.el = this.wrapper;
40680 if (config.container) {
40681 // ctor'ed from a Border/panel.grid
40684 this.wrapper.setStyle("overflow", "hidden");
40685 this.wrapper.addClass('roo-grid-container');
40690 if(config.toolbar){
40691 var tool_el = this.wrapper.createChild();
40692 this.toolbar = Roo.factory(config.toolbar);
40694 if (config.toolbar.items) {
40695 ti = config.toolbar.items ;
40696 delete config.toolbar.items ;
40700 this.toolbar.render(tool_el);
40701 for(var i =0;i < ti.length;i++) {
40702 // Roo.log(['add child', items[i]]);
40703 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40705 this.toolbar.items = nitems;
40707 delete config.toolbar;
40710 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40711 config.grid.scrollBody = true;;
40712 config.grid.monitorWindowResize = false; // turn off autosizing
40713 config.grid.autoHeight = false;
40714 config.grid.autoWidth = false;
40716 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40718 if (config.background) {
40719 // render grid on panel activation (if panel background)
40720 this.on('activate', function(gp) {
40721 if (!gp.grid.rendered) {
40722 gp.grid.render(this.wrapper);
40723 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40728 this.grid.render(this.wrapper);
40729 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40732 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40733 // ??? needed ??? config.el = this.wrapper;
40738 // xtype created footer. - not sure if will work as we normally have to render first..
40739 if (this.footer && !this.footer.el && this.footer.xtype) {
40741 var ctr = this.grid.getView().getFooterPanel(true);
40742 this.footer.dataSource = this.grid.dataSource;
40743 this.footer = Roo.factory(this.footer, Roo);
40744 this.footer.render(ctr);
40754 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40755 getId : function(){
40756 return this.grid.id;
40760 * Returns the grid for this panel
40761 * @return {Roo.bootstrap.Table}
40763 getGrid : function(){
40767 setSize : function(width, height){
40768 if(!this.ignoreResize(width, height)){
40769 var grid = this.grid;
40770 var size = this.adjustForComponents(width, height);
40771 // tfoot is not a footer?
40774 var gridel = grid.getGridEl();
40775 gridel.setSize(size.width, size.height);
40777 var tbd = grid.getGridEl().select('tbody', true).first();
40778 var thd = grid.getGridEl().select('thead',true).first();
40779 var tbf= grid.getGridEl().select('tfoot', true).first();
40782 size.height -= tbf.getHeight();
40785 size.height -= thd.getHeight();
40788 tbd.setSize(size.width, size.height );
40789 // this is for the account management tab -seems to work there.
40790 var thd = grid.getGridEl().select('thead',true).first();
40792 // tbd.setSize(size.width, size.height - thd.getHeight());
40801 beforeSlide : function(){
40802 this.grid.getView().scroller.clip();
40805 afterSlide : function(){
40806 this.grid.getView().scroller.unclip();
40809 destroy : function(){
40810 this.grid.destroy();
40812 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
40817 * @class Roo.bootstrap.panel.Nest
40818 * @extends Roo.bootstrap.panel.Content
40820 * Create a new Panel, that can contain a layout.Border.
40823 * @param {Roo.BorderLayout} layout The layout for this panel
40824 * @param {String/Object} config A string to set only the title or a config object
40826 Roo.bootstrap.panel.Nest = function(config)
40828 // construct with only one argument..
40829 /* FIXME - implement nicer consturctors
40830 if (layout.layout) {
40832 layout = config.layout;
40833 delete config.layout;
40835 if (layout.xtype && !layout.getEl) {
40836 // then layout needs constructing..
40837 layout = Roo.factory(layout, Roo);
40841 config.el = config.layout.getEl();
40843 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40845 config.layout.monitorWindowResize = false; // turn off autosizing
40846 this.layout = config.layout;
40847 this.layout.getEl().addClass("roo-layout-nested-layout");
40848 this.layout.parent = this;
40855 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40857 setSize : function(width, height){
40858 if(!this.ignoreResize(width, height)){
40859 var size = this.adjustForComponents(width, height);
40860 var el = this.layout.getEl();
40861 if (size.height < 1) {
40862 el.setWidth(size.width);
40864 el.setSize(size.width, size.height);
40866 var touch = el.dom.offsetWidth;
40867 this.layout.layout();
40868 // ie requires a double layout on the first pass
40869 if(Roo.isIE && !this.initialized){
40870 this.initialized = true;
40871 this.layout.layout();
40876 // activate all subpanels if not currently active..
40878 setActiveState : function(active){
40879 this.active = active;
40880 this.setActiveClass(active);
40883 this.fireEvent("deactivate", this);
40887 this.fireEvent("activate", this);
40888 // not sure if this should happen before or after..
40889 if (!this.layout) {
40890 return; // should not happen..
40893 for (var r in this.layout.regions) {
40894 reg = this.layout.getRegion(r);
40895 if (reg.getActivePanel()) {
40896 //reg.showPanel(reg.getActivePanel()); // force it to activate..
40897 reg.setActivePanel(reg.getActivePanel());
40900 if (!reg.panels.length) {
40903 reg.showPanel(reg.getPanel(0));
40912 * Returns the nested BorderLayout for this panel
40913 * @return {Roo.BorderLayout}
40915 getLayout : function(){
40916 return this.layout;
40920 * Adds a xtype elements to the layout of the nested panel
40924 xtype : 'ContentPanel',
40931 xtype : 'NestedLayoutPanel',
40937 items : [ ... list of content panels or nested layout panels.. ]
40941 * @param {Object} cfg Xtype definition of item to add.
40943 addxtype : function(cfg) {
40944 return this.layout.addxtype(cfg);
40949 * Ext JS Library 1.1.1
40950 * Copyright(c) 2006-2007, Ext JS, LLC.
40952 * Originally Released Under LGPL - original licence link has changed is not relivant.
40955 * <script type="text/javascript">
40958 * @class Roo.TabPanel
40959 * @extends Roo.util.Observable
40960 * A lightweight tab container.
40964 // basic tabs 1, built from existing content
40965 var tabs = new Roo.TabPanel("tabs1");
40966 tabs.addTab("script", "View Script");
40967 tabs.addTab("markup", "View Markup");
40968 tabs.activate("script");
40970 // more advanced tabs, built from javascript
40971 var jtabs = new Roo.TabPanel("jtabs");
40972 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40974 // set up the UpdateManager
40975 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40976 var updater = tab2.getUpdateManager();
40977 updater.setDefaultUrl("ajax1.htm");
40978 tab2.on('activate', updater.refresh, updater, true);
40980 // Use setUrl for Ajax loading
40981 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40982 tab3.setUrl("ajax2.htm", null, true);
40985 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40988 jtabs.activate("jtabs-1");
40991 * Create a new TabPanel.
40992 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40993 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40995 Roo.bootstrap.panel.Tabs = function(config){
40997 * The container element for this TabPanel.
40998 * @type Roo.Element
41000 this.el = Roo.get(config.el);
41003 if(typeof config == "boolean"){
41004 this.tabPosition = config ? "bottom" : "top";
41006 Roo.apply(this, config);
41010 if(this.tabPosition == "bottom"){
41011 // if tabs are at the bottom = create the body first.
41012 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41013 this.el.addClass("roo-tabs-bottom");
41015 // next create the tabs holders
41017 if (this.tabPosition == "west"){
41019 var reg = this.region; // fake it..
41021 if (!reg.mgr.parent) {
41024 reg = reg.mgr.parent.region;
41026 Roo.log("got nest?");
41028 if (reg.mgr.getRegion('west')) {
41029 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41030 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41031 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41032 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41033 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41041 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41042 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41043 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41044 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41049 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41052 // finally - if tabs are at the top, then create the body last..
41053 if(this.tabPosition != "bottom"){
41054 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41055 * @type Roo.Element
41057 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41058 this.el.addClass("roo-tabs-top");
41062 this.bodyEl.setStyle("position", "relative");
41064 this.active = null;
41065 this.activateDelegate = this.activate.createDelegate(this);
41070 * Fires when the active tab changes
41071 * @param {Roo.TabPanel} this
41072 * @param {Roo.TabPanelItem} activePanel The new active tab
41076 * @event beforetabchange
41077 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41078 * @param {Roo.TabPanel} this
41079 * @param {Object} e Set cancel to true on this object to cancel the tab change
41080 * @param {Roo.TabPanelItem} tab The tab being changed to
41082 "beforetabchange" : true
41085 Roo.EventManager.onWindowResize(this.onResize, this);
41086 this.cpad = this.el.getPadding("lr");
41087 this.hiddenCount = 0;
41090 // toolbar on the tabbar support...
41091 if (this.toolbar) {
41092 alert("no toolbar support yet");
41093 this.toolbar = false;
41095 var tcfg = this.toolbar;
41096 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
41097 this.toolbar = new Roo.Toolbar(tcfg);
41098 if (Roo.isSafari) {
41099 var tbl = tcfg.container.child('table', true);
41100 tbl.setAttribute('width', '100%');
41108 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41111 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41113 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41115 tabPosition : "top",
41117 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41119 currentTabWidth : 0,
41121 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41125 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41129 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41131 preferredTabWidth : 175,
41133 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41135 resizeTabs : false,
41137 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41139 monitorResize : true,
41141 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
41143 toolbar : false, // set by caller..
41145 region : false, /// set by caller
41147 disableTooltips : true, // not used yet...
41150 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41151 * @param {String} id The id of the div to use <b>or create</b>
41152 * @param {String} text The text for the tab
41153 * @param {String} content (optional) Content to put in the TabPanelItem body
41154 * @param {Boolean} closable (optional) True to create a close icon on the tab
41155 * @return {Roo.TabPanelItem} The created TabPanelItem
41157 addTab : function(id, text, content, closable, tpl)
41159 var item = new Roo.bootstrap.panel.TabItem({
41163 closable : closable,
41166 this.addTabItem(item);
41168 item.setContent(content);
41174 * Returns the {@link Roo.TabPanelItem} with the specified id/index
41175 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41176 * @return {Roo.TabPanelItem}
41178 getTab : function(id){
41179 return this.items[id];
41183 * Hides the {@link Roo.TabPanelItem} with the specified id/index
41184 * @param {String/Number} id The id or index of the TabPanelItem to hide.
41186 hideTab : function(id){
41187 var t = this.items[id];
41190 this.hiddenCount++;
41191 this.autoSizeTabs();
41196 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41197 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41199 unhideTab : function(id){
41200 var t = this.items[id];
41202 t.setHidden(false);
41203 this.hiddenCount--;
41204 this.autoSizeTabs();
41209 * Adds an existing {@link Roo.TabPanelItem}.
41210 * @param {Roo.TabPanelItem} item The TabPanelItem to add
41212 addTabItem : function(item)
41214 this.items[item.id] = item;
41215 this.items.push(item);
41216 this.autoSizeTabs();
41217 // if(this.resizeTabs){
41218 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41219 // this.autoSizeTabs();
41221 // item.autoSize();
41226 * Removes a {@link Roo.TabPanelItem}.
41227 * @param {String/Number} id The id or index of the TabPanelItem to remove.
41229 removeTab : function(id){
41230 var items = this.items;
41231 var tab = items[id];
41232 if(!tab) { return; }
41233 var index = items.indexOf(tab);
41234 if(this.active == tab && items.length > 1){
41235 var newTab = this.getNextAvailable(index);
41240 this.stripEl.dom.removeChild(tab.pnode.dom);
41241 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41242 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41244 items.splice(index, 1);
41245 delete this.items[tab.id];
41246 tab.fireEvent("close", tab);
41247 tab.purgeListeners();
41248 this.autoSizeTabs();
41251 getNextAvailable : function(start){
41252 var items = this.items;
41254 // look for a next tab that will slide over to
41255 // replace the one being removed
41256 while(index < items.length){
41257 var item = items[++index];
41258 if(item && !item.isHidden()){
41262 // if one isn't found select the previous tab (on the left)
41265 var item = items[--index];
41266 if(item && !item.isHidden()){
41274 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41275 * @param {String/Number} id The id or index of the TabPanelItem to disable.
41277 disableTab : function(id){
41278 var tab = this.items[id];
41279 if(tab && this.active != tab){
41285 * Enables a {@link Roo.TabPanelItem} that is disabled.
41286 * @param {String/Number} id The id or index of the TabPanelItem to enable.
41288 enableTab : function(id){
41289 var tab = this.items[id];
41294 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41295 * @param {String/Number} id The id or index of the TabPanelItem to activate.
41296 * @return {Roo.TabPanelItem} The TabPanelItem.
41298 activate : function(id)
41300 //Roo.log('activite:' + id);
41302 var tab = this.items[id];
41306 if(tab == this.active || tab.disabled){
41310 this.fireEvent("beforetabchange", this, e, tab);
41311 if(e.cancel !== true && !tab.disabled){
41313 this.active.hide();
41315 this.active = this.items[id];
41316 this.active.show();
41317 this.fireEvent("tabchange", this, this.active);
41323 * Gets the active {@link Roo.TabPanelItem}.
41324 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41326 getActiveTab : function(){
41327 return this.active;
41331 * Updates the tab body element to fit the height of the container element
41332 * for overflow scrolling
41333 * @param {Number} targetHeight (optional) Override the starting height from the elements height
41335 syncHeight : function(targetHeight){
41336 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41337 var bm = this.bodyEl.getMargins();
41338 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41339 this.bodyEl.setHeight(newHeight);
41343 onResize : function(){
41344 if(this.monitorResize){
41345 this.autoSizeTabs();
41350 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41352 beginUpdate : function(){
41353 this.updating = true;
41357 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41359 endUpdate : function(){
41360 this.updating = false;
41361 this.autoSizeTabs();
41365 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41367 autoSizeTabs : function()
41369 var count = this.items.length;
41370 var vcount = count - this.hiddenCount;
41373 this.stripEl.hide();
41375 this.stripEl.show();
41378 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41383 var w = Math.max(this.el.getWidth() - this.cpad, 10);
41384 var availWidth = Math.floor(w / vcount);
41385 var b = this.stripBody;
41386 if(b.getWidth() > w){
41387 var tabs = this.items;
41388 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41389 if(availWidth < this.minTabWidth){
41390 /*if(!this.sleft){ // incomplete scrolling code
41391 this.createScrollButtons();
41394 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41397 if(this.currentTabWidth < this.preferredTabWidth){
41398 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41404 * Returns the number of tabs in this TabPanel.
41407 getCount : function(){
41408 return this.items.length;
41412 * Resizes all the tabs to the passed width
41413 * @param {Number} The new width
41415 setTabWidth : function(width){
41416 this.currentTabWidth = width;
41417 for(var i = 0, len = this.items.length; i < len; i++) {
41418 if(!this.items[i].isHidden()) {
41419 this.items[i].setWidth(width);
41425 * Destroys this TabPanel
41426 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41428 destroy : function(removeEl){
41429 Roo.EventManager.removeResizeListener(this.onResize, this);
41430 for(var i = 0, len = this.items.length; i < len; i++){
41431 this.items[i].purgeListeners();
41433 if(removeEl === true){
41434 this.el.update("");
41439 createStrip : function(container)
41441 var strip = document.createElement("nav");
41442 strip.className = Roo.bootstrap.version == 4 ?
41443 "navbar-light bg-light" :
41444 "navbar navbar-default"; //"x-tabs-wrap";
41445 container.appendChild(strip);
41449 createStripList : function(strip)
41451 // div wrapper for retard IE
41452 // returns the "tr" element.
41453 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41454 //'<div class="x-tabs-strip-wrap">'+
41455 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41456 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41457 return strip.firstChild; //.firstChild.firstChild.firstChild;
41459 createBody : function(container)
41461 var body = document.createElement("div");
41462 Roo.id(body, "tab-body");
41463 //Roo.fly(body).addClass("x-tabs-body");
41464 Roo.fly(body).addClass("tab-content");
41465 container.appendChild(body);
41468 createItemBody :function(bodyEl, id){
41469 var body = Roo.getDom(id);
41471 body = document.createElement("div");
41474 //Roo.fly(body).addClass("x-tabs-item-body");
41475 Roo.fly(body).addClass("tab-pane");
41476 bodyEl.insertBefore(body, bodyEl.firstChild);
41480 createStripElements : function(stripEl, text, closable, tpl)
41482 var td = document.createElement("li"); // was td..
41483 td.className = 'nav-item';
41485 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41488 stripEl.appendChild(td);
41490 td.className = "x-tabs-closable";
41491 if(!this.closeTpl){
41492 this.closeTpl = new Roo.Template(
41493 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41494 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41495 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41498 var el = this.closeTpl.overwrite(td, {"text": text});
41499 var close = el.getElementsByTagName("div")[0];
41500 var inner = el.getElementsByTagName("em")[0];
41501 return {"el": el, "close": close, "inner": inner};
41504 // not sure what this is..
41505 // if(!this.tabTpl){
41506 //this.tabTpl = new Roo.Template(
41507 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41508 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41510 // this.tabTpl = new Roo.Template(
41511 // '<a href="#">' +
41512 // '<span unselectable="on"' +
41513 // (this.disableTooltips ? '' : ' title="{text}"') +
41514 // ' >{text}</span></a>'
41520 var template = tpl || this.tabTpl || false;
41523 template = new Roo.Template(
41524 Roo.bootstrap.version == 4 ?
41526 '<a class="nav-link" href="#" unselectable="on"' +
41527 (this.disableTooltips ? '' : ' title="{text}"') +
41530 '<a class="nav-link" href="#">' +
41531 '<span unselectable="on"' +
41532 (this.disableTooltips ? '' : ' title="{text}"') +
41533 ' >{text}</span></a>'
41538 switch (typeof(template)) {
41542 template = new Roo.Template(template);
41548 var el = template.overwrite(td, {"text": text});
41550 var inner = el.getElementsByTagName("span")[0];
41552 return {"el": el, "inner": inner};
41560 * @class Roo.TabPanelItem
41561 * @extends Roo.util.Observable
41562 * Represents an individual item (tab plus body) in a TabPanel.
41563 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41564 * @param {String} id The id of this TabPanelItem
41565 * @param {String} text The text for the tab of this TabPanelItem
41566 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41568 Roo.bootstrap.panel.TabItem = function(config){
41570 * The {@link Roo.TabPanel} this TabPanelItem belongs to
41571 * @type Roo.TabPanel
41573 this.tabPanel = config.panel;
41575 * The id for this TabPanelItem
41578 this.id = config.id;
41580 this.disabled = false;
41582 this.text = config.text;
41584 this.loaded = false;
41585 this.closable = config.closable;
41588 * The body element for this TabPanelItem.
41589 * @type Roo.Element
41591 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41592 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41593 this.bodyEl.setStyle("display", "block");
41594 this.bodyEl.setStyle("zoom", "1");
41595 //this.hideAction();
41597 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41599 this.el = Roo.get(els.el);
41600 this.inner = Roo.get(els.inner, true);
41601 this.textEl = Roo.bootstrap.version == 4 ?
41602 this.el : Roo.get(this.el.dom.firstChild, true);
41604 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41605 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41608 // this.el.on("mousedown", this.onTabMouseDown, this);
41609 this.el.on("click", this.onTabClick, this);
41611 if(config.closable){
41612 var c = Roo.get(els.close, true);
41613 c.dom.title = this.closeText;
41614 c.addClassOnOver("close-over");
41615 c.on("click", this.closeClick, this);
41621 * Fires when this tab becomes the active tab.
41622 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41623 * @param {Roo.TabPanelItem} this
41627 * @event beforeclose
41628 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41629 * @param {Roo.TabPanelItem} this
41630 * @param {Object} e Set cancel to true on this object to cancel the close.
41632 "beforeclose": true,
41635 * Fires when this tab is closed.
41636 * @param {Roo.TabPanelItem} this
41640 * @event deactivate
41641 * Fires when this tab is no longer the active tab.
41642 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41643 * @param {Roo.TabPanelItem} this
41645 "deactivate" : true
41647 this.hidden = false;
41649 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41652 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41654 purgeListeners : function(){
41655 Roo.util.Observable.prototype.purgeListeners.call(this);
41656 this.el.removeAllListeners();
41659 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41662 this.status_node.addClass("active");
41665 this.tabPanel.stripWrap.repaint();
41667 this.fireEvent("activate", this.tabPanel, this);
41671 * Returns true if this tab is the active tab.
41672 * @return {Boolean}
41674 isActive : function(){
41675 return this.tabPanel.getActiveTab() == this;
41679 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41682 this.status_node.removeClass("active");
41684 this.fireEvent("deactivate", this.tabPanel, this);
41687 hideAction : function(){
41688 this.bodyEl.hide();
41689 this.bodyEl.setStyle("position", "absolute");
41690 this.bodyEl.setLeft("-20000px");
41691 this.bodyEl.setTop("-20000px");
41694 showAction : function(){
41695 this.bodyEl.setStyle("position", "relative");
41696 this.bodyEl.setTop("");
41697 this.bodyEl.setLeft("");
41698 this.bodyEl.show();
41702 * Set the tooltip for the tab.
41703 * @param {String} tooltip The tab's tooltip
41705 setTooltip : function(text){
41706 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41707 this.textEl.dom.qtip = text;
41708 this.textEl.dom.removeAttribute('title');
41710 this.textEl.dom.title = text;
41714 onTabClick : function(e){
41715 e.preventDefault();
41716 this.tabPanel.activate(this.id);
41719 onTabMouseDown : function(e){
41720 e.preventDefault();
41721 this.tabPanel.activate(this.id);
41724 getWidth : function(){
41725 return this.inner.getWidth();
41728 setWidth : function(width){
41729 var iwidth = width - this.linode.getPadding("lr");
41730 this.inner.setWidth(iwidth);
41731 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41732 this.linode.setWidth(width);
41736 * Show or hide the tab
41737 * @param {Boolean} hidden True to hide or false to show.
41739 setHidden : function(hidden){
41740 this.hidden = hidden;
41741 this.linode.setStyle("display", hidden ? "none" : "");
41745 * Returns true if this tab is "hidden"
41746 * @return {Boolean}
41748 isHidden : function(){
41749 return this.hidden;
41753 * Returns the text for this tab
41756 getText : function(){
41760 autoSize : function(){
41761 //this.el.beginMeasure();
41762 this.textEl.setWidth(1);
41764 * #2804 [new] Tabs in Roojs
41765 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41767 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41768 //this.el.endMeasure();
41772 * Sets the text for the tab (Note: this also sets the tooltip text)
41773 * @param {String} text The tab's text and tooltip
41775 setText : function(text){
41777 this.textEl.update(text);
41778 this.setTooltip(text);
41779 //if(!this.tabPanel.resizeTabs){
41780 // this.autoSize();
41784 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41786 activate : function(){
41787 this.tabPanel.activate(this.id);
41791 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41793 disable : function(){
41794 if(this.tabPanel.active != this){
41795 this.disabled = true;
41796 this.status_node.addClass("disabled");
41801 * Enables this TabPanelItem if it was previously disabled.
41803 enable : function(){
41804 this.disabled = false;
41805 this.status_node.removeClass("disabled");
41809 * Sets the content for this TabPanelItem.
41810 * @param {String} content The content
41811 * @param {Boolean} loadScripts true to look for and load scripts
41813 setContent : function(content, loadScripts){
41814 this.bodyEl.update(content, loadScripts);
41818 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41819 * @return {Roo.UpdateManager} The UpdateManager
41821 getUpdateManager : function(){
41822 return this.bodyEl.getUpdateManager();
41826 * Set a URL to be used to load the content for this TabPanelItem.
41827 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41828 * @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)
41829 * @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)
41830 * @return {Roo.UpdateManager} The UpdateManager
41832 setUrl : function(url, params, loadOnce){
41833 if(this.refreshDelegate){
41834 this.un('activate', this.refreshDelegate);
41836 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41837 this.on("activate", this.refreshDelegate);
41838 return this.bodyEl.getUpdateManager();
41842 _handleRefresh : function(url, params, loadOnce){
41843 if(!loadOnce || !this.loaded){
41844 var updater = this.bodyEl.getUpdateManager();
41845 updater.update(url, params, this._setLoaded.createDelegate(this));
41850 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
41851 * Will fail silently if the setUrl method has not been called.
41852 * This does not activate the panel, just updates its content.
41854 refresh : function(){
41855 if(this.refreshDelegate){
41856 this.loaded = false;
41857 this.refreshDelegate();
41862 _setLoaded : function(){
41863 this.loaded = true;
41867 closeClick : function(e){
41870 this.fireEvent("beforeclose", this, o);
41871 if(o.cancel !== true){
41872 this.tabPanel.removeTab(this.id);
41876 * The text displayed in the tooltip for the close icon.
41879 closeText : "Close this tab"
41882 * This script refer to:
41883 * Title: International Telephone Input
41884 * Author: Jack O'Connor
41885 * Code version: v12.1.12
41886 * Availability: https://github.com/jackocnr/intl-tel-input.git
41889 Roo.bootstrap.PhoneInputData = function() {
41892 "Afghanistan (افغانستان)",
41897 "Albania (Shqipëri)",
41902 "Algeria (الجزائر)",
41927 "Antigua and Barbuda",
41937 "Armenia (Հայաստան)",
41953 "Austria (Österreich)",
41958 "Azerbaijan (Azərbaycan)",
41968 "Bahrain (البحرين)",
41973 "Bangladesh (বাংলাদেশ)",
41983 "Belarus (Беларусь)",
41988 "Belgium (België)",
42018 "Bosnia and Herzegovina (Босна и Херцеговина)",
42033 "British Indian Ocean Territory",
42038 "British Virgin Islands",
42048 "Bulgaria (България)",
42058 "Burundi (Uburundi)",
42063 "Cambodia (កម្ពុជា)",
42068 "Cameroon (Cameroun)",
42077 ["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"]
42080 "Cape Verde (Kabu Verdi)",
42085 "Caribbean Netherlands",
42096 "Central African Republic (République centrafricaine)",
42116 "Christmas Island",
42122 "Cocos (Keeling) Islands",
42133 "Comoros (جزر القمر)",
42138 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42143 "Congo (Republic) (Congo-Brazzaville)",
42163 "Croatia (Hrvatska)",
42184 "Czech Republic (Česká republika)",
42189 "Denmark (Danmark)",
42204 "Dominican Republic (República Dominicana)",
42208 ["809", "829", "849"]
42226 "Equatorial Guinea (Guinea Ecuatorial)",
42246 "Falkland Islands (Islas Malvinas)",
42251 "Faroe Islands (Føroyar)",
42272 "French Guiana (Guyane française)",
42277 "French Polynesia (Polynésie française)",
42292 "Georgia (საქართველო)",
42297 "Germany (Deutschland)",
42317 "Greenland (Kalaallit Nunaat)",
42354 "Guinea-Bissau (Guiné Bissau)",
42379 "Hungary (Magyarország)",
42384 "Iceland (Ísland)",
42404 "Iraq (العراق)",
42420 "Israel (ישראל)",
42447 "Jordan (الأردن)",
42452 "Kazakhstan (Казахстан)",
42473 "Kuwait (الكويت)",
42478 "Kyrgyzstan (Кыргызстан)",
42488 "Latvia (Latvija)",
42493 "Lebanon (لبنان)",
42508 "Libya (ليبيا)",
42518 "Lithuania (Lietuva)",
42533 "Macedonia (FYROM) (Македонија)",
42538 "Madagascar (Madagasikara)",
42568 "Marshall Islands",
42578 "Mauritania (موريتانيا)",
42583 "Mauritius (Moris)",
42604 "Moldova (Republica Moldova)",
42614 "Mongolia (Монгол)",
42619 "Montenegro (Crna Gora)",
42629 "Morocco (المغرب)",
42635 "Mozambique (Moçambique)",
42640 "Myanmar (Burma) (မြန်မာ)",
42645 "Namibia (Namibië)",
42660 "Netherlands (Nederland)",
42665 "New Caledonia (Nouvelle-Calédonie)",
42700 "North Korea (조선 민주주의 인민 공화국)",
42705 "Northern Mariana Islands",
42721 "Pakistan (پاکستان)",
42731 "Palestine (فلسطين)",
42741 "Papua New Guinea",
42783 "Réunion (La Réunion)",
42789 "Romania (România)",
42805 "Saint Barthélemy",
42816 "Saint Kitts and Nevis",
42826 "Saint Martin (Saint-Martin (partie française))",
42832 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42837 "Saint Vincent and the Grenadines",
42852 "São Tomé and Príncipe (São Tomé e Príncipe)",
42857 "Saudi Arabia (المملكة العربية السعودية)",
42862 "Senegal (Sénégal)",
42892 "Slovakia (Slovensko)",
42897 "Slovenia (Slovenija)",
42907 "Somalia (Soomaaliya)",
42917 "South Korea (대한민국)",
42922 "South Sudan (جنوب السودان)",
42932 "Sri Lanka (ශ්රී ලංකාව)",
42937 "Sudan (السودان)",
42947 "Svalbard and Jan Mayen",
42958 "Sweden (Sverige)",
42963 "Switzerland (Schweiz)",
42968 "Syria (سوريا)",
43013 "Trinidad and Tobago",
43018 "Tunisia (تونس)",
43023 "Turkey (Türkiye)",
43033 "Turks and Caicos Islands",
43043 "U.S. Virgin Islands",
43053 "Ukraine (Україна)",
43058 "United Arab Emirates (الإمارات العربية المتحدة)",
43080 "Uzbekistan (Oʻzbekiston)",
43090 "Vatican City (Città del Vaticano)",
43101 "Vietnam (Việt Nam)",
43106 "Wallis and Futuna (Wallis-et-Futuna)",
43111 "Western Sahara (الصحراء الغربية)",
43117 "Yemen (اليمن)",
43141 * This script refer to:
43142 * Title: International Telephone Input
43143 * Author: Jack O'Connor
43144 * Code version: v12.1.12
43145 * Availability: https://github.com/jackocnr/intl-tel-input.git
43149 * @class Roo.bootstrap.PhoneInput
43150 * @extends Roo.bootstrap.TriggerField
43151 * An input with International dial-code selection
43153 * @cfg {String} defaultDialCode default '+852'
43154 * @cfg {Array} preferedCountries default []
43157 * Create a new PhoneInput.
43158 * @param {Object} config Configuration options
43161 Roo.bootstrap.PhoneInput = function(config) {
43162 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43165 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43167 listWidth: undefined,
43169 selectedClass: 'active',
43171 invalidClass : "has-warning",
43173 validClass: 'has-success',
43175 allowed: '0123456789',
43180 * @cfg {String} defaultDialCode The default dial code when initializing the input
43182 defaultDialCode: '+852',
43185 * @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
43187 preferedCountries: false,
43189 getAutoCreate : function()
43191 var data = Roo.bootstrap.PhoneInputData();
43192 var align = this.labelAlign || this.parentLabelAlign();
43195 this.allCountries = [];
43196 this.dialCodeMapping = [];
43198 for (var i = 0; i < data.length; i++) {
43200 this.allCountries[i] = {
43204 priority: c[3] || 0,
43205 areaCodes: c[4] || null
43207 this.dialCodeMapping[c[2]] = {
43210 priority: c[3] || 0,
43211 areaCodes: c[4] || null
43223 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43224 maxlength: this.max_length,
43225 cls : 'form-control tel-input',
43226 autocomplete: 'new-password'
43229 var hiddenInput = {
43232 cls: 'hidden-tel-input'
43236 hiddenInput.name = this.name;
43239 if (this.disabled) {
43240 input.disabled = true;
43243 var flag_container = {
43260 cls: this.hasFeedback ? 'has-feedback' : '',
43266 cls: 'dial-code-holder',
43273 cls: 'roo-select2-container input-group',
43280 if (this.fieldLabel.length) {
43283 tooltip: 'This field is required'
43289 cls: 'control-label',
43295 html: this.fieldLabel
43298 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43304 if(this.indicatorpos == 'right') {
43305 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43312 if(align == 'left') {
43320 if(this.labelWidth > 12){
43321 label.style = "width: " + this.labelWidth + 'px';
43323 if(this.labelWidth < 13 && this.labelmd == 0){
43324 this.labelmd = this.labelWidth;
43326 if(this.labellg > 0){
43327 label.cls += ' col-lg-' + this.labellg;
43328 input.cls += ' col-lg-' + (12 - this.labellg);
43330 if(this.labelmd > 0){
43331 label.cls += ' col-md-' + this.labelmd;
43332 container.cls += ' col-md-' + (12 - this.labelmd);
43334 if(this.labelsm > 0){
43335 label.cls += ' col-sm-' + this.labelsm;
43336 container.cls += ' col-sm-' + (12 - this.labelsm);
43338 if(this.labelxs > 0){
43339 label.cls += ' col-xs-' + this.labelxs;
43340 container.cls += ' col-xs-' + (12 - this.labelxs);
43350 var settings = this;
43352 ['xs','sm','md','lg'].map(function(size){
43353 if (settings[size]) {
43354 cfg.cls += ' col-' + size + '-' + settings[size];
43358 this.store = new Roo.data.Store({
43359 proxy : new Roo.data.MemoryProxy({}),
43360 reader : new Roo.data.JsonReader({
43371 'name' : 'dialCode',
43375 'name' : 'priority',
43379 'name' : 'areaCodes',
43386 if(!this.preferedCountries) {
43387 this.preferedCountries = [
43394 var p = this.preferedCountries.reverse();
43397 for (var i = 0; i < p.length; i++) {
43398 for (var j = 0; j < this.allCountries.length; j++) {
43399 if(this.allCountries[j].iso2 == p[i]) {
43400 var t = this.allCountries[j];
43401 this.allCountries.splice(j,1);
43402 this.allCountries.unshift(t);
43408 this.store.proxy.data = {
43410 data: this.allCountries
43416 initEvents : function()
43419 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43421 this.indicator = this.indicatorEl();
43422 this.flag = this.flagEl();
43423 this.dialCodeHolder = this.dialCodeHolderEl();
43425 this.trigger = this.el.select('div.flag-box',true).first();
43426 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43431 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43432 _this.list.setWidth(lw);
43435 this.list.on('mouseover', this.onViewOver, this);
43436 this.list.on('mousemove', this.onViewMove, this);
43437 this.inputEl().on("keyup", this.onKeyUp, this);
43438 this.inputEl().on("keypress", this.onKeyPress, this);
43440 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43442 this.view = new Roo.View(this.list, this.tpl, {
43443 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43446 this.view.on('click', this.onViewClick, this);
43447 this.setValue(this.defaultDialCode);
43450 onTriggerClick : function(e)
43452 Roo.log('trigger click');
43457 if(this.isExpanded()){
43459 this.hasFocus = false;
43461 this.store.load({});
43462 this.hasFocus = true;
43467 isExpanded : function()
43469 return this.list.isVisible();
43472 collapse : function()
43474 if(!this.isExpanded()){
43478 Roo.get(document).un('mousedown', this.collapseIf, this);
43479 Roo.get(document).un('mousewheel', this.collapseIf, this);
43480 this.fireEvent('collapse', this);
43484 expand : function()
43488 if(this.isExpanded() || !this.hasFocus){
43492 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43493 this.list.setWidth(lw);
43496 this.restrictHeight();
43498 Roo.get(document).on('mousedown', this.collapseIf, this);
43499 Roo.get(document).on('mousewheel', this.collapseIf, this);
43501 this.fireEvent('expand', this);
43504 restrictHeight : function()
43506 this.list.alignTo(this.inputEl(), this.listAlign);
43507 this.list.alignTo(this.inputEl(), this.listAlign);
43510 onViewOver : function(e, t)
43512 if(this.inKeyMode){
43515 var item = this.view.findItemFromChild(t);
43518 var index = this.view.indexOf(item);
43519 this.select(index, false);
43524 onViewClick : function(view, doFocus, el, e)
43526 var index = this.view.getSelectedIndexes()[0];
43528 var r = this.store.getAt(index);
43531 this.onSelect(r, index);
43533 if(doFocus !== false && !this.blockFocus){
43534 this.inputEl().focus();
43538 onViewMove : function(e, t)
43540 this.inKeyMode = false;
43543 select : function(index, scrollIntoView)
43545 this.selectedIndex = index;
43546 this.view.select(index);
43547 if(scrollIntoView !== false){
43548 var el = this.view.getNode(index);
43550 this.list.scrollChildIntoView(el, false);
43555 createList : function()
43557 this.list = Roo.get(document.body).createChild({
43559 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43560 style: 'display:none'
43563 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43566 collapseIf : function(e)
43568 var in_combo = e.within(this.el);
43569 var in_list = e.within(this.list);
43570 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43572 if (in_combo || in_list || is_list) {
43578 onSelect : function(record, index)
43580 if(this.fireEvent('beforeselect', this, record, index) !== false){
43582 this.setFlagClass(record.data.iso2);
43583 this.setDialCode(record.data.dialCode);
43584 this.hasFocus = false;
43586 this.fireEvent('select', this, record, index);
43590 flagEl : function()
43592 var flag = this.el.select('div.flag',true).first();
43599 dialCodeHolderEl : function()
43601 var d = this.el.select('input.dial-code-holder',true).first();
43608 setDialCode : function(v)
43610 this.dialCodeHolder.dom.value = '+'+v;
43613 setFlagClass : function(n)
43615 this.flag.dom.className = 'flag '+n;
43618 getValue : function()
43620 var v = this.inputEl().getValue();
43621 if(this.dialCodeHolder) {
43622 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43627 setValue : function(v)
43629 var d = this.getDialCode(v);
43631 //invalid dial code
43632 if(v.length == 0 || !d || d.length == 0) {
43634 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43635 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43641 this.setFlagClass(this.dialCodeMapping[d].iso2);
43642 this.setDialCode(d);
43643 this.inputEl().dom.value = v.replace('+'+d,'');
43644 this.hiddenEl().dom.value = this.getValue();
43649 getDialCode : function(v)
43653 if (v.length == 0) {
43654 return this.dialCodeHolder.dom.value;
43658 if (v.charAt(0) != "+") {
43661 var numericChars = "";
43662 for (var i = 1; i < v.length; i++) {
43663 var c = v.charAt(i);
43666 if (this.dialCodeMapping[numericChars]) {
43667 dialCode = v.substr(1, i);
43669 if (numericChars.length == 4) {
43679 this.setValue(this.defaultDialCode);
43683 hiddenEl : function()
43685 return this.el.select('input.hidden-tel-input',true).first();
43688 // after setting val
43689 onKeyUp : function(e){
43690 this.setValue(this.getValue());
43693 onKeyPress : function(e){
43694 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43701 * @class Roo.bootstrap.MoneyField
43702 * @extends Roo.bootstrap.ComboBox
43703 * Bootstrap MoneyField class
43706 * Create a new MoneyField.
43707 * @param {Object} config Configuration options
43710 Roo.bootstrap.MoneyField = function(config) {
43712 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43716 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43719 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43721 allowDecimals : true,
43723 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43725 decimalSeparator : ".",
43727 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43729 decimalPrecision : 0,
43731 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43733 allowNegative : true,
43735 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43739 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43741 minValue : Number.NEGATIVE_INFINITY,
43743 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43745 maxValue : Number.MAX_VALUE,
43747 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43749 minText : "The minimum value for this field is {0}",
43751 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43753 maxText : "The maximum value for this field is {0}",
43755 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
43756 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43758 nanText : "{0} is not a valid number",
43760 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43764 * @cfg {String} defaults currency of the MoneyField
43765 * value should be in lkey
43767 defaultCurrency : false,
43769 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43771 thousandsDelimiter : false,
43773 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43784 getAutoCreate : function()
43786 var align = this.labelAlign || this.parentLabelAlign();
43798 cls : 'form-control roo-money-amount-input',
43799 autocomplete: 'new-password'
43802 var hiddenInput = {
43806 cls: 'hidden-number-input'
43809 if(this.max_length) {
43810 input.maxlength = this.max_length;
43814 hiddenInput.name = this.name;
43817 if (this.disabled) {
43818 input.disabled = true;
43821 var clg = 12 - this.inputlg;
43822 var cmd = 12 - this.inputmd;
43823 var csm = 12 - this.inputsm;
43824 var cxs = 12 - this.inputxs;
43828 cls : 'row roo-money-field',
43832 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43836 cls: 'roo-select2-container input-group',
43840 cls : 'form-control roo-money-currency-input',
43841 autocomplete: 'new-password',
43843 name : this.currencyName
43847 cls : 'input-group-addon',
43861 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43865 cls: this.hasFeedback ? 'has-feedback' : '',
43876 if (this.fieldLabel.length) {
43879 tooltip: 'This field is required'
43885 cls: 'control-label',
43891 html: this.fieldLabel
43894 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43900 if(this.indicatorpos == 'right') {
43901 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43908 if(align == 'left') {
43916 if(this.labelWidth > 12){
43917 label.style = "width: " + this.labelWidth + 'px';
43919 if(this.labelWidth < 13 && this.labelmd == 0){
43920 this.labelmd = this.labelWidth;
43922 if(this.labellg > 0){
43923 label.cls += ' col-lg-' + this.labellg;
43924 input.cls += ' col-lg-' + (12 - this.labellg);
43926 if(this.labelmd > 0){
43927 label.cls += ' col-md-' + this.labelmd;
43928 container.cls += ' col-md-' + (12 - this.labelmd);
43930 if(this.labelsm > 0){
43931 label.cls += ' col-sm-' + this.labelsm;
43932 container.cls += ' col-sm-' + (12 - this.labelsm);
43934 if(this.labelxs > 0){
43935 label.cls += ' col-xs-' + this.labelxs;
43936 container.cls += ' col-xs-' + (12 - this.labelxs);
43947 var settings = this;
43949 ['xs','sm','md','lg'].map(function(size){
43950 if (settings[size]) {
43951 cfg.cls += ' col-' + size + '-' + settings[size];
43958 initEvents : function()
43960 this.indicator = this.indicatorEl();
43962 this.initCurrencyEvent();
43964 this.initNumberEvent();
43967 initCurrencyEvent : function()
43970 throw "can not find store for combo";
43973 this.store = Roo.factory(this.store, Roo.data);
43974 this.store.parent = this;
43978 this.triggerEl = this.el.select('.input-group-addon', true).first();
43980 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43985 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43986 _this.list.setWidth(lw);
43989 this.list.on('mouseover', this.onViewOver, this);
43990 this.list.on('mousemove', this.onViewMove, this);
43991 this.list.on('scroll', this.onViewScroll, this);
43994 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43997 this.view = new Roo.View(this.list, this.tpl, {
43998 singleSelect:true, store: this.store, selectedClass: this.selectedClass
44001 this.view.on('click', this.onViewClick, this);
44003 this.store.on('beforeload', this.onBeforeLoad, this);
44004 this.store.on('load', this.onLoad, this);
44005 this.store.on('loadexception', this.onLoadException, this);
44007 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44008 "up" : function(e){
44009 this.inKeyMode = true;
44013 "down" : function(e){
44014 if(!this.isExpanded()){
44015 this.onTriggerClick();
44017 this.inKeyMode = true;
44022 "enter" : function(e){
44025 if(this.fireEvent("specialkey", this, e)){
44026 this.onViewClick(false);
44032 "esc" : function(e){
44036 "tab" : function(e){
44039 if(this.fireEvent("specialkey", this, e)){
44040 this.onViewClick(false);
44048 doRelay : function(foo, bar, hname){
44049 if(hname == 'down' || this.scope.isExpanded()){
44050 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44058 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44062 initNumberEvent : function(e)
44064 this.inputEl().on("keydown" , this.fireKey, this);
44065 this.inputEl().on("focus", this.onFocus, this);
44066 this.inputEl().on("blur", this.onBlur, this);
44068 this.inputEl().relayEvent('keyup', this);
44070 if(this.indicator){
44071 this.indicator.addClass('invisible');
44074 this.originalValue = this.getValue();
44076 if(this.validationEvent == 'keyup'){
44077 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44078 this.inputEl().on('keyup', this.filterValidation, this);
44080 else if(this.validationEvent !== false){
44081 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44084 if(this.selectOnFocus){
44085 this.on("focus", this.preFocus, this);
44088 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44089 this.inputEl().on("keypress", this.filterKeys, this);
44091 this.inputEl().relayEvent('keypress', this);
44094 var allowed = "0123456789";
44096 if(this.allowDecimals){
44097 allowed += this.decimalSeparator;
44100 if(this.allowNegative){
44104 if(this.thousandsDelimiter) {
44108 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44110 var keyPress = function(e){
44112 var k = e.getKey();
44114 var c = e.getCharCode();
44117 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44118 allowed.indexOf(String.fromCharCode(c)) === -1
44124 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44128 if(allowed.indexOf(String.fromCharCode(c)) === -1){
44133 this.inputEl().on("keypress", keyPress, this);
44137 onTriggerClick : function(e)
44144 this.loadNext = false;
44146 if(this.isExpanded()){
44151 this.hasFocus = true;
44153 if(this.triggerAction == 'all') {
44154 this.doQuery(this.allQuery, true);
44158 this.doQuery(this.getRawValue());
44161 getCurrency : function()
44163 var v = this.currencyEl().getValue();
44168 restrictHeight : function()
44170 this.list.alignTo(this.currencyEl(), this.listAlign);
44171 this.list.alignTo(this.currencyEl(), this.listAlign);
44174 onViewClick : function(view, doFocus, el, e)
44176 var index = this.view.getSelectedIndexes()[0];
44178 var r = this.store.getAt(index);
44181 this.onSelect(r, index);
44185 onSelect : function(record, index){
44187 if(this.fireEvent('beforeselect', this, record, index) !== false){
44189 this.setFromCurrencyData(index > -1 ? record.data : false);
44193 this.fireEvent('select', this, record, index);
44197 setFromCurrencyData : function(o)
44201 this.lastCurrency = o;
44203 if (this.currencyField) {
44204 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44206 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
44209 this.lastSelectionText = currency;
44211 //setting default currency
44212 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44213 this.setCurrency(this.defaultCurrency);
44217 this.setCurrency(currency);
44220 setFromData : function(o)
44224 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44226 this.setFromCurrencyData(c);
44231 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44233 Roo.log('no value set for '+ (this.name ? this.name : this.id));
44236 this.setValue(value);
44240 setCurrency : function(v)
44242 this.currencyValue = v;
44245 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44250 setValue : function(v)
44252 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44258 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44260 this.inputEl().dom.value = (v == '') ? '' :
44261 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44263 if(!this.allowZero && v === '0') {
44264 this.hiddenEl().dom.value = '';
44265 this.inputEl().dom.value = '';
44272 getRawValue : function()
44274 var v = this.inputEl().getValue();
44279 getValue : function()
44281 return this.fixPrecision(this.parseValue(this.getRawValue()));
44284 parseValue : function(value)
44286 if(this.thousandsDelimiter) {
44288 r = new RegExp(",", "g");
44289 value = value.replace(r, "");
44292 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44293 return isNaN(value) ? '' : value;
44297 fixPrecision : function(value)
44299 if(this.thousandsDelimiter) {
44301 r = new RegExp(",", "g");
44302 value = value.replace(r, "");
44305 var nan = isNaN(value);
44307 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44308 return nan ? '' : value;
44310 return parseFloat(value).toFixed(this.decimalPrecision);
44313 decimalPrecisionFcn : function(v)
44315 return Math.floor(v);
44318 validateValue : function(value)
44320 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44324 var num = this.parseValue(value);
44327 this.markInvalid(String.format(this.nanText, value));
44331 if(num < this.minValue){
44332 this.markInvalid(String.format(this.minText, this.minValue));
44336 if(num > this.maxValue){
44337 this.markInvalid(String.format(this.maxText, this.maxValue));
44344 validate : function()
44346 if(this.disabled || this.allowBlank){
44351 var currency = this.getCurrency();
44353 if(this.validateValue(this.getRawValue()) && currency.length){
44358 this.markInvalid();
44362 getName: function()
44367 beforeBlur : function()
44373 var v = this.parseValue(this.getRawValue());
44380 onBlur : function()
44384 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44385 //this.el.removeClass(this.focusClass);
44388 this.hasFocus = false;
44390 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44394 var v = this.getValue();
44396 if(String(v) !== String(this.startValue)){
44397 this.fireEvent('change', this, v, this.startValue);
44400 this.fireEvent("blur", this);
44403 inputEl : function()
44405 return this.el.select('.roo-money-amount-input', true).first();
44408 currencyEl : function()
44410 return this.el.select('.roo-money-currency-input', true).first();
44413 hiddenEl : function()
44415 return this.el.select('input.hidden-number-input',true).first();
44419 * @class Roo.bootstrap.BezierSignature
44420 * @extends Roo.bootstrap.Component
44421 * Bootstrap BezierSignature class
44422 * This script refer to:
44423 * Title: Signature Pad
44425 * Availability: https://github.com/szimek/signature_pad
44428 * Create a new BezierSignature
44429 * @param {Object} config The config object
44432 Roo.bootstrap.BezierSignature = function(config){
44433 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44439 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44446 mouse_btn_down: true,
44449 * @cfg {int} canvas height
44451 canvas_height: '200px',
44454 * @cfg {float|function} Radius of a single dot.
44459 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44464 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44469 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44474 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44479 * @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.
44481 bg_color: 'rgba(0, 0, 0, 0)',
44484 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44486 dot_color: 'black',
44489 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44491 velocity_filter_weight: 0.7,
44494 * @cfg {function} Callback when stroke begin.
44499 * @cfg {function} Callback when stroke end.
44503 getAutoCreate : function()
44505 var cls = 'roo-signature column';
44508 cls += ' ' + this.cls;
44518 for(var i = 0; i < col_sizes.length; i++) {
44519 if(this[col_sizes[i]]) {
44520 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44530 cls: 'roo-signature-body',
44534 cls: 'roo-signature-body-canvas',
44535 height: this.canvas_height,
44536 width: this.canvas_width
44543 style: 'display: none'
44551 initEvents: function()
44553 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44555 var canvas = this.canvasEl();
44557 // mouse && touch event swapping...
44558 canvas.dom.style.touchAction = 'none';
44559 canvas.dom.style.msTouchAction = 'none';
44561 this.mouse_btn_down = false;
44562 canvas.on('mousedown', this._handleMouseDown, this);
44563 canvas.on('mousemove', this._handleMouseMove, this);
44564 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44566 if (window.PointerEvent) {
44567 canvas.on('pointerdown', this._handleMouseDown, this);
44568 canvas.on('pointermove', this._handleMouseMove, this);
44569 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44572 if ('ontouchstart' in window) {
44573 canvas.on('touchstart', this._handleTouchStart, this);
44574 canvas.on('touchmove', this._handleTouchMove, this);
44575 canvas.on('touchend', this._handleTouchEnd, this);
44578 Roo.EventManager.onWindowResize(this.resize, this, true);
44580 // file input event
44581 this.fileEl().on('change', this.uploadImage, this);
44588 resize: function(){
44590 var canvas = this.canvasEl().dom;
44591 var ctx = this.canvasElCtx();
44592 var img_data = false;
44594 if(canvas.width > 0) {
44595 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44597 // setting canvas width will clean img data
44600 var style = window.getComputedStyle ?
44601 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44603 var padding_left = parseInt(style.paddingLeft) || 0;
44604 var padding_right = parseInt(style.paddingRight) || 0;
44606 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44609 ctx.putImageData(img_data, 0, 0);
44613 _handleMouseDown: function(e)
44615 if (e.browserEvent.which === 1) {
44616 this.mouse_btn_down = true;
44617 this.strokeBegin(e);
44621 _handleMouseMove: function (e)
44623 if (this.mouse_btn_down) {
44624 this.strokeMoveUpdate(e);
44628 _handleMouseUp: function (e)
44630 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44631 this.mouse_btn_down = false;
44636 _handleTouchStart: function (e) {
44638 e.preventDefault();
44639 if (e.browserEvent.targetTouches.length === 1) {
44640 // var touch = e.browserEvent.changedTouches[0];
44641 // this.strokeBegin(touch);
44643 this.strokeBegin(e); // assume e catching the correct xy...
44647 _handleTouchMove: function (e) {
44648 e.preventDefault();
44649 // var touch = event.targetTouches[0];
44650 // _this._strokeMoveUpdate(touch);
44651 this.strokeMoveUpdate(e);
44654 _handleTouchEnd: function (e) {
44655 var wasCanvasTouched = e.target === this.canvasEl().dom;
44656 if (wasCanvasTouched) {
44657 e.preventDefault();
44658 // var touch = event.changedTouches[0];
44659 // _this._strokeEnd(touch);
44664 reset: function () {
44665 this._lastPoints = [];
44666 this._lastVelocity = 0;
44667 this._lastWidth = (this.min_width + this.max_width) / 2;
44668 this.canvasElCtx().fillStyle = this.dot_color;
44671 strokeMoveUpdate: function(e)
44673 this.strokeUpdate(e);
44675 if (this.throttle) {
44676 this.throttleStroke(this.strokeUpdate, this.throttle);
44679 this.strokeUpdate(e);
44683 strokeBegin: function(e)
44685 var newPointGroup = {
44686 color: this.dot_color,
44690 if (typeof this.onBegin === 'function') {
44694 this.curve_data.push(newPointGroup);
44696 this.strokeUpdate(e);
44699 strokeUpdate: function(e)
44701 var rect = this.canvasEl().dom.getBoundingClientRect();
44702 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44703 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44704 var lastPoints = lastPointGroup.points;
44705 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44706 var isLastPointTooClose = lastPoint
44707 ? point.distanceTo(lastPoint) <= this.min_distance
44709 var color = lastPointGroup.color;
44710 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44711 var curve = this.addPoint(point);
44713 this.drawDot({color: color, point: point});
44716 this.drawCurve({color: color, curve: curve});
44726 strokeEnd: function(e)
44728 this.strokeUpdate(e);
44729 if (typeof this.onEnd === 'function') {
44734 addPoint: function (point) {
44735 var _lastPoints = this._lastPoints;
44736 _lastPoints.push(point);
44737 if (_lastPoints.length > 2) {
44738 if (_lastPoints.length === 3) {
44739 _lastPoints.unshift(_lastPoints[0]);
44741 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44742 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44743 _lastPoints.shift();
44749 calculateCurveWidths: function (startPoint, endPoint) {
44750 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44751 (1 - this.velocity_filter_weight) * this._lastVelocity;
44753 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44756 start: this._lastWidth
44759 this._lastVelocity = velocity;
44760 this._lastWidth = newWidth;
44764 drawDot: function (_a) {
44765 var color = _a.color, point = _a.point;
44766 var ctx = this.canvasElCtx();
44767 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44769 this.drawCurveSegment(point.x, point.y, width);
44771 ctx.fillStyle = color;
44775 drawCurve: function (_a) {
44776 var color = _a.color, curve = _a.curve;
44777 var ctx = this.canvasElCtx();
44778 var widthDelta = curve.endWidth - curve.startWidth;
44779 var drawSteps = Math.floor(curve.length()) * 2;
44781 ctx.fillStyle = color;
44782 for (var i = 0; i < drawSteps; i += 1) {
44783 var t = i / drawSteps;
44789 var x = uuu * curve.startPoint.x;
44790 x += 3 * uu * t * curve.control1.x;
44791 x += 3 * u * tt * curve.control2.x;
44792 x += ttt * curve.endPoint.x;
44793 var y = uuu * curve.startPoint.y;
44794 y += 3 * uu * t * curve.control1.y;
44795 y += 3 * u * tt * curve.control2.y;
44796 y += ttt * curve.endPoint.y;
44797 var width = curve.startWidth + ttt * widthDelta;
44798 this.drawCurveSegment(x, y, width);
44804 drawCurveSegment: function (x, y, width) {
44805 var ctx = this.canvasElCtx();
44807 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44808 this.is_empty = false;
44813 var ctx = this.canvasElCtx();
44814 var canvas = this.canvasEl().dom;
44815 ctx.fillStyle = this.bg_color;
44816 ctx.clearRect(0, 0, canvas.width, canvas.height);
44817 ctx.fillRect(0, 0, canvas.width, canvas.height);
44818 this.curve_data = [];
44820 this.is_empty = true;
44825 return this.el.select('input',true).first();
44828 canvasEl: function()
44830 return this.el.select('canvas',true).first();
44833 canvasElCtx: function()
44835 return this.el.select('canvas',true).first().dom.getContext('2d');
44838 getImage: function(type)
44840 if(this.is_empty) {
44845 return this.canvasEl().dom.toDataURL('image/'+type, 1);
44848 drawFromImage: function(img_src)
44850 var img = new Image();
44852 img.onload = function(){
44853 this.canvasElCtx().drawImage(img, 0, 0);
44858 this.is_empty = false;
44861 selectImage: function()
44863 this.fileEl().dom.click();
44866 uploadImage: function(e)
44868 var reader = new FileReader();
44870 reader.onload = function(e){
44871 var img = new Image();
44872 img.onload = function(){
44874 this.canvasElCtx().drawImage(img, 0, 0);
44876 img.src = e.target.result;
44879 reader.readAsDataURL(e.target.files[0]);
44882 // Bezier Point Constructor
44883 Point: (function () {
44884 function Point(x, y, time) {
44887 this.time = time || Date.now();
44889 Point.prototype.distanceTo = function (start) {
44890 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44892 Point.prototype.equals = function (other) {
44893 return this.x === other.x && this.y === other.y && this.time === other.time;
44895 Point.prototype.velocityFrom = function (start) {
44896 return this.time !== start.time
44897 ? this.distanceTo(start) / (this.time - start.time)
44904 // Bezier Constructor
44905 Bezier: (function () {
44906 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44907 this.startPoint = startPoint;
44908 this.control2 = control2;
44909 this.control1 = control1;
44910 this.endPoint = endPoint;
44911 this.startWidth = startWidth;
44912 this.endWidth = endWidth;
44914 Bezier.fromPoints = function (points, widths, scope) {
44915 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44916 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44917 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44919 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44920 var dx1 = s1.x - s2.x;
44921 var dy1 = s1.y - s2.y;
44922 var dx2 = s2.x - s3.x;
44923 var dy2 = s2.y - s3.y;
44924 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44925 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44926 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44927 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44928 var dxm = m1.x - m2.x;
44929 var dym = m1.y - m2.y;
44930 var k = l2 / (l1 + l2);
44931 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44932 var tx = s2.x - cm.x;
44933 var ty = s2.y - cm.y;
44935 c1: new scope.Point(m1.x + tx, m1.y + ty),
44936 c2: new scope.Point(m2.x + tx, m2.y + ty)
44939 Bezier.prototype.length = function () {
44944 for (var i = 0; i <= steps; i += 1) {
44946 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44947 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44949 var xdiff = cx - px;
44950 var ydiff = cy - py;
44951 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44958 Bezier.prototype.point = function (t, start, c1, c2, end) {
44959 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44960 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44961 + (3.0 * c2 * (1.0 - t) * t * t)
44962 + (end * t * t * t);
44967 throttleStroke: function(fn, wait) {
44968 if (wait === void 0) { wait = 250; }
44970 var timeout = null;
44974 var later = function () {
44975 previous = Date.now();
44977 result = fn.apply(storedContext, storedArgs);
44979 storedContext = null;
44983 return function wrapper() {
44985 for (var _i = 0; _i < arguments.length; _i++) {
44986 args[_i] = arguments[_i];
44988 var now = Date.now();
44989 var remaining = wait - (now - previous);
44990 storedContext = this;
44992 if (remaining <= 0 || remaining > wait) {
44994 clearTimeout(timeout);
44998 result = fn.apply(storedContext, storedArgs);
45000 storedContext = null;
45004 else if (!timeout) {
45005 timeout = window.setTimeout(later, remaining);