2 * set the version of bootstrap based on the stylesheet...
6 Roo.bootstrap.version = ( function() {
8 Roo.each(document.styleSheets, function(s) {
9 if ( s.href && s.href.match(/css-bootstrap4/)) {
14 Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
19 * Ext JS Library 1.1.1
20 * Copyright(c) 2006-2007, Ext JS, LLC.
22 * Originally Released Under LGPL - original licence link has changed is not relivant.
25 * <script type="text/javascript">
31 * Simple class that can provide a shadow effect for any element. Note that the element MUST be absolutely positioned,
32 * and the shadow does not provide any shimming. This should be used only in simple cases -- for more advanced
33 * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
36 * @param {Object} config The config object
38 Roo.Shadow = function(config){
39 Roo.apply(this, config);
40 if(typeof this.mode != "string"){
41 this.mode = this.defaultMode;
43 var o = this.offset, a = {h: 0};
44 var rad = Math.floor(this.offset/2);
45 switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
51 a.l -= this.offset + rad;
52 a.t -= this.offset + rad;
63 a.l -= (this.offset - rad);
64 a.t -= this.offset + rad;
66 a.w -= (this.offset - rad)*2;
77 a.l -= (this.offset - rad);
78 a.t -= (this.offset - rad);
80 a.w -= (this.offset + rad + 1);
81 a.h -= (this.offset + rad);
90 Roo.Shadow.prototype = {
93 * The shadow display mode. Supports the following options:<br />
94 * sides: Shadow displays on both sides and bottom only<br />
95 * frame: Shadow displays equally on all four sides<br />
96 * drop: Traditional bottom-right drop shadow (default)
99 * @cfg {String} offset
100 * The number of pixels to offset the shadow from the element (defaults to 4)
108 * Displays the shadow under the target element
109 * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
111 show : function(target){
112 target = Roo.get(target);
114 this.el = Roo.Shadow.Pool.pull();
115 if(this.el.dom.nextSibling != target.dom){
116 this.el.insertBefore(target);
119 this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
121 this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
124 target.getLeft(true),
129 this.el.dom.style.display = "block";
133 * Returns true if the shadow is visible, else false
135 isVisible : function(){
136 return this.el ? true : false;
140 * Direct alignment when values are already available. Show must be called at least once before
141 * calling this method to ensure it is initialized.
142 * @param {Number} left The target element left position
143 * @param {Number} top The target element top position
144 * @param {Number} width The target element width
145 * @param {Number} height The target element height
147 realign : function(l, t, w, h){
151 var a = this.adjusts, d = this.el.dom, s = d.style;
153 s.left = (l+a.l)+"px";
154 s.top = (t+a.t)+"px";
155 var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
157 if(s.width != sws || s.height != shs){
161 var cn = d.childNodes;
162 var sww = Math.max(0, (sw-12))+"px";
163 cn[0].childNodes[1].style.width = sww;
164 cn[1].childNodes[1].style.width = sww;
165 cn[2].childNodes[1].style.width = sww;
166 cn[1].style.height = Math.max(0, (sh-12))+"px";
176 this.el.dom.style.display = "none";
177 Roo.Shadow.Pool.push(this.el);
183 * Adjust the z-index of this shadow
184 * @param {Number} zindex The new z-index
186 setZIndex : function(z){
189 this.el.setStyle("z-index", z);
194 // Private utility class that manages the internal Shadow cache
195 Roo.Shadow.Pool = function(){
197 var markup = Roo.isIE ?
198 '<div class="x-ie-shadow"></div>' :
199 '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
204 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
205 sh.autoBoxAdjust = false;
217 * base class for bootstrap elements.
221 Roo.bootstrap = Roo.bootstrap || {};
223 * @class Roo.bootstrap.Component
224 * @extends Roo.Component
225 * Bootstrap Component base class
226 * @cfg {String} cls css class
227 * @cfg {String} style any extra css
228 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
229 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
230 * @cfg {string} dataId cutomer id
231 * @cfg {string} name Specifies name attribute
232 * @cfg {string} tooltip Text for the tooltip
233 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
234 * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
237 * Do not use directly - it does not do anything..
238 * @param {Object} config The config object
243 Roo.bootstrap.Component = function(config){
244 Roo.bootstrap.Component.superclass.constructor.call(this, config);
248 * @event childrenrendered
249 * Fires when the children have been rendered..
250 * @param {Roo.bootstrap.Component} this
252 "childrenrendered" : true
261 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
264 allowDomMove : false, // to stop relocations in parent onRender...
274 * Initialize Events for the element
276 initEvents : function() { },
282 can_build_overlaid : true,
284 container_method : false,
291 // returns the parent component..
292 return Roo.ComponentMgr.get(this.parentId)
298 onRender : function(ct, position)
300 // Roo.log("Call onRender: " + this.xtype);
302 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
305 if (this.el.attr('xtype')) {
306 this.el.attr('xtypex', this.el.attr('xtype'));
307 this.el.dom.removeAttribute('xtype');
317 var cfg = Roo.apply({}, this.getAutoCreate());
319 cfg.id = this.id || Roo.id();
321 // fill in the extra attributes
322 if (this.xattr && typeof(this.xattr) =='object') {
323 for (var i in this.xattr) {
324 cfg[i] = this.xattr[i];
329 cfg.dataId = this.dataId;
333 cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
336 if (this.style) { // fixme needs to support more complex style data.
337 cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
341 cfg.name = this.name;
344 this.el = ct.createChild(cfg, position);
347 this.tooltipEl().attr('tooltip', this.tooltip);
350 if(this.tabIndex !== undefined){
351 this.el.dom.setAttribute('tabIndex', this.tabIndex);
358 * Fetch the element to add children to
359 * @return {Roo.Element} defaults to this.el
361 getChildContainer : function()
365 getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
367 return Roo.get(document.body);
371 * Fetch the element to display the tooltip on.
372 * @return {Roo.Element} defaults to this.el
374 tooltipEl : function()
379 addxtype : function(tree,cntr)
383 cn = Roo.factory(tree);
384 //Roo.log(['addxtype', cn]);
386 cn.parentType = this.xtype; //??
387 cn.parentId = this.id;
389 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
390 if (typeof(cn.container_method) == 'string') {
391 cntr = cn.container_method;
395 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
397 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
399 var build_from_html = Roo.XComponent.build_from_html;
401 var is_body = (tree.xtype == 'Body') ;
403 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
405 var self_cntr_el = Roo.get(this[cntr](false));
407 // do not try and build conditional elements
408 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
412 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
413 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
414 return this.addxtypeChild(tree,cntr, is_body);
417 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
420 return this.addxtypeChild(Roo.apply({}, tree),cntr);
423 Roo.log('skipping render');
429 if (!build_from_html) {
433 // this i think handles overlaying multiple children of the same type
434 // with the sam eelement.. - which might be buggy..
436 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
442 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
446 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
453 addxtypeChild : function (tree, cntr, is_body)
455 Roo.debug && Roo.log('addxtypeChild:' + cntr);
457 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
460 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
461 (typeof(tree['flexy:foreach']) != 'undefined');
465 skip_children = false;
466 // render the element if it's not BODY.
469 // if parent was disabled, then do not try and create the children..
470 if(!this[cntr](true)){
475 cn = Roo.factory(tree);
477 cn.parentType = this.xtype; //??
478 cn.parentId = this.id;
480 var build_from_html = Roo.XComponent.build_from_html;
483 // does the container contain child eleemnts with 'xtype' attributes.
484 // that match this xtype..
485 // note - when we render we create these as well..
486 // so we should check to see if body has xtype set.
487 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
489 var self_cntr_el = Roo.get(this[cntr](false));
490 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
492 //Roo.log(Roo.XComponent.build_from_html);
493 //Roo.log("got echild:");
496 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
497 // and are not displayed -this causes this to use up the wrong element when matching.
498 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
501 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
502 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
508 //echild.dom.removeAttribute('xtype');
510 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
511 Roo.debug && Roo.log(self_cntr_el);
512 Roo.debug && Roo.log(echild);
513 Roo.debug && Roo.log(cn);
519 // if object has flexy:if - then it may or may not be rendered.
520 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
521 // skip a flexy if element.
522 Roo.debug && Roo.log('skipping render');
523 Roo.debug && Roo.log(tree);
525 Roo.debug && Roo.log('skipping all children');
526 skip_children = true;
531 // actually if flexy:foreach is found, we really want to create
532 // multiple copies here...
534 //Roo.log(this[cntr]());
535 // some elements do not have render methods.. like the layouts...
537 if(this[cntr](true) === false){
542 cn.render && cn.render(this[cntr](true));
545 // then add the element..
552 if (typeof (tree.menu) != 'undefined') {
553 tree.menu.parentType = cn.xtype;
554 tree.menu.triggerEl = cn.el;
555 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
559 if (!tree.items || !tree.items.length) {
561 //Roo.log(["no children", this]);
566 var items = tree.items;
569 //Roo.log(items.length);
571 if (!skip_children) {
572 for(var i =0;i < items.length;i++) {
573 // Roo.log(['add child', items[i]]);
574 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
580 //Roo.log("fire childrenrendered");
582 cn.fireEvent('childrenrendered', this);
588 * Set the element that will be used to show or hide
590 setVisibilityEl : function(el)
592 this.visibilityEl = el;
596 * Get the element that will be used to show or hide
598 getVisibilityEl : function()
600 if (typeof(this.visibilityEl) == 'object') {
601 return this.visibilityEl;
604 if (typeof(this.visibilityEl) == 'string') {
605 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
612 * Show a component - removes 'hidden' class
616 if(!this.getVisibilityEl()){
620 this.getVisibilityEl().removeClass(['hidden','d-none']);
622 this.fireEvent('show', this);
627 * Hide a component - adds 'hidden' class
631 if(!this.getVisibilityEl()){
635 this.getVisibilityEl().addClass(['hidden','d-none']);
637 this.fireEvent('hide', this);
650 * @class Roo.bootstrap.Element
651 * @extends Roo.bootstrap.Component
652 * Bootstrap Element class
653 * @cfg {String} html contents of the element
654 * @cfg {String} tag tag of the element
655 * @cfg {String} cls class of the element
656 * @cfg {Boolean} preventDefault (true|false) default false
657 * @cfg {Boolean} clickable (true|false) default false
658 * @cfg {String} role default blank - set to button to force cursor pointer
662 * Create a new Element
663 * @param {Object} config The config object
666 Roo.bootstrap.Element = function(config){
667 Roo.bootstrap.Element.superclass.constructor.call(this, config);
673 * When a element is chick
674 * @param {Roo.bootstrap.Element} this
675 * @param {Roo.EventObject} e
683 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
688 preventDefault: false,
693 getAutoCreate : function(){
697 // cls: this.cls, double assign in parent class Component.js :: onRender
700 if (this.role !== false) {
701 cfg.role = this.role;
707 initEvents: function()
709 Roo.bootstrap.Element.superclass.initEvents.call(this);
712 this.el.on('click', this.onClick, this);
718 onClick : function(e)
720 if(this.preventDefault){
724 this.fireEvent('click', this, e); // why was this double click before?
732 getValue : function()
734 return this.el.dom.innerHTML;
737 setValue : function(value)
739 this.el.dom.innerHTML = value;
754 * @class Roo.bootstrap.DropTarget
755 * @extends Roo.bootstrap.Element
756 * Bootstrap DropTarget class
758 * @cfg {string} name dropable name
761 * Create a new Dropable Area
762 * @param {Object} config The config object
765 Roo.bootstrap.DropTarget = function(config){
766 Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
772 * When a element is chick
773 * @param {Roo.bootstrap.Element} this
774 * @param {Roo.EventObject} e
780 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element, {
783 getAutoCreate : function(){
788 initEvents: function()
790 Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
791 this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
794 drop : this.dragDrop.createDelegate(this),
795 enter : this.dragEnter.createDelegate(this),
796 out : this.dragOut.createDelegate(this),
797 over : this.dragOver.createDelegate(this)
801 this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
804 dragDrop : function(source,e,data)
806 // user has to decide how to impliment this.
809 //this.fireEvent('drop', this, source, e ,data);
813 dragEnter : function(n, dd, e, data)
815 // probably want to resize the element to match the dropped element..
817 this.originalSize = this.el.getSize();
818 this.el.setSize( n.el.getSize());
819 this.dropZone.DDM.refreshCache(this.name);
820 Roo.log([n, dd, e, data]);
823 dragOut : function(value)
825 // resize back to normal
827 this.el.setSize(this.originalSize);
828 this.dropZone.resetConstraints();
831 dragOver : function()
848 * @class Roo.bootstrap.Body
849 * @extends Roo.bootstrap.Component
850 * Bootstrap Body class
854 * @param {Object} config The config object
857 Roo.bootstrap.Body = function(config){
859 config = config || {};
861 Roo.bootstrap.Body.superclass.constructor.call(this, config);
862 this.el = Roo.get(config.el ? config.el : document.body );
863 if (this.cls && this.cls.length) {
864 Roo.get(document.body).addClass(this.cls);
868 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
870 is_body : true,// just to make sure it's constructed?
875 onRender : function(ct, position)
877 /* Roo.log("Roo.bootstrap.Body - onRender");
878 if (this.cls && this.cls.length) {
879 Roo.get(document.body).addClass(this.cls);
898 * @class Roo.bootstrap.ButtonGroup
899 * @extends Roo.bootstrap.Component
900 * Bootstrap ButtonGroup class
901 * @cfg {String} size lg | sm | xs (default empty normal)
902 * @cfg {String} align vertical | justified (default none)
903 * @cfg {String} direction up | down (default down)
904 * @cfg {Boolean} toolbar false | true
905 * @cfg {Boolean} btn true | false
910 * @param {Object} config The config object
913 Roo.bootstrap.ButtonGroup = function(config){
914 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
917 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
925 getAutoCreate : function(){
931 cfg.html = this.html || cfg.html;
942 if (['vertical','justified'].indexOf(this.align)!==-1) {
943 cfg.cls = 'btn-group-' + this.align;
945 if (this.align == 'justified') {
946 console.log(this.items);
950 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
951 cfg.cls += ' btn-group-' + this.size;
954 if (this.direction == 'up') {
955 cfg.cls += ' dropup' ;
961 * Add a button to the group (similar to NavItem API.)
963 addItem : function(cfg)
965 var cn = new Roo.bootstrap.Button(cfg);
967 cn.parentId = this.id;
968 cn.onRender(this.el, null);
982 * @class Roo.bootstrap.Button
983 * @extends Roo.bootstrap.Component
984 * Bootstrap Button class
985 * @cfg {String} html The button content
986 * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
987 * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
988 * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
989 * @cfg {String} size (lg|sm|xs)
990 * @cfg {String} tag (a|input|submit)
991 * @cfg {String} href empty or href
992 * @cfg {Boolean} disabled default false;
993 * @cfg {Boolean} isClose default false;
994 * @cfg {String} glyphicon depricated - use fa
995 * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
996 * @cfg {String} badge text for badge
997 * @cfg {String} theme (default|glow)
998 * @cfg {Boolean} inverse dark themed version
999 * @cfg {Boolean} toggle is it a slidy toggle button
1000 * @cfg {Boolean} pressed default null - if the button ahs active state
1001 * @cfg {String} ontext text for on slidy toggle state
1002 * @cfg {String} offtext text for off slidy toggle state
1003 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
1004 * @cfg {Boolean} removeClass remove the standard class..
1005 * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href.
1006 * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1009 * Create a new button
1010 * @param {Object} config The config object
1014 Roo.bootstrap.Button = function(config){
1015 Roo.bootstrap.Button.superclass.constructor.call(this, config);
1021 * When a button is pressed
1022 * @param {Roo.bootstrap.Button} btn
1023 * @param {Roo.EventObject} e
1028 * When a button is double clicked
1029 * @param {Roo.bootstrap.Button} btn
1030 * @param {Roo.EventObject} e
1035 * After the button has been toggles
1036 * @param {Roo.bootstrap.Button} btn
1037 * @param {Roo.EventObject} e
1038 * @param {boolean} pressed (also available as button.pressed)
1044 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
1065 preventDefault: true,
1074 getAutoCreate : function(){
1082 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1083 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1084 this.tag = 'button';
1088 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1090 if (this.toggle == true) {
1093 cls: 'slider-frame roo-button',
1097 'data-on-text':'ON',
1098 'data-off-text':'OFF',
1099 cls: 'slider-button',
1104 // why are we validating the weights?
1105 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1106 cfg.cls += ' ' + this.weight;
1113 cfg.cls += ' close';
1115 cfg["aria-hidden"] = true;
1117 cfg.html = "×";
1123 if (this.theme==='default') {
1124 cfg.cls = 'btn roo-button';
1126 //if (this.parentType != 'Navbar') {
1127 this.weight = this.weight.length ? this.weight : 'default';
1129 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1131 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1132 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1133 cfg.cls += ' btn-' + outline + weight;
1134 if (this.weight == 'default') {
1136 cfg.cls += ' btn-' + this.weight;
1139 } else if (this.theme==='glow') {
1142 cfg.cls = 'btn-glow roo-button';
1144 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1146 cfg.cls += ' ' + this.weight;
1152 this.cls += ' inverse';
1156 if (this.active || this.pressed === true) {
1157 cfg.cls += ' active';
1160 if (this.disabled) {
1161 cfg.disabled = 'disabled';
1165 Roo.log('changing to ul' );
1167 this.glyphicon = 'caret';
1168 if (Roo.bootstrap.version == 4) {
1169 this.fa = 'caret-down';
1174 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1176 //gsRoo.log(this.parentType);
1177 if (this.parentType === 'Navbar' && !this.parent().bar) {
1178 Roo.log('changing to li?');
1187 href : this.href || '#'
1190 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
1191 cfg.cls += ' dropdown';
1198 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
1200 if (this.glyphicon) {
1201 cfg.html = ' ' + cfg.html;
1206 cls: 'glyphicon glyphicon-' + this.glyphicon
1211 cfg.html = ' ' + cfg.html;
1216 cls: 'fa fas fa-' + this.fa
1226 // cfg.cls='btn roo-button';
1230 var value = cfg.html;
1235 cls: 'glyphicon glyphicon-' + this.glyphicon,
1242 cls: 'fa fas fa-' + this.fa,
1247 var bw = this.badge_weight.length ? this.badge_weight :
1248 (this.weight.length ? this.weight : 'secondary');
1249 bw = bw == 'default' ? 'secondary' : bw;
1255 cls: 'badge badge-' + bw,
1264 cfg.cls += ' dropdown';
1265 cfg.html = typeof(cfg.html) != 'undefined' ?
1266 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1269 if (cfg.tag !== 'a' && this.href !== '') {
1270 throw "Tag must be a to set href.";
1271 } else if (this.href.length > 0) {
1272 cfg.href = this.href;
1275 if(this.removeClass){
1280 cfg.target = this.target;
1285 initEvents: function() {
1286 // Roo.log('init events?');
1287 // Roo.log(this.el.dom);
1290 if (typeof (this.menu) != 'undefined') {
1291 this.menu.parentType = this.xtype;
1292 this.menu.triggerEl = this.el;
1293 this.addxtype(Roo.apply({}, this.menu));
1297 if (this.el.hasClass('roo-button')) {
1298 this.el.on('click', this.onClick, this);
1299 this.el.on('dblclick', this.onDblClick, this);
1301 this.el.select('.roo-button').on('click', this.onClick, this);
1302 this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1306 if(this.removeClass){
1307 this.el.on('click', this.onClick, this);
1310 if (this.group === true) {
1311 if (this.pressed === false || this.pressed === true) {
1314 this.pressed = false;
1315 this.setActive(this.pressed);
1320 this.el.enableDisplayMode();
1323 onClick : function(e)
1325 if (this.disabled) {
1329 Roo.log('button on click ');
1330 if(this.preventDefault){
1339 this.setActive(true);
1340 var pi = this.parent().items;
1341 for (var i = 0;i < pi.length;i++) {
1342 if (this == pi[i]) {
1345 if (pi[i].el.hasClass('roo-button')) {
1346 pi[i].setActive(false);
1349 this.fireEvent('click', this, e);
1353 if (this.pressed === true || this.pressed === false) {
1354 this.toggleActive(e);
1358 this.fireEvent('click', this, e);
1360 onDblClick: function(e)
1362 if (this.disabled) {
1365 if(this.preventDefault){
1368 this.fireEvent('dblclick', this, e);
1371 * Enables this button
1375 this.disabled = false;
1376 this.el.removeClass('disabled');
1377 this.el.dom.removeAttribute("disabled");
1381 * Disable this button
1383 disable : function()
1385 this.disabled = true;
1386 this.el.addClass('disabled');
1387 this.el.attr("disabled", "disabled")
1390 * sets the active state on/off,
1391 * @param {Boolean} state (optional) Force a particular state
1393 setActive : function(v) {
1395 this.el[v ? 'addClass' : 'removeClass']('active');
1399 * toggles the current active state
1401 toggleActive : function(e)
1403 this.setActive(!this.pressed); // this modifies pressed...
1404 this.fireEvent('toggle', this, e, this.pressed);
1407 * get the current active state
1408 * @return {boolean} true if it's active
1410 isActive : function()
1412 return this.el.hasClass('active');
1415 * set the text of the first selected button
1417 setText : function(str)
1419 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1422 * get the text of the first selected button
1424 getText : function()
1426 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1429 setWeight : function(str)
1431 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1432 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1434 var outline = this.outline ? 'outline-' : '';
1435 if (str == 'default') {
1436 this.el.addClass('btn-default btn-outline-secondary');
1439 this.el.addClass('btn-' + outline + str);
1444 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1446 Roo.bootstrap.Button.weights = [
1466 * @class Roo.bootstrap.Column
1467 * @extends Roo.bootstrap.Component
1468 * Bootstrap Column class
1469 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1470 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1471 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1472 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1473 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1474 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1475 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1476 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1479 * @cfg {Boolean} hidden (true|false) hide the element
1480 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1481 * @cfg {String} fa (ban|check|...) font awesome icon
1482 * @cfg {Number} fasize (1|2|....) font awsome size
1484 * @cfg {String} icon (info-sign|check|...) glyphicon name
1486 * @cfg {String} html content of column.
1489 * Create a new Column
1490 * @param {Object} config The config object
1493 Roo.bootstrap.Column = function(config){
1494 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1497 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1515 getAutoCreate : function(){
1516 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1524 var sizes = ['xs','sm','md','lg'];
1525 sizes.map(function(size ,ix){
1526 //Roo.log( size + ':' + settings[size]);
1528 if (settings[size+'off'] !== false) {
1529 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1532 if (settings[size] === false) {
1536 if (!settings[size]) { // 0 = hidden
1537 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1539 for (var i = ix; i > -1; i--) {
1540 cfg.cls += ' d-' + sizes[i] + '-none';
1546 cfg.cls += ' col-' + size + '-' + settings[size] + (
1547 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1553 cfg.cls += ' hidden';
1556 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1557 cfg.cls +=' alert alert-' + this.alert;
1561 if (this.html.length) {
1562 cfg.html = this.html;
1566 if (this.fasize > 1) {
1567 fasize = ' fa-' + this.fasize + 'x';
1569 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1574 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1593 * @class Roo.bootstrap.Container
1594 * @extends Roo.bootstrap.Component
1595 * Bootstrap Container class
1596 * @cfg {Boolean} jumbotron is it a jumbotron element
1597 * @cfg {String} html content of element
1598 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1599 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1600 * @cfg {String} header content of header (for panel)
1601 * @cfg {String} footer content of footer (for panel)
1602 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1603 * @cfg {String} tag (header|aside|section) type of HTML tag.
1604 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1605 * @cfg {String} fa font awesome icon
1606 * @cfg {String} icon (info-sign|check|...) glyphicon name
1607 * @cfg {Boolean} hidden (true|false) hide the element
1608 * @cfg {Boolean} expandable (true|false) default false
1609 * @cfg {Boolean} expanded (true|false) default true
1610 * @cfg {String} rheader contet on the right of header
1611 * @cfg {Boolean} clickable (true|false) default false
1615 * Create a new Container
1616 * @param {Object} config The config object
1619 Roo.bootstrap.Container = function(config){
1620 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1626 * After the panel has been expand
1628 * @param {Roo.bootstrap.Container} this
1633 * After the panel has been collapsed
1635 * @param {Roo.bootstrap.Container} this
1640 * When a element is chick
1641 * @param {Roo.bootstrap.Container} this
1642 * @param {Roo.EventObject} e
1648 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1666 getChildContainer : function() {
1672 if (this.panel.length) {
1673 return this.el.select('.panel-body',true).first();
1680 getAutoCreate : function(){
1683 tag : this.tag || 'div',
1687 if (this.jumbotron) {
1688 cfg.cls = 'jumbotron';
1693 // - this is applied by the parent..
1695 // cfg.cls = this.cls + '';
1698 if (this.sticky.length) {
1700 var bd = Roo.get(document.body);
1701 if (!bd.hasClass('bootstrap-sticky')) {
1702 bd.addClass('bootstrap-sticky');
1703 Roo.select('html',true).setStyle('height', '100%');
1706 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1710 if (this.well.length) {
1711 switch (this.well) {
1714 cfg.cls +=' well well-' +this.well;
1723 cfg.cls += ' hidden';
1727 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1728 cfg.cls +=' alert alert-' + this.alert;
1733 if (this.panel.length) {
1734 cfg.cls += ' panel panel-' + this.panel;
1736 if (this.header.length) {
1740 if(this.expandable){
1742 cfg.cls = cfg.cls + ' expandable';
1746 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1754 cls : 'panel-title',
1755 html : (this.expandable ? ' ' : '') + this.header
1759 cls: 'panel-header-right',
1765 cls : 'panel-heading',
1766 style : this.expandable ? 'cursor: pointer' : '',
1774 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1779 if (this.footer.length) {
1781 cls : 'panel-footer',
1790 body.html = this.html || cfg.html;
1791 // prefix with the icons..
1793 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1796 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1801 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1802 cfg.cls = 'container';
1808 initEvents: function()
1810 if(this.expandable){
1811 var headerEl = this.headerEl();
1814 headerEl.on('click', this.onToggleClick, this);
1819 this.el.on('click', this.onClick, this);
1824 onToggleClick : function()
1826 var headerEl = this.headerEl();
1842 if(this.fireEvent('expand', this)) {
1844 this.expanded = true;
1846 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1848 this.el.select('.panel-body',true).first().removeClass('hide');
1850 var toggleEl = this.toggleEl();
1856 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1861 collapse : function()
1863 if(this.fireEvent('collapse', this)) {
1865 this.expanded = false;
1867 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1868 this.el.select('.panel-body',true).first().addClass('hide');
1870 var toggleEl = this.toggleEl();
1876 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1880 toggleEl : function()
1882 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1886 return this.el.select('.panel-heading .fa',true).first();
1889 headerEl : function()
1891 if(!this.el || !this.panel.length || !this.header.length){
1895 return this.el.select('.panel-heading',true).first()
1900 if(!this.el || !this.panel.length){
1904 return this.el.select('.panel-body',true).first()
1907 titleEl : function()
1909 if(!this.el || !this.panel.length || !this.header.length){
1913 return this.el.select('.panel-title',true).first();
1916 setTitle : function(v)
1918 var titleEl = this.titleEl();
1924 titleEl.dom.innerHTML = v;
1927 getTitle : function()
1930 var titleEl = this.titleEl();
1936 return titleEl.dom.innerHTML;
1939 setRightTitle : function(v)
1941 var t = this.el.select('.panel-header-right',true).first();
1947 t.dom.innerHTML = v;
1950 onClick : function(e)
1954 this.fireEvent('click', this, e);
1961 * This is BS4's Card element.. - similar to our containers probably..
1965 * @class Roo.bootstrap.Card
1966 * @extends Roo.bootstrap.Component
1967 * Bootstrap Card class
1970 * possible... may not be implemented..
1971 * @cfg {String} header_image src url of image.
1972 * @cfg {String|Object} header
1973 * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1974 * @cfg {Number} header_weight (primary|secondary|success|info|warning|danger|light|dark)
1976 * @cfg {String} title
1977 * @cfg {String} subtitle
1978 * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1979 * @cfg {String} footer
1981 * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1983 * @cfg {String} margin (0|1|2|3|4|5|auto)
1984 * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1985 * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1986 * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1987 * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1988 * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1989 * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1991 * @cfg {String} padding (0|1|2|3|4|5)
1992 * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1993 * @cfg {String} padding_bottom (0|1|2|3|4|5)
1994 * @cfg {String} padding_left (0|1|2|3|4|5)
1995 * @cfg {String} padding_right (0|1|2|3|4|5)
1996 * @cfg {String} padding_x (0|1|2|3|4|5)
1997 * @cfg {String} padding_y (0|1|2|3|4|5)
1999 * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2000 * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2001 * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2002 * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2003 * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2005 * @config {Boolean} dragable if this card can be dragged.
2006 * @config {String} drag_group group for drag
2007 * @config {Boolean} dropable if this card can recieve other cards being dropped onto it..
2008 * @config {String} drop_group group for drag
2010 * @config {Boolean} collapsable can the body be collapsed.
2011 * @config {Boolean} collapsed is the body collapsed when rendered...
2012 * @config {Boolean} rotateable can the body be rotated by clicking on it..
2013 * @config {Boolean} rotated is the body rotated when rendered...
2016 * Create a new Container
2017 * @param {Object} config The config object
2020 Roo.bootstrap.Card = function(config){
2021 Roo.bootstrap.Card.superclass.constructor.call(this, config);
2027 * When a element a card is dropped
2028 * @param {Roo.bootstrap.Card} this
2031 * @param {Roo.bootstrap.Card} move_card the card being dropped?
2032 * @param {String} position 'above' or 'below'
2033 * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2039 * When a element a card is rotate
2040 * @param {Roo.bootstrap.Card} this
2041 * @param {Roo.Element} n the node being dropped?
2042 * @param {Boolean} rotate status
2047 * When a card element is dragged over ready to drop (return false to block dropable)
2048 * @param {Roo.bootstrap.Card} this
2049 * @param {Object} data from dragdrop
2057 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component, {
2062 margin: '', /// may be better in component?
2092 collapsable : false,
2101 childContainer : false,
2102 dropEl : false, /// the dom placeholde element that indicates drop location.
2103 containerEl: false, // body container
2104 bodyEl: false, // card-body
2105 headerContainerEl : false, //
2107 header_imageEl : false,
2110 layoutCls : function()
2114 Roo.log(this.margin_bottom.length);
2115 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2116 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2118 if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2119 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
2121 if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2122 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
2126 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2127 if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2128 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2132 // more generic support?
2140 // Roo.log("Call onRender: " + this.xtype);
2141 /* We are looking at something like this.
2143 <img src="..." class="card-img-top" alt="...">
2144 <div class="card-body">
2145 <h5 class="card-title">Card title</h5>
2146 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2148 >> this bit is really the body...
2149 <div> << we will ad dthis in hopefully it will not break shit.
2151 ** card text does not actually have any styling...
2153 <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2156 <a href="#" class="card-link">Card link</a>
2159 <div class="card-footer">
2160 <small class="text-muted">Last updated 3 mins ago</small>
2164 getAutoCreate : function(){
2172 if (this.weight.length && this.weight != 'light') {
2173 cfg.cls += ' text-white';
2175 cfg.cls += ' text-dark'; // need as it's nested..
2177 if (this.weight.length) {
2178 cfg.cls += ' bg-' + this.weight;
2181 cfg.cls += ' ' + this.layoutCls();
2184 var hdr_ctr = false;
2185 if (this.header.length) {
2187 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2188 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2196 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2202 if (this.collapsable) {
2205 cls : 'd-block user-select-none',
2209 cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2214 hdr.cn.push(hdr_ctr);
2219 cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2224 if (this.header_image.length) {
2227 cls : 'card-img-top',
2228 src: this.header_image // escape?
2233 cls : 'card-img-top d-none'
2239 cls : 'card-body' + (this.html === false ? ' d-none' : ''),
2243 if (this.collapsable || this.rotateable) {
2246 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2253 if (this.title.length) {
2257 src: this.title // escape?
2261 if (this.subtitle.length) {
2265 src: this.subtitle // escape?
2271 cls : 'roo-card-body-ctr'
2274 if (this.html.length) {
2280 // fixme ? handle objects?
2282 if (this.footer.length) {
2285 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2290 cfg.cn.push({cls : 'card-footer d-none'});
2299 getCardHeader : function()
2301 var ret = this.el.select('.card-header',true).first();
2302 if (ret.hasClass('d-none')) {
2303 ret.removeClass('d-none');
2308 getCardFooter : function()
2310 var ret = this.el.select('.card-footer',true).first();
2311 if (ret.hasClass('d-none')) {
2312 ret.removeClass('d-none');
2317 getCardImageTop : function()
2319 var ret = this.header_imageEl;
2320 if (ret.hasClass('d-none')) {
2321 ret.removeClass('d-none');
2327 getChildContainer : function()
2333 return this.el.select('.roo-card-body-ctr',true).first();
2336 initEvents: function()
2338 this.bodyEl = this.el.select('.card-body',true).first();
2339 this.containerEl = this.getChildContainer();
2341 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2342 containerScroll: true,
2343 ddGroup: this.drag_group || 'default_card_drag_group'
2345 this.dragZone.getDragData = this.getDragData.createDelegate(this);
2347 if (this.dropable) {
2348 this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2349 containerScroll: true,
2350 ddGroup: this.drop_group || 'default_card_drag_group'
2352 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2353 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2354 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2355 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2356 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2359 if (this.collapsable) {
2360 this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2362 if (this.rotateable) {
2363 this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2365 this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2367 this.footerEl = this.el.select('.card-footer',true).first();
2368 this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2369 this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2370 this.headerEl = this.el.select('.card-header',true).first();
2373 this.el.addClass('roo-card-rotated');
2374 this.fireEvent('rotate', this, true);
2376 this.header_imageEl = this.el.select('.card-img-top',true).first();
2377 this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2380 getDragData : function(e)
2382 var target = this.getEl();
2384 //this.handleSelection(e);
2389 nodes: this.getEl(),
2394 dragData.ddel = target.dom ; // the div element
2395 Roo.log(target.getWidth( ));
2396 dragData.ddel.style.width = target.getWidth() + 'px';
2403 * Part of the Roo.dd.DropZone interface. If no target node is found, the
2404 * whole Element becomes the target, and this causes the drop gesture to append.
2406 * Returns an object:
2409 position : 'below' or 'above'
2410 card : relateive to card OBJECT (or true for no cards listed)
2411 items_n : relative to nth item in list
2412 card_n : relative to nth card in list
2417 getTargetFromEvent : function(e, dragged_card_el)
2419 var target = e.getTarget();
2420 while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2421 target = target.parentNode;
2432 //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2433 // see if target is one of the 'cards'...
2436 //Roo.log(this.items.length);
2439 var last_card_n = 0;
2441 for (var i = 0;i< this.items.length;i++) {
2443 if (!this.items[i].el.hasClass('card')) {
2446 pos = this.getDropPoint(e, this.items[i].el.dom);
2448 cards_len = ret.cards.length;
2449 //Roo.log(this.items[i].el.dom.id);
2450 ret.cards.push(this.items[i]);
2452 if (ret.card_n < 0 && pos == 'above') {
2453 ret.position = cards_len > 0 ? 'below' : pos;
2454 ret.items_n = i > 0 ? i - 1 : 0;
2455 ret.card_n = cards_len > 0 ? cards_len - 1 : 0;
2456 ret.card = ret.cards[ret.card_n];
2459 if (!ret.cards.length) {
2461 ret.position = 'below';
2465 // could not find a card.. stick it at the end..
2466 if (ret.card_n < 0) {
2467 ret.card_n = last_card_n;
2468 ret.card = ret.cards[last_card_n];
2469 ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2470 ret.position = 'below';
2473 if (this.items[ret.items_n].el == dragged_card_el) {
2477 if (ret.position == 'below') {
2478 var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2480 if (card_after && card_after.el == dragged_card_el) {
2487 var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2489 if (card_before && card_before.el == dragged_card_el) {
2496 onNodeEnter : function(n, dd, e, data){
2499 onNodeOver : function(n, dd, e, data)
2502 var target_info = this.getTargetFromEvent(e,data.source.el);
2503 if (target_info === false) {
2504 this.dropPlaceHolder('hide');
2507 Roo.log(['getTargetFromEvent', target_info ]);
2510 if (this.fireEvent('cardover', this, [ data ]) === false) {
2514 this.dropPlaceHolder('show', target_info,data);
2518 onNodeOut : function(n, dd, e, data){
2519 this.dropPlaceHolder('hide');
2522 onNodeDrop : function(n, dd, e, data)
2525 // call drop - return false if
2527 // this could actually fail - if the Network drops..
2528 // we will ignore this at present..- client should probably reload
2529 // the whole set of cards if stuff like that fails.
2532 var info = this.getTargetFromEvent(e,data.source.el);
2533 if (info === false) {
2536 this.dropPlaceHolder('hide');
2540 this.acceptCard(data.source, info.position, info.card, info.items_n);
2544 firstChildCard : function()
2546 for (var i = 0;i< this.items.length;i++) {
2548 if (!this.items[i].el.hasClass('card')) {
2551 return this.items[i];
2553 return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2558 * - card.acceptCard(move_card, info.position, info.card, info.items_n);
2560 acceptCard : function(move_card, position, next_to_card )
2562 if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2566 var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2568 move_card.parent().removeCard(move_card);
2571 var dom = move_card.el.dom;
2572 dom.style.width = ''; // clear with - which is set by drag.
2574 if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2575 var cardel = next_to_card.el.dom;
2577 if (position == 'above' ) {
2578 cardel.parentNode.insertBefore(dom, cardel);
2579 } else if (cardel.nextSibling) {
2580 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2582 cardel.parentNode.append(dom);
2585 // card container???
2586 this.containerEl.dom.append(dom);
2589 //FIXME HANDLE card = true
2591 // add this to the correct place in items.
2593 // remove Card from items.
2596 if (this.items.length) {
2598 //Roo.log([info.items_n, info.position, this.items.length]);
2599 for (var i =0; i < this.items.length; i++) {
2600 if (i == to_items_n && position == 'above') {
2601 nitems.push(move_card);
2603 nitems.push(this.items[i]);
2604 if (i == to_items_n && position == 'below') {
2605 nitems.push(move_card);
2608 this.items = nitems;
2609 Roo.log(this.items);
2611 this.items.push(move_card);
2614 move_card.parentId = this.id;
2620 removeCard : function(c)
2622 this.items = this.items.filter(function(e) { return e != c });
2625 dom.parentNode.removeChild(dom);
2626 dom.style.width = ''; // clear with - which is set by drag.
2631 /** Decide whether to drop above or below a View node. */
2632 getDropPoint : function(e, n, dd)
2637 if (n == this.containerEl.dom) {
2640 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2641 var c = t + (b - t) / 2;
2642 var y = Roo.lib.Event.getPageY(e);
2649 onToggleCollapse : function(e)
2651 if (this.collapsed) {
2652 this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2653 this.collapsableEl.addClass('show');
2654 this.collapsed = false;
2657 this.el.select('.roo-collapse-toggle').addClass('collapsed');
2658 this.collapsableEl.removeClass('show');
2659 this.collapsed = true;
2664 onToggleRotate : function(e)
2666 this.collapsableEl.removeClass('show');
2667 this.footerEl.removeClass('d-none');
2668 this.el.removeClass('roo-card-rotated');
2669 this.el.removeClass('d-none');
2672 this.collapsableEl.addClass('show');
2673 this.rotated = false;
2674 this.fireEvent('rotate', this, this.rotated);
2677 this.el.addClass('roo-card-rotated');
2678 this.footerEl.addClass('d-none');
2679 this.el.select('.roo-collapsable').removeClass('show');
2681 this.rotated = true;
2682 this.fireEvent('rotate', this, this.rotated);
2686 dropPlaceHolder: function (action, info, data)
2688 if (this.dropEl === false) {
2689 this.dropEl = Roo.DomHelper.append(this.containerEl, {
2693 this.dropEl.removeClass(['d-none', 'd-block']);
2694 if (action == 'hide') {
2696 this.dropEl.addClass('d-none');
2699 // FIXME - info.card == true!!!
2700 this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2702 if (info.card !== true) {
2703 var cardel = info.card.el.dom;
2705 if (info.position == 'above') {
2706 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2707 } else if (cardel.nextSibling) {
2708 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2710 cardel.parentNode.append(this.dropEl.dom);
2713 // card container???
2714 this.containerEl.dom.append(this.dropEl.dom);
2717 this.dropEl.addClass('d-block roo-card-dropzone');
2719 this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2726 setHeaderText: function(html)
2729 if (this.headerContainerEl) {
2730 this.headerContainerEl.dom.innerHTML = html;
2733 onHeaderImageLoad : function(ev, he)
2735 if (!this.header_image_fit_square) {
2739 var hw = he.naturalHeight / he.naturalWidth;
2742 //var w = he.dom.naturalWidth;
2745 he.style.position = 'relative';
2747 var nw = (ww * (1/hw));
2748 Roo.get(he).setSize( ww * (1/hw), ww);
2749 he.style.left = ((ww - nw)/ 2) + 'px';
2750 he.style.position = 'relative';
2761 * Card header - holder for the card header elements.
2766 * @class Roo.bootstrap.CardHeader
2767 * @extends Roo.bootstrap.Element
2768 * Bootstrap CardHeader class
2770 * Create a new Card Header - that you can embed children into
2771 * @param {Object} config The config object
2774 Roo.bootstrap.CardHeader = function(config){
2775 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2778 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2781 container_method : 'getCardHeader'
2794 * Card footer - holder for the card footer elements.
2799 * @class Roo.bootstrap.CardFooter
2800 * @extends Roo.bootstrap.Element
2801 * Bootstrap CardFooter class
2803 * Create a new Card Footer - that you can embed children into
2804 * @param {Object} config The config object
2807 Roo.bootstrap.CardFooter = function(config){
2808 Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2811 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element, {
2814 container_method : 'getCardFooter'
2827 * Card header - holder for the card header elements.
2832 * @class Roo.bootstrap.CardImageTop
2833 * @extends Roo.bootstrap.Element
2834 * Bootstrap CardImageTop class
2836 * Create a new Card Image Top container
2837 * @param {Object} config The config object
2840 Roo.bootstrap.CardImageTop = function(config){
2841 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2844 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2847 container_method : 'getCardImageTop'
2862 * @class Roo.bootstrap.ButtonUploader
2863 * @extends Roo.bootstrap.Button
2864 * Bootstrap Button Uploader class - it's a button which when you add files to it
2867 * @cfg {Number} errorTimeout default 3000
2868 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
2869 * @cfg {Array} html The button text.
2870 * @cfg {Boolean} multiple (default true) Should the upload allow multiple files to be uploaded.
2873 * Create a new CardUploader
2874 * @param {Object} config The config object
2877 Roo.bootstrap.ButtonUploader = function(config){
2881 Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2887 * @event beforeselect
2888 * When button is pressed, before show upload files dialog is shown
2889 * @param {Roo.bootstrap.UploaderButton} this
2892 'beforeselect' : true,
2894 * @event fired when files have been selected,
2895 * When a the download link is clicked
2896 * @param {Roo.bootstrap.UploaderButton} this
2897 * @param {Array} Array of files that have been uploaded
2904 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button, {
2907 errorTimeout : 3000,
2911 fileCollection : false,
2916 getAutoCreate : function()
2921 cls : 'd-none roo-card-upload-selector'
2924 if (this.multiple) {
2925 im.multiple = 'multiple';
2931 Roo.bootstrap.Button.prototype.getAutoCreate.call(this),
2941 initEvents : function()
2944 Roo.bootstrap.Button.prototype.initEvents.call(this);
2950 this.urlAPI = (window.createObjectURL && window) ||
2951 (window.URL && URL.revokeObjectURL && URL) ||
2952 (window.webkitURL && webkitURL);
2957 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2959 this.selectorEl.on('change', this.onFileSelected, this);
2966 onClick : function(e)
2970 if ( this.fireEvent('beforeselect', this) === false) {
2974 this.selectorEl.dom.click();
2978 onFileSelected : function(e)
2982 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
2985 var files = Array.prototype.slice.call(this.selectorEl.dom.files);
2986 this.selectorEl.dom.value = '';// hopefully reset..
2988 this.fireEvent('uploaded', this, files );
2996 * addCard - add an Attachment to the uploader
2997 * @param data - the data about the image to upload
3001 title : "Title of file",
3002 is_uploaded : false,
3003 src : "http://.....",
3004 srcfile : { the File upload object },
3005 mimetype : file.type,
3008 .. any other data...
3033 * @class Roo.bootstrap.Img
3034 * @extends Roo.bootstrap.Component
3035 * Bootstrap Img class
3036 * @cfg {Boolean} imgResponsive false | true
3037 * @cfg {String} border rounded | circle | thumbnail
3038 * @cfg {String} src image source
3039 * @cfg {String} alt image alternative text
3040 * @cfg {String} href a tag href
3041 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3042 * @cfg {String} xsUrl xs image source
3043 * @cfg {String} smUrl sm image source
3044 * @cfg {String} mdUrl md image source
3045 * @cfg {String} lgUrl lg image source
3046 * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3049 * Create a new Input
3050 * @param {Object} config The config object
3053 Roo.bootstrap.Img = function(config){
3054 Roo.bootstrap.Img.superclass.constructor.call(this, config);
3060 * The img click event for the img.
3061 * @param {Roo.EventObject} e
3066 * The when any image loads
3067 * @param {Roo.EventObject} e
3073 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
3075 imgResponsive: true,
3084 backgroundContain : false,
3086 getAutoCreate : function()
3088 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3089 return this.createSingleImg();
3094 cls: 'roo-image-responsive-group',
3099 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3101 if(!_this[size + 'Url']){
3107 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3108 html: _this.html || cfg.html,
3109 src: _this[size + 'Url']
3112 img.cls += ' roo-image-responsive-' + size;
3114 var s = ['xs', 'sm', 'md', 'lg'];
3116 s.splice(s.indexOf(size), 1);
3118 Roo.each(s, function(ss){
3119 img.cls += ' hidden-' + ss;
3122 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3123 cfg.cls += ' img-' + _this.border;
3127 cfg.alt = _this.alt;
3140 a.target = _this.target;
3144 cfg.cn.push((_this.href) ? a : img);
3151 createSingleImg : function()
3155 cls: (this.imgResponsive) ? 'img-responsive' : '',
3157 src : Roo.BLANK_IMAGE_URL // just incase src get's set to undefined?!?
3160 if (this.backgroundContain) {
3161 cfg.cls += ' background-contain';
3164 cfg.html = this.html || cfg.html;
3166 if (this.backgroundContain) {
3167 cfg.style="background-image: url(" + this.src + ')';
3169 cfg.src = this.src || cfg.src;
3172 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3173 cfg.cls += ' img-' + this.border;
3190 a.target = this.target;
3195 return (this.href) ? a : cfg;
3198 initEvents: function()
3201 this.el.on('click', this.onClick, this);
3203 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3204 this.el.on('load', this.onImageLoad, this);
3206 // not sure if this works.. not tested
3207 this.el.select('img', true).on('load', this.onImageLoad, this);
3212 onClick : function(e)
3214 Roo.log('img onclick');
3215 this.fireEvent('click', this, e);
3217 onImageLoad: function(e)
3219 Roo.log('img load');
3220 this.fireEvent('load', this, e);
3224 * Sets the url of the image - used to update it
3225 * @param {String} url the url of the image
3228 setSrc : function(url)
3232 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3233 if (this.backgroundContain) {
3234 this.el.dom.style.backgroundImage = 'url(' + url + ')';
3236 this.el.dom.src = url;
3241 this.el.select('img', true).first().dom.src = url;
3257 * @class Roo.bootstrap.Link
3258 * @extends Roo.bootstrap.Component
3259 * Bootstrap Link Class
3260 * @cfg {String} alt image alternative text
3261 * @cfg {String} href a tag href
3262 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3263 * @cfg {String} html the content of the link.
3264 * @cfg {String} anchor name for the anchor link
3265 * @cfg {String} fa - favicon
3267 * @cfg {Boolean} preventDefault (true | false) default false
3271 * Create a new Input
3272 * @param {Object} config The config object
3275 Roo.bootstrap.Link = function(config){
3276 Roo.bootstrap.Link.superclass.constructor.call(this, config);
3282 * The img click event for the img.
3283 * @param {Roo.EventObject} e
3289 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
3293 preventDefault: false,
3299 getAutoCreate : function()
3301 var html = this.html || '';
3303 if (this.fa !== false) {
3304 html = '<i class="fa fa-' + this.fa + '"></i>';
3309 // anchor's do not require html/href...
3310 if (this.anchor === false) {
3312 cfg.href = this.href || '#';
3314 cfg.name = this.anchor;
3315 if (this.html !== false || this.fa !== false) {
3318 if (this.href !== false) {
3319 cfg.href = this.href;
3323 if(this.alt !== false){
3328 if(this.target !== false) {
3329 cfg.target = this.target;
3335 initEvents: function() {
3337 if(!this.href || this.preventDefault){
3338 this.el.on('click', this.onClick, this);
3342 onClick : function(e)
3344 if(this.preventDefault){
3347 //Roo.log('img onclick');
3348 this.fireEvent('click', this, e);
3361 * @class Roo.bootstrap.Header
3362 * @extends Roo.bootstrap.Component
3363 * Bootstrap Header class
3364 * @cfg {String} html content of header
3365 * @cfg {Number} level (1|2|3|4|5|6) default 1
3368 * Create a new Header
3369 * @param {Object} config The config object
3373 Roo.bootstrap.Header = function(config){
3374 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3377 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3385 getAutoCreate : function(){
3390 tag: 'h' + (1 *this.level),
3391 html: this.html || ''
3403 * Ext JS Library 1.1.1
3404 * Copyright(c) 2006-2007, Ext JS, LLC.
3406 * Originally Released Under LGPL - original licence link has changed is not relivant.
3409 * <script type="text/javascript">
3413 * @class Roo.bootstrap.MenuMgr
3414 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3417 Roo.bootstrap.MenuMgr = function(){
3418 var menus, active, groups = {}, attached = false, lastShow = new Date();
3420 // private - called when first menu is created
3423 active = new Roo.util.MixedCollection();
3424 Roo.get(document).addKeyListener(27, function(){
3425 if(active.length > 0){
3433 if(active && active.length > 0){
3434 var c = active.clone();
3444 if(active.length < 1){
3445 Roo.get(document).un("mouseup", onMouseDown);
3453 var last = active.last();
3454 lastShow = new Date();
3457 Roo.get(document).on("mouseup", onMouseDown);
3462 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3463 m.parentMenu.activeChild = m;
3464 }else if(last && last.isVisible()){
3465 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3470 function onBeforeHide(m){
3472 m.activeChild.hide();
3474 if(m.autoHideTimer){
3475 clearTimeout(m.autoHideTimer);
3476 delete m.autoHideTimer;
3481 function onBeforeShow(m){
3482 var pm = m.parentMenu;
3483 if(!pm && !m.allowOtherMenus){
3485 }else if(pm && pm.activeChild && active != m){
3486 pm.activeChild.hide();
3490 // private this should really trigger on mouseup..
3491 function onMouseDown(e){
3492 Roo.log("on Mouse Up");
3494 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3495 Roo.log("MenuManager hideAll");
3504 function onBeforeCheck(mi, state){
3506 var g = groups[mi.group];
3507 for(var i = 0, l = g.length; i < l; i++){
3509 g[i].setChecked(false);
3518 * Hides all menus that are currently visible
3520 hideAll : function(){
3525 register : function(menu){
3529 menus[menu.id] = menu;
3530 menu.on("beforehide", onBeforeHide);
3531 menu.on("hide", onHide);
3532 menu.on("beforeshow", onBeforeShow);
3533 menu.on("show", onShow);
3535 if(g && menu.events["checkchange"]){
3539 groups[g].push(menu);
3540 menu.on("checkchange", onCheck);
3545 * Returns a {@link Roo.menu.Menu} object
3546 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3547 * be used to generate and return a new Menu instance.
3549 get : function(menu){
3550 if(typeof menu == "string"){ // menu id
3552 }else if(menu.events){ // menu instance
3555 /*else if(typeof menu.length == 'number'){ // array of menu items?
3556 return new Roo.bootstrap.Menu({items:menu});
3557 }else{ // otherwise, must be a config
3558 return new Roo.bootstrap.Menu(menu);
3565 unregister : function(menu){
3566 delete menus[menu.id];
3567 menu.un("beforehide", onBeforeHide);
3568 menu.un("hide", onHide);
3569 menu.un("beforeshow", onBeforeShow);
3570 menu.un("show", onShow);
3572 if(g && menu.events["checkchange"]){
3573 groups[g].remove(menu);
3574 menu.un("checkchange", onCheck);
3579 registerCheckable : function(menuItem){
3580 var g = menuItem.group;
3585 groups[g].push(menuItem);
3586 menuItem.on("beforecheckchange", onBeforeCheck);
3591 unregisterCheckable : function(menuItem){
3592 var g = menuItem.group;
3594 groups[g].remove(menuItem);
3595 menuItem.un("beforecheckchange", onBeforeCheck);
3607 * @class Roo.bootstrap.Menu
3608 * @extends Roo.bootstrap.Component
3609 * Bootstrap Menu class - container for MenuItems
3610 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3611 * @cfg {bool} hidden if the menu should be hidden when rendered.
3612 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3613 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3614 * @cfg {bool} hideTrigger (true|false) default false - hide the carret for trigger.
3615 * @cfg {String} align default tl-bl? == below - how the menu should be aligned.
3619 * @param {Object} config The config object
3623 Roo.bootstrap.Menu = function(config){
3625 if (config.type == 'treeview') {
3626 // normally menu's are drawn attached to the document to handle layering etc..
3627 // however treeview (used by the docs menu is drawn into the parent element)
3628 this.container_method = 'getChildContainer';
3631 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3632 if (this.registerMenu && this.type != 'treeview') {
3633 Roo.bootstrap.MenuMgr.register(this);
3640 * Fires before this menu is displayed (return false to block)
3641 * @param {Roo.menu.Menu} this
3646 * Fires before this menu is hidden (return false to block)
3647 * @param {Roo.menu.Menu} this
3652 * Fires after this menu is displayed
3653 * @param {Roo.menu.Menu} this
3658 * Fires after this menu is hidden
3659 * @param {Roo.menu.Menu} this
3664 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3665 * @param {Roo.menu.Menu} this
3666 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3667 * @param {Roo.EventObject} e
3672 * Fires when the mouse is hovering over this menu
3673 * @param {Roo.menu.Menu} this
3674 * @param {Roo.EventObject} e
3675 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3680 * Fires when the mouse exits this menu
3681 * @param {Roo.menu.Menu} this
3682 * @param {Roo.EventObject} e
3683 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3688 * Fires when a menu item contained in this menu is clicked
3689 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3690 * @param {Roo.EventObject} e
3694 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3697 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
3701 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3704 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3706 registerMenu : true,
3708 menuItems :false, // stores the menu items..
3718 container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3720 hideTrigger : false,
3725 getChildContainer : function() {
3729 getAutoCreate : function(){
3731 //if (['right'].indexOf(this.align)!==-1) {
3732 // cfg.cn[1].cls += ' pull-right'
3737 cls : 'dropdown-menu shadow' ,
3738 style : 'z-index:1000'
3742 if (this.type === 'submenu') {
3743 cfg.cls = 'submenu active';
3745 if (this.type === 'treeview') {
3746 cfg.cls = 'treeview-menu';
3751 initEvents : function() {
3753 // Roo.log("ADD event");
3754 // Roo.log(this.triggerEl.dom);
3755 if (this.triggerEl) {
3757 this.triggerEl.on('click', this.onTriggerClick, this);
3759 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3761 if (!this.hideTrigger) {
3762 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3763 // dropdown toggle on the 'a' in BS4?
3764 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3766 this.triggerEl.addClass('dropdown-toggle');
3772 this.el.on('touchstart' , this.onTouch, this);
3774 this.el.on('click' , this.onClick, this);
3776 this.el.on("mouseover", this.onMouseOver, this);
3777 this.el.on("mouseout", this.onMouseOut, this);
3781 findTargetItem : function(e)
3783 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3787 //Roo.log(t); Roo.log(t.id);
3789 //Roo.log(this.menuitems);
3790 return this.menuitems.get(t.id);
3792 //return this.items.get(t.menuItemId);
3798 onTouch : function(e)
3800 Roo.log("menu.onTouch");
3801 //e.stopEvent(); this make the user popdown broken
3805 onClick : function(e)
3807 Roo.log("menu.onClick");
3809 var t = this.findTargetItem(e);
3810 if(!t || t.isContainer){
3815 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3816 if(t == this.activeItem && t.shouldDeactivate(e)){
3817 this.activeItem.deactivate();
3818 delete this.activeItem;
3822 this.setActiveItem(t, true);
3830 Roo.log('pass click event');
3834 this.fireEvent("click", this, t, e);
3838 if(!t.href.length || t.href == '#'){
3839 (function() { _this.hide(); }).defer(100);
3844 onMouseOver : function(e){
3845 var t = this.findTargetItem(e);
3848 // if(t.canActivate && !t.disabled){
3849 // this.setActiveItem(t, true);
3853 this.fireEvent("mouseover", this, e, t);
3855 isVisible : function(){
3856 return !this.hidden;
3858 onMouseOut : function(e){
3859 var t = this.findTargetItem(e);
3862 // if(t == this.activeItem && t.shouldDeactivate(e)){
3863 // this.activeItem.deactivate();
3864 // delete this.activeItem;
3867 this.fireEvent("mouseout", this, e, t);
3872 * Displays this menu relative to another element
3873 * @param {String/HTMLElement/Roo.Element} element The element to align to
3874 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3875 * the element (defaults to this.defaultAlign)
3876 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3878 show : function(el, pos, parentMenu)
3880 if (false === this.fireEvent("beforeshow", this)) {
3881 Roo.log("show canceled");
3884 this.parentMenu = parentMenu;
3888 this.el.addClass('show'); // show otherwise we do not know how big we are..
3890 var xy = this.el.getAlignToXY(el, pos);
3892 // bl-tl << left align below
3893 // tl-bl << left align
3895 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3896 // if it goes to far to the right.. -> align left.
3897 xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3900 // was left align - go right?
3901 xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3904 // goes down the bottom
3905 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3907 var a = this.align.replace('?', '').split('-');
3908 xy = this.el.getAlignToXY(el, a[1] + '-' + a[0] + '?')
3912 this.showAt( xy , parentMenu, false);
3915 * Displays this menu at a specific xy position
3916 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3917 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3919 showAt : function(xy, parentMenu, /* private: */_e){
3920 this.parentMenu = parentMenu;
3925 this.fireEvent("beforeshow", this);
3926 //xy = this.el.adjustForConstraints(xy);
3930 this.hideMenuItems();
3931 this.hidden = false;
3932 if (this.triggerEl) {
3933 this.triggerEl.addClass('open');
3936 this.el.addClass('show');
3940 // reassign x when hitting right
3942 // reassign y when hitting bottom
3944 // but the list may align on trigger left or trigger top... should it be a properity?
3946 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3951 this.fireEvent("show", this);
3957 this.doFocus.defer(50, this);
3961 doFocus : function(){
3963 this.focusEl.focus();
3968 * Hides this menu and optionally all parent menus
3969 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3971 hide : function(deep)
3973 if (false === this.fireEvent("beforehide", this)) {
3974 Roo.log("hide canceled");
3977 this.hideMenuItems();
3978 if(this.el && this.isVisible()){
3980 if(this.activeItem){
3981 this.activeItem.deactivate();
3982 this.activeItem = null;
3984 if (this.triggerEl) {
3985 this.triggerEl.removeClass('open');
3988 this.el.removeClass('show');
3990 this.fireEvent("hide", this);
3992 if(deep === true && this.parentMenu){
3993 this.parentMenu.hide(true);
3997 onTriggerClick : function(e)
3999 Roo.log('trigger click');
4001 var target = e.getTarget();
4003 Roo.log(target.nodeName.toLowerCase());
4005 if(target.nodeName.toLowerCase() === 'i'){
4011 onTriggerPress : function(e)
4013 Roo.log('trigger press');
4014 //Roo.log(e.getTarget());
4015 // Roo.log(this.triggerEl.dom);
4017 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4018 var pel = Roo.get(e.getTarget());
4019 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4020 Roo.log('is treeview or dropdown?');
4024 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4028 if (this.isVisible()) {
4034 this.show(this.triggerEl, this.align, false);
4037 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4044 hideMenuItems : function()
4046 Roo.log("hide Menu Items");
4051 this.el.select('.open',true).each(function(aa) {
4053 aa.removeClass('open');
4057 addxtypeChild : function (tree, cntr) {
4058 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4060 this.menuitems.add(comp);
4072 this.getEl().dom.innerHTML = '';
4073 this.menuitems.clear();
4087 * @class Roo.bootstrap.MenuItem
4088 * @extends Roo.bootstrap.Component
4089 * Bootstrap MenuItem class
4090 * @cfg {String} html the menu label
4091 * @cfg {String} href the link
4092 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4093 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4094 * @cfg {Boolean} active used on sidebars to highlight active itesm
4095 * @cfg {String} fa favicon to show on left of menu item.
4096 * @cfg {Roo.bootsrap.Menu} menu the child menu.
4100 * Create a new MenuItem
4101 * @param {Object} config The config object
4105 Roo.bootstrap.MenuItem = function(config){
4106 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4111 * The raw click event for the entire grid.
4112 * @param {Roo.bootstrap.MenuItem} this
4113 * @param {Roo.EventObject} e
4119 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
4123 preventDefault: false,
4124 isContainer : false,
4128 getAutoCreate : function(){
4130 if(this.isContainer){
4133 cls: 'dropdown-menu-item '
4143 cls : 'dropdown-item',
4148 if (this.fa !== false) {
4151 cls : 'fa fa-' + this.fa
4160 cls: 'dropdown-menu-item',
4163 if (this.parent().type == 'treeview') {
4164 cfg.cls = 'treeview-menu';
4167 cfg.cls += ' active';
4172 anc.href = this.href || cfg.cn[0].href ;
4173 ctag.html = this.html || cfg.cn[0].html ;
4177 initEvents: function()
4179 if (this.parent().type == 'treeview') {
4180 this.el.select('a').on('click', this.onClick, this);
4184 this.menu.parentType = this.xtype;
4185 this.menu.triggerEl = this.el;
4186 this.menu = this.addxtype(Roo.apply({}, this.menu));
4190 onClick : function(e)
4192 Roo.log('item on click ');
4194 if(this.preventDefault){
4197 //this.parent().hideMenuItems();
4199 this.fireEvent('click', this, e);
4218 * @class Roo.bootstrap.MenuSeparator
4219 * @extends Roo.bootstrap.Component
4220 * Bootstrap MenuSeparator class
4223 * Create a new MenuItem
4224 * @param {Object} config The config object
4228 Roo.bootstrap.MenuSeparator = function(config){
4229 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4232 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
4234 getAutoCreate : function(){
4253 * @class Roo.bootstrap.Modal
4254 * @extends Roo.bootstrap.Component
4255 * Bootstrap Modal class
4256 * @cfg {String} title Title of dialog
4257 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4258 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
4259 * @cfg {Boolean} specificTitle default false
4260 * @cfg {Array} buttons Array of buttons or standard button set..
4261 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4262 * @cfg {Boolean} animate default true
4263 * @cfg {Boolean} allow_close default true
4264 * @cfg {Boolean} fitwindow default false
4265 * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4266 * @cfg {Number} width fixed width - usefull for chrome extension only really.
4267 * @cfg {Number} height fixed height - usefull for chrome extension only really.
4268 * @cfg {String} size (sm|lg|xl) default empty
4269 * @cfg {Number} max_width set the max width of modal
4270 * @cfg {Boolean} editableTitle can the title be edited
4275 * Create a new Modal Dialog
4276 * @param {Object} config The config object
4279 Roo.bootstrap.Modal = function(config){
4280 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4285 * The raw btnclick event for the button
4286 * @param {Roo.EventObject} e
4291 * Fire when dialog resize
4292 * @param {Roo.bootstrap.Modal} this
4293 * @param {Roo.EventObject} e
4297 * @event titlechanged
4298 * Fire when the editable title has been changed
4299 * @param {Roo.bootstrap.Modal} this
4300 * @param {Roo.EventObject} value
4302 "titlechanged" : true
4305 this.buttons = this.buttons || [];
4308 this.tmpl = Roo.factory(this.tmpl);
4313 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
4315 title : 'test dialog',
4325 specificTitle: false,
4327 buttonPosition: 'right',
4349 editableTitle : false,
4351 onRender : function(ct, position)
4353 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4356 var cfg = Roo.apply({}, this.getAutoCreate());
4359 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4361 //if (!cfg.name.length) {
4365 cfg.cls += ' ' + this.cls;
4368 cfg.style = this.style;
4370 this.el = Roo.get(document.body).createChild(cfg, position);
4372 //var type = this.el.dom.type;
4375 if(this.tabIndex !== undefined){
4376 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4379 this.dialogEl = this.el.select('.modal-dialog',true).first();
4380 this.bodyEl = this.el.select('.modal-body',true).first();
4381 this.closeEl = this.el.select('.modal-header .close', true).first();
4382 this.headerEl = this.el.select('.modal-header',true).first();
4383 this.titleEl = this.el.select('.modal-title',true).first();
4384 this.footerEl = this.el.select('.modal-footer',true).first();
4386 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4388 //this.el.addClass("x-dlg-modal");
4390 if (this.buttons.length) {
4391 Roo.each(this.buttons, function(bb) {
4392 var b = Roo.apply({}, bb);
4393 b.xns = b.xns || Roo.bootstrap;
4394 b.xtype = b.xtype || 'Button';
4395 if (typeof(b.listeners) == 'undefined') {
4396 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4399 var btn = Roo.factory(b);
4401 btn.render(this.getButtonContainer());
4405 // render the children.
4408 if(typeof(this.items) != 'undefined'){
4409 var items = this.items;
4412 for(var i =0;i < items.length;i++) {
4413 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4417 this.items = nitems;
4419 // where are these used - they used to be body/close/footer
4423 //this.el.addClass([this.fieldClass, this.cls]);
4427 getAutoCreate : function()
4429 // we will default to modal-body-overflow - might need to remove or make optional later.
4431 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''),
4432 html : this.html || ''
4437 cls : 'modal-title',
4441 if(this.specificTitle){ // WTF is this?
4446 if (this.allow_close && Roo.bootstrap.version == 3) {
4456 if (this.editableTitle) {
4458 cls: 'form-control roo-editable-title d-none',
4464 if (this.allow_close && Roo.bootstrap.version == 4) {
4474 if(this.size.length){
4475 size = 'modal-' + this.size;
4478 var footer = Roo.bootstrap.version == 3 ?
4480 cls : 'modal-footer',
4484 cls: 'btn-' + this.buttonPosition
4489 { // BS4 uses mr-auto on left buttons....
4490 cls : 'modal-footer'
4501 cls: "modal-dialog " + size,
4504 cls : "modal-content",
4507 cls : 'modal-header',
4522 modal.cls += ' fade';
4528 getChildContainer : function() {
4533 getButtonContainer : function() {
4535 return Roo.bootstrap.version == 4 ?
4536 this.el.select('.modal-footer',true).first()
4537 : this.el.select('.modal-footer div',true).first();
4540 initEvents : function()
4542 if (this.allow_close) {
4543 this.closeEl.on('click', this.hide, this);
4545 Roo.EventManager.onWindowResize(this.resize, this, true);
4546 if (this.editableTitle) {
4547 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4548 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4549 this.headerEditEl.on('keyup', function(e) {
4550 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4551 this.toggleHeaderInput(false)
4554 this.headerEditEl.on('blur', function(e) {
4555 this.toggleHeaderInput(false)
4564 this.maskEl.setSize(
4565 Roo.lib.Dom.getViewWidth(true),
4566 Roo.lib.Dom.getViewHeight(true)
4569 if (this.fitwindow) {
4571 this.dialogEl.setStyle( { 'max-width' : '100%' });
4573 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4574 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4579 if(this.max_width !== 0) {
4581 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4584 this.setSize(w, this.height);
4588 if(this.max_height) {
4589 this.setSize(w,Math.min(
4591 Roo.lib.Dom.getViewportHeight(true) - 60
4597 if(!this.fit_content) {
4598 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4602 this.setSize(w, Math.min(
4604 this.headerEl.getHeight() +
4605 this.footerEl.getHeight() +
4606 this.getChildHeight(this.bodyEl.dom.childNodes),
4607 Roo.lib.Dom.getViewportHeight(true) - 60)
4613 setSize : function(w,h)
4624 if (!this.rendered) {
4627 this.toggleHeaderInput(false);
4628 //this.el.setStyle('display', 'block');
4629 this.el.removeClass('hideing');
4630 this.el.dom.style.display='block';
4632 Roo.get(document.body).addClass('modal-open');
4634 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4637 this.el.addClass('show');
4638 this.el.addClass('in');
4641 this.el.addClass('show');
4642 this.el.addClass('in');
4645 // not sure how we can show data in here..
4647 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4650 Roo.get(document.body).addClass("x-body-masked");
4652 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4653 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4654 this.maskEl.dom.style.display = 'block';
4655 this.maskEl.addClass('show');
4660 this.fireEvent('show', this);
4662 // set zindex here - otherwise it appears to be ignored...
4663 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4666 this.items.forEach( function(e) {
4667 e.layout ? e.layout() : false;
4675 if(this.fireEvent("beforehide", this) !== false){
4677 this.maskEl.removeClass('show');
4679 this.maskEl.dom.style.display = '';
4680 Roo.get(document.body).removeClass("x-body-masked");
4681 this.el.removeClass('in');
4682 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4684 if(this.animate){ // why
4685 this.el.addClass('hideing');
4686 this.el.removeClass('show');
4688 if (!this.el.hasClass('hideing')) {
4689 return; // it's been shown again...
4692 this.el.dom.style.display='';
4694 Roo.get(document.body).removeClass('modal-open');
4695 this.el.removeClass('hideing');
4699 this.el.removeClass('show');
4700 this.el.dom.style.display='';
4701 Roo.get(document.body).removeClass('modal-open');
4704 this.fireEvent('hide', this);
4707 isVisible : function()
4710 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4714 addButton : function(str, cb)
4718 var b = Roo.apply({}, { html : str } );
4719 b.xns = b.xns || Roo.bootstrap;
4720 b.xtype = b.xtype || 'Button';
4721 if (typeof(b.listeners) == 'undefined') {
4722 b.listeners = { click : cb.createDelegate(this) };
4725 var btn = Roo.factory(b);
4727 btn.render(this.getButtonContainer());
4733 setDefaultButton : function(btn)
4735 //this.el.select('.modal-footer').()
4738 resizeTo: function(w,h)
4740 this.dialogEl.setWidth(w);
4742 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4744 this.bodyEl.setHeight(h - diff);
4746 this.fireEvent('resize', this);
4749 setContentSize : function(w, h)
4753 onButtonClick: function(btn,e)
4756 this.fireEvent('btnclick', btn.name, e);
4759 * Set the title of the Dialog
4760 * @param {String} str new Title
4762 setTitle: function(str) {
4763 this.titleEl.dom.innerHTML = str;
4767 * Set the body of the Dialog
4768 * @param {String} str new Title
4770 setBody: function(str) {
4771 this.bodyEl.dom.innerHTML = str;
4774 * Set the body of the Dialog using the template
4775 * @param {Obj} data - apply this data to the template and replace the body contents.
4777 applyBody: function(obj)
4780 Roo.log("Error - using apply Body without a template");
4783 this.tmpl.overwrite(this.bodyEl, obj);
4786 getChildHeight : function(child_nodes)
4790 child_nodes.length == 0
4795 var child_height = 0;
4797 for(var i = 0; i < child_nodes.length; i++) {
4800 * for modal with tabs...
4801 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4803 var layout_childs = child_nodes[i].childNodes;
4805 for(var j = 0; j < layout_childs.length; j++) {
4807 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4809 var layout_body_childs = layout_childs[j].childNodes;
4811 for(var k = 0; k < layout_body_childs.length; k++) {
4813 if(layout_body_childs[k].classList.contains('navbar')) {
4814 child_height += layout_body_childs[k].offsetHeight;
4818 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4820 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4822 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4824 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4825 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4840 child_height += child_nodes[i].offsetHeight;
4841 // Roo.log(child_nodes[i].offsetHeight);
4844 return child_height;
4846 toggleHeaderInput : function(is_edit)
4848 if (!this.editableTitle) {
4849 return; // not editable.
4851 if (is_edit && this.is_header_editing) {
4852 return; // already editing..
4856 this.headerEditEl.dom.value = this.title;
4857 this.headerEditEl.removeClass('d-none');
4858 this.headerEditEl.dom.focus();
4859 this.titleEl.addClass('d-none');
4861 this.is_header_editing = true;
4864 // flip back to not editing.
4865 this.title = this.headerEditEl.dom.value;
4866 this.headerEditEl.addClass('d-none');
4867 this.titleEl.removeClass('d-none');
4868 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4869 this.is_header_editing = false;
4870 this.fireEvent('titlechanged', this, this.title);
4879 Roo.apply(Roo.bootstrap.Modal, {
4881 * Button config that displays a single OK button
4890 * Button config that displays Yes and No buttons
4906 * Button config that displays OK and Cancel buttons
4921 * Button config that displays Yes, No and Cancel buttons
4946 * messagebox - can be used as a replace
4950 * @class Roo.MessageBox
4951 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4955 Roo.Msg.alert('Status', 'Changes saved successfully.');
4957 // Prompt for user data:
4958 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4960 // process text value...
4964 // Show a dialog using config options:
4966 title:'Save Changes?',
4967 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4968 buttons: Roo.Msg.YESNOCANCEL,
4975 Roo.bootstrap.MessageBox = function(){
4976 var dlg, opt, mask, waitTimer;
4977 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4978 var buttons, activeTextEl, bwidth;
4982 var handleButton = function(button){
4984 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4988 var handleHide = function(){
4990 dlg.el.removeClass(opt.cls);
4993 // Roo.TaskMgr.stop(waitTimer);
4994 // waitTimer = null;
4999 var updateButtons = function(b){
5002 buttons["ok"].hide();
5003 buttons["cancel"].hide();
5004 buttons["yes"].hide();
5005 buttons["no"].hide();
5006 dlg.footerEl.hide();
5010 dlg.footerEl.show();
5011 for(var k in buttons){
5012 if(typeof buttons[k] != "function"){
5015 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5016 width += buttons[k].el.getWidth()+15;
5026 var handleEsc = function(d, k, e){
5027 if(opt && opt.closable !== false){
5037 * Returns a reference to the underlying {@link Roo.BasicDialog} element
5038 * @return {Roo.BasicDialog} The BasicDialog element
5040 getDialog : function(){
5042 dlg = new Roo.bootstrap.Modal( {
5045 //constraintoviewport:false,
5047 //collapsible : false,
5052 //buttonAlign:"center",
5053 closeClick : function(){
5054 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5057 handleButton("cancel");
5062 dlg.on("hide", handleHide);
5064 //dlg.addKeyListener(27, handleEsc);
5066 this.buttons = buttons;
5067 var bt = this.buttonText;
5068 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5069 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5070 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5071 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5073 bodyEl = dlg.bodyEl.createChild({
5075 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5076 '<textarea class="roo-mb-textarea"></textarea>' +
5077 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
5079 msgEl = bodyEl.dom.firstChild;
5080 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5081 textboxEl.enableDisplayMode();
5082 textboxEl.addKeyListener([10,13], function(){
5083 if(dlg.isVisible() && opt && opt.buttons){
5086 }else if(opt.buttons.yes){
5087 handleButton("yes");
5091 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5092 textareaEl.enableDisplayMode();
5093 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5094 progressEl.enableDisplayMode();
5096 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5097 var pf = progressEl.dom.firstChild;
5099 pp = Roo.get(pf.firstChild);
5100 pp.setHeight(pf.offsetHeight);
5108 * Updates the message box body text
5109 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5110 * the XHTML-compliant non-breaking space character '&#160;')
5111 * @return {Roo.MessageBox} This message box
5113 updateText : function(text)
5115 if(!dlg.isVisible() && !opt.width){
5116 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5117 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5119 msgEl.innerHTML = text || ' ';
5121 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5122 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5124 Math.min(opt.width || cw , this.maxWidth),
5125 Math.max(opt.minWidth || this.minWidth, bwidth)
5128 activeTextEl.setWidth(w);
5130 if(dlg.isVisible()){
5131 dlg.fixedcenter = false;
5133 // to big, make it scroll. = But as usual stupid IE does not support
5136 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5137 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5138 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5140 bodyEl.dom.style.height = '';
5141 bodyEl.dom.style.overflowY = '';
5144 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5146 bodyEl.dom.style.overflowX = '';
5149 dlg.setContentSize(w, bodyEl.getHeight());
5150 if(dlg.isVisible()){
5151 dlg.fixedcenter = true;
5157 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
5158 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5159 * @param {Number} value Any number between 0 and 1 (e.g., .5)
5160 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5161 * @return {Roo.MessageBox} This message box
5163 updateProgress : function(value, text){
5165 this.updateText(text);
5168 if (pp) { // weird bug on my firefox - for some reason this is not defined
5169 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5170 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5176 * Returns true if the message box is currently displayed
5177 * @return {Boolean} True if the message box is visible, else false
5179 isVisible : function(){
5180 return dlg && dlg.isVisible();
5184 * Hides the message box if it is displayed
5187 if(this.isVisible()){
5193 * Displays a new message box, or reinitializes an existing message box, based on the config options
5194 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5195 * The following config object properties are supported:
5197 Property Type Description
5198 ---------- --------------- ------------------------------------------------------------------------------------
5199 animEl String/Element An id or Element from which the message box should animate as it opens and
5200 closes (defaults to undefined)
5201 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5202 cancel:'Bar'}), or false to not show any buttons (defaults to false)
5203 closable Boolean False to hide the top-right close button (defaults to true). Note that
5204 progress and wait dialogs will ignore this property and always hide the
5205 close button as they can only be closed programmatically.
5206 cls String A custom CSS class to apply to the message box element
5207 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
5208 displayed (defaults to 75)
5209 fn Function A callback function to execute after closing the dialog. The arguments to the
5210 function will be btn (the name of the button that was clicked, if applicable,
5211 e.g. "ok"), and text (the value of the active text field, if applicable).
5212 Progress and wait dialogs will ignore this option since they do not respond to
5213 user actions and can only be closed programmatically, so any required function
5214 should be called by the same code after it closes the dialog.
5215 icon String A CSS class that provides a background image to be used as an icon for
5216 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5217 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
5218 minWidth Number The minimum width in pixels of the message box (defaults to 100)
5219 modal Boolean False to allow user interaction with the page while the message box is
5220 displayed (defaults to true)
5221 msg String A string that will replace the existing message box body text (defaults
5222 to the XHTML-compliant non-breaking space character ' ')
5223 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
5224 progress Boolean True to display a progress bar (defaults to false)
5225 progressText String The text to display inside the progress bar if progress = true (defaults to '')
5226 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
5227 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
5228 title String The title text
5229 value String The string value to set into the active textbox element if displayed
5230 wait Boolean True to display a progress bar (defaults to false)
5231 width Number The width of the dialog in pixels
5238 msg: 'Please enter your address:',
5240 buttons: Roo.MessageBox.OKCANCEL,
5243 animEl: 'addAddressBtn'
5246 * @param {Object} config Configuration options
5247 * @return {Roo.MessageBox} This message box
5249 show : function(options)
5252 // this causes nightmares if you show one dialog after another
5253 // especially on callbacks..
5255 if(this.isVisible()){
5258 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5259 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
5260 Roo.log("New Dialog Message:" + options.msg )
5261 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5262 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5265 var d = this.getDialog();
5267 d.setTitle(opt.title || " ");
5268 d.closeEl.setDisplayed(opt.closable !== false);
5269 activeTextEl = textboxEl;
5270 opt.prompt = opt.prompt || (opt.multiline ? true : false);
5275 textareaEl.setHeight(typeof opt.multiline == "number" ?
5276 opt.multiline : this.defaultTextHeight);
5277 activeTextEl = textareaEl;
5286 progressEl.setDisplayed(opt.progress === true);
5288 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5290 this.updateProgress(0);
5291 activeTextEl.dom.value = opt.value || "";
5293 dlg.setDefaultButton(activeTextEl);
5295 var bs = opt.buttons;
5299 }else if(bs && bs.yes){
5300 db = buttons["yes"];
5302 dlg.setDefaultButton(db);
5304 bwidth = updateButtons(opt.buttons);
5305 this.updateText(opt.msg);
5307 d.el.addClass(opt.cls);
5309 d.proxyDrag = opt.proxyDrag === true;
5310 d.modal = opt.modal !== false;
5311 d.mask = opt.modal !== false ? mask : false;
5313 // force it to the end of the z-index stack so it gets a cursor in FF
5314 document.body.appendChild(dlg.el.dom);
5315 d.animateTarget = null;
5316 d.show(options.animEl);
5322 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5323 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5324 * and closing the message box when the process is complete.
5325 * @param {String} title The title bar text
5326 * @param {String} msg The message box body text
5327 * @return {Roo.MessageBox} This message box
5329 progress : function(title, msg){
5336 minWidth: this.minProgressWidth,
5343 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5344 * If a callback function is passed it will be called after the user clicks the button, and the
5345 * id of the button that was clicked will be passed as the only parameter to the callback
5346 * (could also be the top-right close button).
5347 * @param {String} title The title bar text
5348 * @param {String} msg The message box body text
5349 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5350 * @param {Object} scope (optional) The scope of the callback function
5351 * @return {Roo.MessageBox} This message box
5353 alert : function(title, msg, fn, scope)
5368 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5369 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5370 * You are responsible for closing the message box when the process is complete.
5371 * @param {String} msg The message box body text
5372 * @param {String} title (optional) The title bar text
5373 * @return {Roo.MessageBox} This message box
5375 wait : function(msg, title){
5386 waitTimer = Roo.TaskMgr.start({
5388 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5396 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5397 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5398 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5399 * @param {String} title The title bar text
5400 * @param {String} msg The message box body text
5401 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5402 * @param {Object} scope (optional) The scope of the callback function
5403 * @return {Roo.MessageBox} This message box
5405 confirm : function(title, msg, fn, scope){
5409 buttons: this.YESNO,
5418 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5419 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5420 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5421 * (could also be the top-right close button) and the text that was entered will be passed as the two
5422 * parameters to the callback.
5423 * @param {String} title The title bar text
5424 * @param {String} msg The message box body text
5425 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5426 * @param {Object} scope (optional) The scope of the callback function
5427 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5428 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5429 * @return {Roo.MessageBox} This message box
5431 prompt : function(title, msg, fn, scope, multiline){
5435 buttons: this.OKCANCEL,
5440 multiline: multiline,
5447 * Button config that displays a single OK button
5452 * Button config that displays Yes and No buttons
5455 YESNO : {yes:true, no:true},
5457 * Button config that displays OK and Cancel buttons
5460 OKCANCEL : {ok:true, cancel:true},
5462 * Button config that displays Yes, No and Cancel buttons
5465 YESNOCANCEL : {yes:true, no:true, cancel:true},
5468 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5471 defaultTextHeight : 75,
5473 * The maximum width in pixels of the message box (defaults to 600)
5478 * The minimum width in pixels of the message box (defaults to 100)
5483 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5484 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5487 minProgressWidth : 250,
5489 * An object containing the default button text strings that can be overriden for localized language support.
5490 * Supported properties are: ok, cancel, yes and no.
5491 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5504 * Shorthand for {@link Roo.MessageBox}
5506 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5507 Roo.Msg = Roo.Msg || Roo.MessageBox;
5516 * @class Roo.bootstrap.Navbar
5517 * @extends Roo.bootstrap.Component
5518 * Bootstrap Navbar class
5521 * Create a new Navbar
5522 * @param {Object} config The config object
5526 Roo.bootstrap.Navbar = function(config){
5527 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5531 * @event beforetoggle
5532 * Fire before toggle the menu
5533 * @param {Roo.EventObject} e
5535 "beforetoggle" : true
5539 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
5548 getAutoCreate : function(){
5551 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5555 initEvents :function ()
5557 //Roo.log(this.el.select('.navbar-toggle',true));
5558 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5565 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5567 var size = this.el.getSize();
5568 this.maskEl.setSize(size.width, size.height);
5569 this.maskEl.enableDisplayMode("block");
5578 getChildContainer : function()
5580 if (this.el && this.el.select('.collapse').getCount()) {
5581 return this.el.select('.collapse',true).first();
5596 onToggle : function()
5599 if(this.fireEvent('beforetoggle', this) === false){
5602 var ce = this.el.select('.navbar-collapse',true).first();
5604 if (!ce.hasClass('show')) {
5614 * Expand the navbar pulldown
5616 expand : function ()
5619 var ce = this.el.select('.navbar-collapse',true).first();
5620 if (ce.hasClass('collapsing')) {
5623 ce.dom.style.height = '';
5625 ce.addClass('in'); // old...
5626 ce.removeClass('collapse');
5627 ce.addClass('show');
5628 var h = ce.getHeight();
5630 ce.removeClass('show');
5631 // at this point we should be able to see it..
5632 ce.addClass('collapsing');
5634 ce.setHeight(0); // resize it ...
5635 ce.on('transitionend', function() {
5636 //Roo.log('done transition');
5637 ce.removeClass('collapsing');
5638 ce.addClass('show');
5639 ce.removeClass('collapse');
5641 ce.dom.style.height = '';
5642 }, this, { single: true} );
5644 ce.dom.scrollTop = 0;
5647 * Collapse the navbar pulldown
5649 collapse : function()
5651 var ce = this.el.select('.navbar-collapse',true).first();
5653 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5654 // it's collapsed or collapsing..
5657 ce.removeClass('in'); // old...
5658 ce.setHeight(ce.getHeight());
5659 ce.removeClass('show');
5660 ce.addClass('collapsing');
5662 ce.on('transitionend', function() {
5663 ce.dom.style.height = '';
5664 ce.removeClass('collapsing');
5665 ce.addClass('collapse');
5666 }, this, { single: true} );
5686 * @class Roo.bootstrap.NavSimplebar
5687 * @extends Roo.bootstrap.Navbar
5688 * Bootstrap Sidebar class
5690 * @cfg {Boolean} inverse is inverted color
5692 * @cfg {String} type (nav | pills | tabs)
5693 * @cfg {Boolean} arrangement stacked | justified
5694 * @cfg {String} align (left | right) alignment
5696 * @cfg {Boolean} main (true|false) main nav bar? default false
5697 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5699 * @cfg {String} tag (header|footer|nav|div) default is nav
5701 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5705 * Create a new Sidebar
5706 * @param {Object} config The config object
5710 Roo.bootstrap.NavSimplebar = function(config){
5711 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5714 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5730 getAutoCreate : function(){
5734 tag : this.tag || 'div',
5735 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5737 if (['light','white'].indexOf(this.weight) > -1) {
5738 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5740 cfg.cls += ' bg-' + this.weight;
5743 cfg.cls += ' navbar-inverse';
5747 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5749 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5758 cls: 'nav nav-' + this.xtype,
5764 this.type = this.type || 'nav';
5765 if (['tabs','pills'].indexOf(this.type) != -1) {
5766 cfg.cn[0].cls += ' nav-' + this.type
5770 if (this.type!=='nav') {
5771 Roo.log('nav type must be nav/tabs/pills')
5773 cfg.cn[0].cls += ' navbar-nav'
5779 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5780 cfg.cn[0].cls += ' nav-' + this.arrangement;
5784 if (this.align === 'right') {
5785 cfg.cn[0].cls += ' navbar-right';
5810 * navbar-expand-md fixed-top
5814 * @class Roo.bootstrap.NavHeaderbar
5815 * @extends Roo.bootstrap.NavSimplebar
5816 * Bootstrap Sidebar class
5818 * @cfg {String} brand what is brand
5819 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5820 * @cfg {String} brand_href href of the brand
5821 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5822 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5823 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5824 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5827 * Create a new Sidebar
5828 * @param {Object} config The config object
5832 Roo.bootstrap.NavHeaderbar = function(config){
5833 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5837 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5844 desktopCenter : false,
5847 getAutoCreate : function(){
5850 tag: this.nav || 'nav',
5851 cls: 'navbar navbar-expand-md',
5857 if (this.desktopCenter) {
5858 cn.push({cls : 'container', cn : []});
5866 cls: 'navbar-toggle navbar-toggler',
5867 'data-toggle': 'collapse',
5872 html: 'Toggle navigation'
5876 cls: 'icon-bar navbar-toggler-icon'
5889 cn.push( Roo.bootstrap.version == 4 ? btn : {
5891 cls: 'navbar-header',
5900 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5904 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5906 if (['light','white'].indexOf(this.weight) > -1) {
5907 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5909 cfg.cls += ' bg-' + this.weight;
5912 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5913 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5915 // tag can override this..
5917 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5920 if (this.brand !== '') {
5921 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5922 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5924 href: this.brand_href ? this.brand_href : '#',
5925 cls: 'navbar-brand',
5933 cfg.cls += ' main-nav';
5941 getHeaderChildContainer : function()
5943 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5944 return this.el.select('.navbar-header',true).first();
5947 return this.getChildContainer();
5950 getChildContainer : function()
5953 return this.el.select('.roo-navbar-collapse',true).first();
5958 initEvents : function()
5960 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5962 if (this.autohide) {
5967 Roo.get(document).on('scroll',function(e) {
5968 var ns = Roo.get(document).getScroll().top;
5969 var os = prevScroll;
5973 ft.removeClass('slideDown');
5974 ft.addClass('slideUp');
5977 ft.removeClass('slideUp');
5978 ft.addClass('slideDown');
5999 * @class Roo.bootstrap.NavSidebar
6000 * @extends Roo.bootstrap.Navbar
6001 * Bootstrap Sidebar class
6004 * Create a new Sidebar
6005 * @param {Object} config The config object
6009 Roo.bootstrap.NavSidebar = function(config){
6010 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
6013 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
6015 sidebar : true, // used by Navbar Item and NavbarGroup at present...
6017 getAutoCreate : function(){
6022 cls: 'sidebar sidebar-nav'
6044 * @class Roo.bootstrap.NavGroup
6045 * @extends Roo.bootstrap.Component
6046 * Bootstrap NavGroup class
6047 * @cfg {String} align (left|right)
6048 * @cfg {Boolean} inverse
6049 * @cfg {String} type (nav|pills|tab) default nav
6050 * @cfg {String} navId - reference Id for navbar.
6051 * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6054 * Create a new nav group
6055 * @param {Object} config The config object
6058 Roo.bootstrap.NavGroup = function(config){
6059 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
6062 Roo.bootstrap.NavGroup.register(this);
6066 * Fires when the active item changes
6067 * @param {Roo.bootstrap.NavGroup} this
6068 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6069 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
6076 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
6088 getAutoCreate : function()
6090 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6096 if (Roo.bootstrap.version == 4) {
6097 if (['tabs','pills'].indexOf(this.type) != -1) {
6098 cfg.cls += ' nav-' + this.type;
6100 // trying to remove so header bar can right align top?
6101 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6102 // do not use on header bar...
6103 cfg.cls += ' navbar-nav';
6108 if (['tabs','pills'].indexOf(this.type) != -1) {
6109 cfg.cls += ' nav-' + this.type
6111 if (this.type !== 'nav') {
6112 Roo.log('nav type must be nav/tabs/pills')
6114 cfg.cls += ' navbar-nav'
6118 if (this.parent() && this.parent().sidebar) {
6121 cls: 'dashboard-menu sidebar-menu'
6127 if (this.form === true) {
6130 cls: 'navbar-form form-inline'
6132 //nav navbar-right ml-md-auto
6133 if (this.align === 'right') {
6134 cfg.cls += ' navbar-right ml-md-auto';
6136 cfg.cls += ' navbar-left';
6140 if (this.align === 'right') {
6141 cfg.cls += ' navbar-right ml-md-auto';
6143 cfg.cls += ' mr-auto';
6147 cfg.cls += ' navbar-inverse';
6155 * sets the active Navigation item
6156 * @param {Roo.bootstrap.NavItem} the new current navitem
6158 setActiveItem : function(item)
6161 Roo.each(this.navItems, function(v){
6166 v.setActive(false, true);
6173 item.setActive(true, true);
6174 this.fireEvent('changed', this, item, prev);
6179 * gets the active Navigation item
6180 * @return {Roo.bootstrap.NavItem} the current navitem
6182 getActive : function()
6186 Roo.each(this.navItems, function(v){
6197 indexOfNav : function()
6201 Roo.each(this.navItems, function(v,i){
6212 * adds a Navigation item
6213 * @param {Roo.bootstrap.NavItem} the navitem to add
6215 addItem : function(cfg)
6217 if (this.form && Roo.bootstrap.version == 4) {
6220 var cn = new Roo.bootstrap.NavItem(cfg);
6222 cn.parentId = this.id;
6223 cn.onRender(this.el, null);
6227 * register a Navigation item
6228 * @param {Roo.bootstrap.NavItem} the navitem to add
6230 register : function(item)
6232 this.navItems.push( item);
6233 item.navId = this.navId;
6238 * clear all the Navigation item
6241 clearAll : function()
6244 this.el.dom.innerHTML = '';
6247 getNavItem: function(tabId)
6250 Roo.each(this.navItems, function(e) {
6251 if (e.tabId == tabId) {
6261 setActiveNext : function()
6263 var i = this.indexOfNav(this.getActive());
6264 if (i > this.navItems.length) {
6267 this.setActiveItem(this.navItems[i+1]);
6269 setActivePrev : function()
6271 var i = this.indexOfNav(this.getActive());
6275 this.setActiveItem(this.navItems[i-1]);
6277 clearWasActive : function(except) {
6278 Roo.each(this.navItems, function(e) {
6279 if (e.tabId != except.tabId && e.was_active) {
6280 e.was_active = false;
6287 getWasActive : function ()
6290 Roo.each(this.navItems, function(e) {
6305 Roo.apply(Roo.bootstrap.NavGroup, {
6309 * register a Navigation Group
6310 * @param {Roo.bootstrap.NavGroup} the navgroup to add
6312 register : function(navgrp)
6314 this.groups[navgrp.navId] = navgrp;
6318 * fetch a Navigation Group based on the navigation ID
6319 * @param {string} the navgroup to add
6320 * @returns {Roo.bootstrap.NavGroup} the navgroup
6322 get: function(navId) {
6323 if (typeof(this.groups[navId]) == 'undefined') {
6325 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6327 return this.groups[navId] ;
6342 * @class Roo.bootstrap.NavItem
6343 * @extends Roo.bootstrap.Component
6344 * Bootstrap Navbar.NavItem class
6345 * @cfg {String} href link to
6346 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6347 * @cfg {Boolean} button_outline show and outlined button
6348 * @cfg {String} html content of button
6349 * @cfg {String} badge text inside badge
6350 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6351 * @cfg {String} glyphicon DEPRICATED - use fa
6352 * @cfg {String} icon DEPRICATED - use fa
6353 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6354 * @cfg {Boolean} active Is item active
6355 * @cfg {Boolean} disabled Is item disabled
6356 * @cfg {String} linkcls Link Class
6357 * @cfg {Boolean} preventDefault (true | false) default false
6358 * @cfg {String} tabId the tab that this item activates.
6359 * @cfg {String} tagtype (a|span) render as a href or span?
6360 * @cfg {Boolean} animateRef (true|false) link to element default false
6363 * Create a new Navbar Item
6364 * @param {Object} config The config object
6366 Roo.bootstrap.NavItem = function(config){
6367 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6372 * The raw click event for the entire grid.
6373 * @param {Roo.EventObject} e
6378 * Fires when the active item active state changes
6379 * @param {Roo.bootstrap.NavItem} this
6380 * @param {boolean} state the new state
6386 * Fires when scroll to element
6387 * @param {Roo.bootstrap.NavItem} this
6388 * @param {Object} options
6389 * @param {Roo.EventObject} e
6397 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
6406 preventDefault : false,
6414 button_outline : false,
6418 getAutoCreate : function(){
6425 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6428 cfg.cls += ' active' ;
6430 if (this.disabled) {
6431 cfg.cls += ' disabled';
6435 if (this.button_weight.length) {
6436 cfg.tag = this.href ? 'a' : 'button';
6437 cfg.html = this.html || '';
6438 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6440 cfg.href = this.href;
6443 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6445 cfg.cls += " nav-html";
6448 // menu .. should add dropdown-menu class - so no need for carat..
6450 if (this.badge !== '') {
6452 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6457 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6461 href : this.href || "#",
6462 html: this.html || '',
6466 if (this.tagtype == 'a') {
6467 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
6471 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6472 } else if (this.fa) {
6473 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6474 } else if(this.glyphicon) {
6475 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6477 cfg.cn[0].cls += " nav-html";
6481 cfg.cn[0].html += " <span class='caret'></span>";
6485 if (this.badge !== '') {
6486 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6494 onRender : function(ct, position)
6496 // Roo.log("Call onRender: " + this.xtype);
6497 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6501 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6502 this.navLink = this.el.select('.nav-link',true).first();
6503 this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6508 initEvents: function()
6510 if (typeof (this.menu) != 'undefined') {
6511 this.menu.parentType = this.xtype;
6512 this.menu.triggerEl = this.el;
6513 this.menu = this.addxtype(Roo.apply({}, this.menu));
6516 this.el.on('click', this.onClick, this);
6518 //if(this.tagtype == 'span'){
6519 // this.el.select('span',true).on('click', this.onClick, this);
6522 // at this point parent should be available..
6523 this.parent().register(this);
6526 onClick : function(e)
6528 if (e.getTarget('.dropdown-menu-item')) {
6529 // did you click on a menu itemm.... - then don't trigger onclick..
6534 this.preventDefault ||
6537 Roo.log("NavItem - prevent Default?");
6541 if (this.disabled) {
6545 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6546 if (tg && tg.transition) {
6547 Roo.log("waiting for the transitionend");
6553 //Roo.log("fire event clicked");
6554 if(this.fireEvent('click', this, e) === false){
6558 if(this.tagtype == 'span'){
6562 //Roo.log(this.href);
6563 var ael = this.el.select('a',true).first();
6566 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6567 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6568 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6569 return; // ignore... - it's a 'hash' to another page.
6571 Roo.log("NavItem - prevent Default?");
6573 this.scrollToElement(e);
6577 var p = this.parent();
6579 if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6580 if (typeof(p.setActiveItem) !== 'undefined') {
6581 p.setActiveItem(this);
6585 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6586 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6587 // remove the collapsed menu expand...
6588 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6592 isActive: function () {
6595 setActive : function(state, fire, is_was_active)
6597 if (this.active && !state && this.navId) {
6598 this.was_active = true;
6599 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6601 nv.clearWasActive(this);
6605 this.active = state;
6608 this.el.removeClass('active');
6609 this.navLink ? this.navLink.removeClass('active') : false;
6610 } else if (!this.el.hasClass('active')) {
6612 this.el.addClass('active');
6613 if (Roo.bootstrap.version == 4 && this.navLink ) {
6614 this.navLink.addClass('active');
6619 this.fireEvent('changed', this, state);
6622 // show a panel if it's registered and related..
6624 if (!this.navId || !this.tabId || !state || is_was_active) {
6628 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6632 var pan = tg.getPanelByName(this.tabId);
6636 // if we can not flip to new panel - go back to old nav highlight..
6637 if (false == tg.showPanel(pan)) {
6638 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6640 var onav = nv.getWasActive();
6642 onav.setActive(true, false, true);
6651 // this should not be here...
6652 setDisabled : function(state)
6654 this.disabled = state;
6656 this.el.removeClass('disabled');
6657 } else if (!this.el.hasClass('disabled')) {
6658 this.el.addClass('disabled');
6664 * Fetch the element to display the tooltip on.
6665 * @return {Roo.Element} defaults to this.el
6667 tooltipEl : function()
6669 return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
6672 scrollToElement : function(e)
6674 var c = document.body;
6677 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6679 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6680 c = document.documentElement;
6683 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6689 var o = target.calcOffsetsTo(c);
6696 this.fireEvent('scrollto', this, options, e);
6698 Roo.get(c).scrollTo('top', options.value, true);
6703 * Set the HTML (text content) of the item
6704 * @param {string} html content for the nav item
6706 setHtml : function(html)
6709 this.htmlEl.dom.innerHTML = html;
6721 * <span> icon </span>
6722 * <span> text </span>
6723 * <span>badge </span>
6727 * @class Roo.bootstrap.NavSidebarItem
6728 * @extends Roo.bootstrap.NavItem
6729 * Bootstrap Navbar.NavSidebarItem class
6730 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6731 * {Boolean} open is the menu open
6732 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6733 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6734 * {String} buttonSize (sm|md|lg)the extra classes for the button
6735 * {Boolean} showArrow show arrow next to the text (default true)
6737 * Create a new Navbar Button
6738 * @param {Object} config The config object
6740 Roo.bootstrap.NavSidebarItem = function(config){
6741 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6746 * The raw click event for the entire grid.
6747 * @param {Roo.EventObject} e
6752 * Fires when the active item active state changes
6753 * @param {Roo.bootstrap.NavSidebarItem} this
6754 * @param {boolean} state the new state
6762 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6764 badgeWeight : 'default',
6770 buttonWeight : 'default',
6776 getAutoCreate : function(){
6781 href : this.href || '#',
6787 if(this.buttonView){
6790 href : this.href || '#',
6791 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6804 cfg.cls += ' active';
6807 if (this.disabled) {
6808 cfg.cls += ' disabled';
6811 cfg.cls += ' open x-open';
6814 if (this.glyphicon || this.icon) {
6815 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6816 a.cn.push({ tag : 'i', cls : c }) ;
6819 if(!this.buttonView){
6822 html : this.html || ''
6829 if (this.badge !== '') {
6830 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6836 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6839 a.cls += ' dropdown-toggle treeview' ;
6845 initEvents : function()
6847 if (typeof (this.menu) != 'undefined') {
6848 this.menu.parentType = this.xtype;
6849 this.menu.triggerEl = this.el;
6850 this.menu = this.addxtype(Roo.apply({}, this.menu));
6853 this.el.on('click', this.onClick, this);
6855 if(this.badge !== ''){
6856 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6861 onClick : function(e)
6868 if(this.preventDefault){
6872 this.fireEvent('click', this, e);
6875 disable : function()
6877 this.setDisabled(true);
6882 this.setDisabled(false);
6885 setDisabled : function(state)
6887 if(this.disabled == state){
6891 this.disabled = state;
6894 this.el.addClass('disabled');
6898 this.el.removeClass('disabled');
6903 setActive : function(state)
6905 if(this.active == state){
6909 this.active = state;
6912 this.el.addClass('active');
6916 this.el.removeClass('active');
6921 isActive: function ()
6926 setBadge : function(str)
6932 this.badgeEl.dom.innerHTML = str;
6947 Roo.namespace('Roo.bootstrap.breadcrumb');
6951 * @class Roo.bootstrap.breadcrumb.Nav
6952 * @extends Roo.bootstrap.Component
6953 * Bootstrap Breadcrumb Nav Class
6955 * @children Roo.bootstrap.breadcrumb.Item
6958 * Create a new breadcrumb.Nav
6959 * @param {Object} config The config object
6963 Roo.bootstrap.breadcrumb.Nav = function(config){
6964 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6969 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
6971 getAutoCreate : function()
6988 initEvents: function()
6990 this.olEl = this.el.select('ol',true).first();
6992 getChildContainer : function()
7008 * @class Roo.bootstrap.breadcrumb.Nav
7009 * @extends Roo.bootstrap.Component
7010 * Bootstrap Breadcrumb Nav Class
7012 * @children Roo.bootstrap.breadcrumb.Component
7013 * @cfg {String} html the content of the link.
7014 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7015 * @cfg {Boolean} active is it active
7019 * Create a new breadcrumb.Nav
7020 * @param {Object} config The config object
7023 Roo.bootstrap.breadcrumb.Item = function(config){
7024 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7029 * The img click event for the img.
7030 * @param {Roo.EventObject} e
7037 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
7042 getAutoCreate : function()
7047 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7049 if (this.href !== false) {
7056 cfg.html = this.html;
7062 initEvents: function()
7065 this.el.select('a', true).first().on('click',this.onClick, this)
7069 onClick : function(e)
7072 this.fireEvent('click',this, e);
7085 * @class Roo.bootstrap.Row
7086 * @extends Roo.bootstrap.Component
7087 * Bootstrap Row class (contains columns...)
7091 * @param {Object} config The config object
7094 Roo.bootstrap.Row = function(config){
7095 Roo.bootstrap.Row.superclass.constructor.call(this, config);
7098 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
7100 getAutoCreate : function(){
7119 * @class Roo.bootstrap.Pagination
7120 * @extends Roo.bootstrap.Component
7121 * Bootstrap Pagination class
7122 * @cfg {String} size xs | sm | md | lg
7123 * @cfg {Boolean} inverse false | true
7126 * Create a new Pagination
7127 * @param {Object} config The config object
7130 Roo.bootstrap.Pagination = function(config){
7131 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7134 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
7140 getAutoCreate : function(){
7146 cfg.cls += ' inverse';
7152 cfg.cls += " " + this.cls;
7170 * @class Roo.bootstrap.PaginationItem
7171 * @extends Roo.bootstrap.Component
7172 * Bootstrap PaginationItem class
7173 * @cfg {String} html text
7174 * @cfg {String} href the link
7175 * @cfg {Boolean} preventDefault (true | false) default true
7176 * @cfg {Boolean} active (true | false) default false
7177 * @cfg {Boolean} disabled default false
7181 * Create a new PaginationItem
7182 * @param {Object} config The config object
7186 Roo.bootstrap.PaginationItem = function(config){
7187 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7192 * The raw click event for the entire grid.
7193 * @param {Roo.EventObject} e
7199 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
7203 preventDefault: true,
7208 getAutoCreate : function(){
7214 href : this.href ? this.href : '#',
7215 html : this.html ? this.html : ''
7225 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7229 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7235 initEvents: function() {
7237 this.el.on('click', this.onClick, this);
7240 onClick : function(e)
7242 Roo.log('PaginationItem on click ');
7243 if(this.preventDefault){
7251 this.fireEvent('click', this, e);
7267 * @class Roo.bootstrap.Slider
7268 * @extends Roo.bootstrap.Component
7269 * Bootstrap Slider class
7272 * Create a new Slider
7273 * @param {Object} config The config object
7276 Roo.bootstrap.Slider = function(config){
7277 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7280 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
7282 getAutoCreate : function(){
7286 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7290 cls: 'ui-slider-handle ui-state-default ui-corner-all'
7302 * Ext JS Library 1.1.1
7303 * Copyright(c) 2006-2007, Ext JS, LLC.
7305 * Originally Released Under LGPL - original licence link has changed is not relivant.
7308 * <script type="text/javascript">
7312 * @class Roo.grid.AbstractSelectionModel
7313 * @extends Roo.util.Observable
7314 * Abstract base class for grid SelectionModels. It provides the interface that should be
7315 * implemented by descendant classes. This class should not be directly instantiated.
7318 Roo.grid.AbstractSelectionModel = function(){
7319 this.locked = false;
7320 Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7323 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable, {
7324 /** @ignore Called by the grid automatically. Do not call directly. */
7325 init : function(grid){
7331 * Locks the selections.
7338 * Unlocks the selections.
7340 unlock : function(){
7341 this.locked = false;
7345 * Returns true if the selections are locked.
7348 isLocked : function(){
7353 * Ext JS Library 1.1.1
7354 * Copyright(c) 2006-2007, Ext JS, LLC.
7356 * Originally Released Under LGPL - original licence link has changed is not relivant.
7359 * <script type="text/javascript">
7362 * @extends Roo.grid.AbstractSelectionModel
7363 * @class Roo.grid.RowSelectionModel
7364 * The default SelectionModel used by {@link Roo.grid.Grid}.
7365 * It supports multiple selections and keyboard selection/navigation.
7367 * @param {Object} config
7369 Roo.grid.RowSelectionModel = function(config){
7370 Roo.apply(this, config);
7371 this.selections = new Roo.util.MixedCollection(false, function(o){
7376 this.lastActive = false;
7380 * @event selectionchange
7381 * Fires when the selection changes
7382 * @param {SelectionModel} this
7384 "selectionchange" : true,
7386 * @event afterselectionchange
7387 * Fires after the selection changes (eg. by key press or clicking)
7388 * @param {SelectionModel} this
7390 "afterselectionchange" : true,
7392 * @event beforerowselect
7393 * Fires when a row is selected being selected, return false to cancel.
7394 * @param {SelectionModel} this
7395 * @param {Number} rowIndex The selected index
7396 * @param {Boolean} keepExisting False if other selections will be cleared
7398 "beforerowselect" : true,
7401 * Fires when a row is selected.
7402 * @param {SelectionModel} this
7403 * @param {Number} rowIndex The selected index
7404 * @param {Roo.data.Record} r The record
7408 * @event rowdeselect
7409 * Fires when a row is deselected.
7410 * @param {SelectionModel} this
7411 * @param {Number} rowIndex The selected index
7413 "rowdeselect" : true
7415 Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7416 this.locked = false;
7419 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel, {
7421 * @cfg {Boolean} singleSelect
7422 * True to allow selection of only one row at a time (defaults to false)
7424 singleSelect : false,
7427 initEvents : function(){
7429 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7430 this.grid.on("mousedown", this.handleMouseDown, this);
7431 }else{ // allow click to work like normal
7432 this.grid.on("rowclick", this.handleDragableRowClick, this);
7434 // bootstrap does not have a view..
7435 var view = this.grid.view ? this.grid.view : this.grid;
7436 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7439 this.selectPrevious(e.shiftKey);
7440 }else if(this.last !== false && this.lastActive !== false){
7441 var last = this.last;
7442 this.selectRange(this.last, this.lastActive-1);
7443 view.focusRow(this.lastActive);
7448 this.selectFirstRow();
7450 this.fireEvent("afterselectionchange", this);
7452 "down" : function(e){
7454 this.selectNext(e.shiftKey);
7455 }else if(this.last !== false && this.lastActive !== false){
7456 var last = this.last;
7457 this.selectRange(this.last, this.lastActive+1);
7458 view.focusRow(this.lastActive);
7463 this.selectFirstRow();
7465 this.fireEvent("afterselectionchange", this);
7471 view.on("refresh", this.onRefresh, this);
7472 view.on("rowupdated", this.onRowUpdated, this);
7473 view.on("rowremoved", this.onRemove, this);
7477 onRefresh : function(){
7478 var ds = this.grid.ds, i, v = this.grid.view;
7479 var s = this.selections;
7481 if((i = ds.indexOfId(r.id)) != -1){
7483 s.add(ds.getAt(i)); // updating the selection relate data
7491 onRemove : function(v, index, r){
7492 this.selections.remove(r);
7496 onRowUpdated : function(v, index, r){
7497 if(this.isSelected(r)){
7498 v.onRowSelect(index);
7504 * @param {Array} records The records to select
7505 * @param {Boolean} keepExisting (optional) True to keep existing selections
7507 selectRecords : function(records, keepExisting){
7509 this.clearSelections();
7511 var ds = this.grid.ds;
7512 for(var i = 0, len = records.length; i < len; i++){
7513 this.selectRow(ds.indexOf(records[i]), true);
7518 * Gets the number of selected rows.
7521 getCount : function(){
7522 return this.selections.length;
7526 * Selects the first row in the grid.
7528 selectFirstRow : function(){
7533 * Select the last row.
7534 * @param {Boolean} keepExisting (optional) True to keep existing selections
7536 selectLastRow : function(keepExisting){
7537 this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
7541 * Selects the row immediately following the last selected row.
7542 * @param {Boolean} keepExisting (optional) True to keep existing selections
7544 selectNext : function(keepExisting){
7545 if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
7546 this.selectRow(this.last+1, keepExisting);
7547 var view = this.grid.view ? this.grid.view : this.grid;
7548 view.focusRow(this.last);
7553 * Selects the row that precedes the last selected row.
7554 * @param {Boolean} keepExisting (optional) True to keep existing selections
7556 selectPrevious : function(keepExisting){
7558 this.selectRow(this.last-1, keepExisting);
7559 var view = this.grid.view ? this.grid.view : this.grid;
7560 view.focusRow(this.last);
7565 * Returns the selected records
7566 * @return {Array} Array of selected records
7568 getSelections : function(){
7569 return [].concat(this.selections.items);
7573 * Returns the first selected record.
7576 getSelected : function(){
7577 return this.selections.itemAt(0);
7582 * Clears all selections.
7584 clearSelections : function(fast){
7589 var ds = this.grid.ds;
7590 var s = this.selections;
7592 this.deselectRow(ds.indexOfId(r.id));
7596 this.selections.clear();
7605 selectAll : function(){
7609 this.selections.clear();
7610 for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
7611 this.selectRow(i, true);
7616 * Returns True if there is a selection.
7619 hasSelection : function(){
7620 return this.selections.length > 0;
7624 * Returns True if the specified row is selected.
7625 * @param {Number/Record} record The record or index of the record to check
7628 isSelected : function(index){
7629 var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
7630 return (r && this.selections.key(r.id) ? true : false);
7634 * Returns True if the specified record id is selected.
7635 * @param {String} id The id of record to check
7638 isIdSelected : function(id){
7639 return (this.selections.key(id) ? true : false);
7643 handleMouseDown : function(e, t)
7645 var view = this.grid.view ? this.grid.view : this.grid;
7647 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
7650 if(e.shiftKey && this.last !== false){
7651 var last = this.last;
7652 this.selectRange(last, rowIndex, e.ctrlKey);
7653 this.last = last; // reset the last
7654 view.focusRow(rowIndex);
7656 var isSelected = this.isSelected(rowIndex);
7657 if(e.button !== 0 && isSelected){
7658 view.focusRow(rowIndex);
7659 }else if(e.ctrlKey && isSelected){
7660 this.deselectRow(rowIndex);
7661 }else if(!isSelected){
7662 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
7663 view.focusRow(rowIndex);
7666 this.fireEvent("afterselectionchange", this);
7669 handleDragableRowClick : function(grid, rowIndex, e)
7671 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
7672 this.selectRow(rowIndex, false);
7673 var view = this.grid.view ? this.grid.view : this.grid;
7674 view.focusRow(rowIndex);
7675 this.fireEvent("afterselectionchange", this);
7680 * Selects multiple rows.
7681 * @param {Array} rows Array of the indexes of the row to select
7682 * @param {Boolean} keepExisting (optional) True to keep existing selections
7684 selectRows : function(rows, keepExisting){
7686 this.clearSelections();
7688 for(var i = 0, len = rows.length; i < len; i++){
7689 this.selectRow(rows[i], true);
7694 * Selects a range of rows. All rows in between startRow and endRow are also selected.
7695 * @param {Number} startRow The index of the first row in the range
7696 * @param {Number} endRow The index of the last row in the range
7697 * @param {Boolean} keepExisting (optional) True to retain existing selections
7699 selectRange : function(startRow, endRow, keepExisting){
7704 this.clearSelections();
7706 if(startRow <= endRow){
7707 for(var i = startRow; i <= endRow; i++){
7708 this.selectRow(i, true);
7711 for(var i = startRow; i >= endRow; i--){
7712 this.selectRow(i, true);
7718 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
7719 * @param {Number} startRow The index of the first row in the range
7720 * @param {Number} endRow The index of the last row in the range
7722 deselectRange : function(startRow, endRow, preventViewNotify){
7726 for(var i = startRow; i <= endRow; i++){
7727 this.deselectRow(i, preventViewNotify);
7733 * @param {Number} row The index of the row to select
7734 * @param {Boolean} keepExisting (optional) True to keep existing selections
7736 selectRow : function(index, keepExisting, preventViewNotify){
7737 if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
7740 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
7741 if(!keepExisting || this.singleSelect){
7742 this.clearSelections();
7744 var r = this.grid.ds.getAt(index);
7745 this.selections.add(r);
7746 this.last = this.lastActive = index;
7747 if(!preventViewNotify){
7748 var view = this.grid.view ? this.grid.view : this.grid;
7749 view.onRowSelect(index);
7751 this.fireEvent("rowselect", this, index, r);
7752 this.fireEvent("selectionchange", this);
7758 * @param {Number} row The index of the row to deselect
7760 deselectRow : function(index, preventViewNotify){
7764 if(this.last == index){
7767 if(this.lastActive == index){
7768 this.lastActive = false;
7770 var r = this.grid.ds.getAt(index);
7771 this.selections.remove(r);
7772 if(!preventViewNotify){
7773 var view = this.grid.view ? this.grid.view : this.grid;
7774 view.onRowDeselect(index);
7776 this.fireEvent("rowdeselect", this, index);
7777 this.fireEvent("selectionchange", this);
7781 restoreLast : function(){
7783 this.last = this._last;
7788 acceptsNav : function(row, col, cm){
7789 return !cm.isHidden(col) && cm.isCellEditable(col, row);
7793 onEditorKey : function(field, e){
7794 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
7799 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
7801 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
7803 }else if(k == e.ENTER && !e.ctrlKey){
7807 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
7809 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
7811 }else if(k == e.ESC){
7815 g.startEditing(newCell[0], newCell[1]);
7820 * Ext JS Library 1.1.1
7821 * Copyright(c) 2006-2007, Ext JS, LLC.
7823 * Originally Released Under LGPL - original licence link has changed is not relivant.
7826 * <script type="text/javascript">
7831 * @class Roo.grid.ColumnModel
7832 * @extends Roo.util.Observable
7833 * This is the default implementation of a ColumnModel used by the Grid. It defines
7834 * the columns in the grid.
7837 var colModel = new Roo.grid.ColumnModel([
7838 {header: "Ticker", width: 60, sortable: true, locked: true},
7839 {header: "Company Name", width: 150, sortable: true},
7840 {header: "Market Cap.", width: 100, sortable: true},
7841 {header: "$ Sales", width: 100, sortable: true, renderer: money},
7842 {header: "Employees", width: 100, sortable: true, resizable: false}
7847 * The config options listed for this class are options which may appear in each
7848 * individual column definition.
7849 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7851 * @param {Object} config An Array of column config objects. See this class's
7852 * config objects for details.
7854 Roo.grid.ColumnModel = function(config){
7856 * The config passed into the constructor
7858 this.config = []; //config;
7861 // if no id, create one
7862 // if the column does not have a dataIndex mapping,
7863 // map it to the order it is in the config
7864 for(var i = 0, len = config.length; i < len; i++){
7865 this.addColumn(config[i]);
7870 * The width of columns which have no width specified (defaults to 100)
7873 this.defaultWidth = 100;
7876 * Default sortable of columns which have no sortable specified (defaults to false)
7879 this.defaultSortable = false;
7883 * @event widthchange
7884 * Fires when the width of a column changes.
7885 * @param {ColumnModel} this
7886 * @param {Number} columnIndex The column index
7887 * @param {Number} newWidth The new width
7889 "widthchange": true,
7891 * @event headerchange
7892 * Fires when the text of a header changes.
7893 * @param {ColumnModel} this
7894 * @param {Number} columnIndex The column index
7895 * @param {Number} newText The new header text
7897 "headerchange": true,
7899 * @event hiddenchange
7900 * Fires when a column is hidden or "unhidden".
7901 * @param {ColumnModel} this
7902 * @param {Number} columnIndex The column index
7903 * @param {Boolean} hidden true if hidden, false otherwise
7905 "hiddenchange": true,
7907 * @event columnmoved
7908 * Fires when a column is moved.
7909 * @param {ColumnModel} this
7910 * @param {Number} oldIndex
7911 * @param {Number} newIndex
7913 "columnmoved" : true,
7915 * @event columlockchange
7916 * Fires when a column's locked state is changed
7917 * @param {ColumnModel} this
7918 * @param {Number} colIndex
7919 * @param {Boolean} locked true if locked
7921 "columnlockchange" : true
7923 Roo.grid.ColumnModel.superclass.constructor.call(this);
7925 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7927 * @cfg {String} header The header text to display in the Grid view.
7930 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7931 * {@link Roo.data.Record} definition from which to draw the column's value. If not
7932 * specified, the column's index is used as an index into the Record's data Array.
7935 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7936 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7939 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7940 * Defaults to the value of the {@link #defaultSortable} property.
7941 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7944 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
7947 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
7950 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7953 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7956 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7957 * given the cell's data value. See {@link #setRenderer}. If not specified, the
7958 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7959 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7962 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
7965 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
7968 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
7971 * @cfg {String} cursor (Optional)
7974 * @cfg {String} tooltip (Optional)
7977 * @cfg {Number} xs (Optional)
7980 * @cfg {Number} sm (Optional)
7983 * @cfg {Number} md (Optional)
7986 * @cfg {Number} lg (Optional)
7989 * Returns the id of the column at the specified index.
7990 * @param {Number} index The column index
7991 * @return {String} the id
7993 getColumnId : function(index){
7994 return this.config[index].id;
7998 * Returns the column for a specified id.
7999 * @param {String} id The column id
8000 * @return {Object} the column
8002 getColumnById : function(id){
8003 return this.lookup[id];
8008 * Returns the column Object for a specified dataIndex.
8009 * @param {String} dataIndex The column dataIndex
8010 * @return {Object|Boolean} the column or false if not found
8012 getColumnByDataIndex: function(dataIndex){
8013 var index = this.findColumnIndex(dataIndex);
8014 return index > -1 ? this.config[index] : false;
8018 * Returns the index for a specified column id.
8019 * @param {String} id The column id
8020 * @return {Number} the index, or -1 if not found
8022 getIndexById : function(id){
8023 for(var i = 0, len = this.config.length; i < len; i++){
8024 if(this.config[i].id == id){
8032 * Returns the index for a specified column dataIndex.
8033 * @param {String} dataIndex The column dataIndex
8034 * @return {Number} the index, or -1 if not found
8037 findColumnIndex : function(dataIndex){
8038 for(var i = 0, len = this.config.length; i < len; i++){
8039 if(this.config[i].dataIndex == dataIndex){
8047 moveColumn : function(oldIndex, newIndex){
8048 var c = this.config[oldIndex];
8049 this.config.splice(oldIndex, 1);
8050 this.config.splice(newIndex, 0, c);
8051 this.dataMap = null;
8052 this.fireEvent("columnmoved", this, oldIndex, newIndex);
8055 isLocked : function(colIndex){
8056 return this.config[colIndex].locked === true;
8059 setLocked : function(colIndex, value, suppressEvent){
8060 if(this.isLocked(colIndex) == value){
8063 this.config[colIndex].locked = value;
8065 this.fireEvent("columnlockchange", this, colIndex, value);
8069 getTotalLockedWidth : function(){
8071 for(var i = 0; i < this.config.length; i++){
8072 if(this.isLocked(i) && !this.isHidden(i)){
8073 this.totalWidth += this.getColumnWidth(i);
8079 getLockedCount : function(){
8080 for(var i = 0, len = this.config.length; i < len; i++){
8081 if(!this.isLocked(i)){
8086 return this.config.length;
8090 * Returns the number of columns.
8093 getColumnCount : function(visibleOnly){
8094 if(visibleOnly === true){
8096 for(var i = 0, len = this.config.length; i < len; i++){
8097 if(!this.isHidden(i)){
8103 return this.config.length;
8107 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8108 * @param {Function} fn
8109 * @param {Object} scope (optional)
8110 * @return {Array} result
8112 getColumnsBy : function(fn, scope){
8114 for(var i = 0, len = this.config.length; i < len; i++){
8115 var c = this.config[i];
8116 if(fn.call(scope||this, c, i) === true){
8124 * Returns true if the specified column is sortable.
8125 * @param {Number} col The column index
8128 isSortable : function(col){
8129 if(typeof this.config[col].sortable == "undefined"){
8130 return this.defaultSortable;
8132 return this.config[col].sortable;
8136 * Returns the rendering (formatting) function defined for the column.
8137 * @param {Number} col The column index.
8138 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8140 getRenderer : function(col){
8141 if(!this.config[col].renderer){
8142 return Roo.grid.ColumnModel.defaultRenderer;
8144 return this.config[col].renderer;
8148 * Sets the rendering (formatting) function for a column.
8149 * @param {Number} col The column index
8150 * @param {Function} fn The function to use to process the cell's raw data
8151 * to return HTML markup for the grid view. The render function is called with
8152 * the following parameters:<ul>
8153 * <li>Data value.</li>
8154 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8155 * <li>css A CSS style string to apply to the table cell.</li>
8156 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8157 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8158 * <li>Row index</li>
8159 * <li>Column index</li>
8160 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8162 setRenderer : function(col, fn){
8163 this.config[col].renderer = fn;
8167 * Returns the width for the specified column.
8168 * @param {Number} col The column index
8171 getColumnWidth : function(col){
8172 return this.config[col].width * 1 || this.defaultWidth;
8176 * Sets the width for a column.
8177 * @param {Number} col The column index
8178 * @param {Number} width The new width
8180 setColumnWidth : function(col, width, suppressEvent){
8181 this.config[col].width = width;
8182 this.totalWidth = null;
8184 this.fireEvent("widthchange", this, col, width);
8189 * Returns the total width of all columns.
8190 * @param {Boolean} includeHidden True to include hidden column widths
8193 getTotalWidth : function(includeHidden){
8194 if(!this.totalWidth){
8195 this.totalWidth = 0;
8196 for(var i = 0, len = this.config.length; i < len; i++){
8197 if(includeHidden || !this.isHidden(i)){
8198 this.totalWidth += this.getColumnWidth(i);
8202 return this.totalWidth;
8206 * Returns the header for the specified column.
8207 * @param {Number} col The column index
8210 getColumnHeader : function(col){
8211 return this.config[col].header;
8215 * Sets the header for a column.
8216 * @param {Number} col The column index
8217 * @param {String} header The new header
8219 setColumnHeader : function(col, header){
8220 this.config[col].header = header;
8221 this.fireEvent("headerchange", this, col, header);
8225 * Returns the tooltip for the specified column.
8226 * @param {Number} col The column index
8229 getColumnTooltip : function(col){
8230 return this.config[col].tooltip;
8233 * Sets the tooltip for a column.
8234 * @param {Number} col The column index
8235 * @param {String} tooltip The new tooltip
8237 setColumnTooltip : function(col, tooltip){
8238 this.config[col].tooltip = tooltip;
8242 * Returns the dataIndex for the specified column.
8243 * @param {Number} col The column index
8246 getDataIndex : function(col){
8247 return this.config[col].dataIndex;
8251 * Sets the dataIndex for a column.
8252 * @param {Number} col The column index
8253 * @param {Number} dataIndex The new dataIndex
8255 setDataIndex : function(col, dataIndex){
8256 this.config[col].dataIndex = dataIndex;
8262 * Returns true if the cell is editable.
8263 * @param {Number} colIndex The column index
8264 * @param {Number} rowIndex The row index - this is nto actually used..?
8267 isCellEditable : function(colIndex, rowIndex){
8268 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8272 * Returns the editor defined for the cell/column.
8273 * return false or null to disable editing.
8274 * @param {Number} colIndex The column index
8275 * @param {Number} rowIndex The row index
8278 getCellEditor : function(colIndex, rowIndex){
8279 return this.config[colIndex].editor;
8283 * Sets if a column is editable.
8284 * @param {Number} col The column index
8285 * @param {Boolean} editable True if the column is editable
8287 setEditable : function(col, editable){
8288 this.config[col].editable = editable;
8293 * Returns true if the column is hidden.
8294 * @param {Number} colIndex The column index
8297 isHidden : function(colIndex){
8298 return this.config[colIndex].hidden;
8303 * Returns true if the column width cannot be changed
8305 isFixed : function(colIndex){
8306 return this.config[colIndex].fixed;
8310 * Returns true if the column can be resized
8313 isResizable : function(colIndex){
8314 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8317 * Sets if a column is hidden.
8318 * @param {Number} colIndex The column index
8319 * @param {Boolean} hidden True if the column is hidden
8321 setHidden : function(colIndex, hidden){
8322 this.config[colIndex].hidden = hidden;
8323 this.totalWidth = null;
8324 this.fireEvent("hiddenchange", this, colIndex, hidden);
8328 * Sets the editor for a column.
8329 * @param {Number} col The column index
8330 * @param {Object} editor The editor object
8332 setEditor : function(col, editor){
8333 this.config[col].editor = editor;
8336 * Add a column (experimental...) - defaults to adding to the end..
8337 * @param {Object} config
8339 addColumn : function(c)
8342 var i = this.config.length;
8345 if(typeof c.dataIndex == "undefined"){
8348 if(typeof c.renderer == "string"){
8349 c.renderer = Roo.util.Format[c.renderer];
8351 if(typeof c.id == "undefined"){
8354 if(c.editor && c.editor.xtype){
8355 c.editor = Roo.factory(c.editor, Roo.grid);
8357 if(c.editor && c.editor.isFormField){
8358 c.editor = new Roo.grid.GridEditor(c.editor);
8360 this.lookup[c.id] = c;
8365 Roo.grid.ColumnModel.defaultRenderer = function(value)
8367 if(typeof value == "object") {
8370 if(typeof value == "string" && value.length < 1){
8374 return String.format("{0}", value);
8377 // Alias for backwards compatibility
8378 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8381 * Ext JS Library 1.1.1
8382 * Copyright(c) 2006-2007, Ext JS, LLC.
8384 * Originally Released Under LGPL - original licence link has changed is not relivant.
8387 * <script type="text/javascript">
8391 * @class Roo.LoadMask
8392 * A simple utility class for generically masking elements while loading data. If the element being masked has
8393 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8394 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
8395 * element's UpdateManager load indicator and will be destroyed after the initial load.
8397 * Create a new LoadMask
8398 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8399 * @param {Object} config The config object
8401 Roo.LoadMask = function(el, config){
8402 this.el = Roo.get(el);
8403 Roo.apply(this, config);
8405 this.store.on('beforeload', this.onBeforeLoad, this);
8406 this.store.on('load', this.onLoad, this);
8407 this.store.on('loadexception', this.onLoadException, this);
8408 this.removeMask = false;
8410 var um = this.el.getUpdateManager();
8411 um.showLoadIndicator = false; // disable the default indicator
8412 um.on('beforeupdate', this.onBeforeLoad, this);
8413 um.on('update', this.onLoad, this);
8414 um.on('failure', this.onLoad, this);
8415 this.removeMask = true;
8419 Roo.LoadMask.prototype = {
8421 * @cfg {Boolean} removeMask
8422 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
8423 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
8427 * The text to display in a centered loading message box (defaults to 'Loading...')
8431 * @cfg {String} msgCls
8432 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
8434 msgCls : 'x-mask-loading',
8437 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
8443 * Disables the mask to prevent it from being displayed
8445 disable : function(){
8446 this.disabled = true;
8450 * Enables the mask so that it can be displayed
8452 enable : function(){
8453 this.disabled = false;
8456 onLoadException : function()
8460 if (typeof(arguments[3]) != 'undefined') {
8461 Roo.MessageBox.alert("Error loading",arguments[3]);
8465 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8466 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8473 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8478 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8482 onBeforeLoad : function(){
8484 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
8489 destroy : function(){
8491 this.store.un('beforeload', this.onBeforeLoad, this);
8492 this.store.un('load', this.onLoad, this);
8493 this.store.un('loadexception', this.onLoadException, this);
8495 var um = this.el.getUpdateManager();
8496 um.un('beforeupdate', this.onBeforeLoad, this);
8497 um.un('update', this.onLoad, this);
8498 um.un('failure', this.onLoad, this);
8502 * @class Roo.bootstrap.Table
8504 * @extends Roo.bootstrap.Component
8505 * Bootstrap Table class. This class represents the primary interface of a component based grid control.
8506 * Similar to Roo.grid.Grid
8508 var table = Roo.factory({
8510 xns : Roo.bootstrap,
8511 autoSizeColumns: true,
8518 sortInfo : { direction : 'ASC', field: 'name' },
8520 xtype : 'HttpProxy',
8523 url : 'https://example.com/some.data.url.json'
8526 xtype : 'JsonReader',
8528 fields : [ 'id', 'name', whatever' ],
8535 xtype : 'ColumnModel',
8539 dataIndex : 'is_in_group',
8542 renderer : function(v, x , r) {
8544 return String.format("{0}", v)
8550 xtype : 'RowSelectionModel',
8551 xns : Roo.bootstrap.Table
8552 // you can add listeners to catch selection change here....
8558 grid.render(Roo.get("some-div"));
8561 Currently the Table uses multiple headers to try and handle XL / Medium etc... styling
8566 * @cfg {Roo.grid.RowSelectionModel|Roo.grid.CellSelectionModel} sm The selection model to use (cell selection is not supported yet)
8567 * @cfg {Roo.data.Store|Roo.data.SimpleStore} store The data store to use
8568 * @cfg {Roo.grid.ColumnModel} cm[] A column for th grid.
8570 * @cfg {String} cls table class
8573 * @cfg {boolean} striped Should the rows be alternative striped
8574 * @cfg {boolean} bordered Add borders to the table
8575 * @cfg {boolean} hover Add hover highlighting
8576 * @cfg {boolean} condensed Format condensed
8577 * @cfg {boolean} responsive Format condensed
8578 * @cfg {Boolean} loadMask (true|false) default false
8579 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
8580 * @cfg {Boolean} headerShow (true|false) generate thead, default true
8581 * @cfg {Boolean} rowSelection (true|false) default false
8582 * @cfg {Boolean} cellSelection (true|false) default false
8583 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
8584 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
8585 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
8586 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
8590 * Create a new Table
8591 * @param {Object} config The config object
8594 Roo.bootstrap.Table = function(config)
8596 Roo.bootstrap.Table.superclass.constructor.call(this, config);
8599 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
8600 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
8601 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
8602 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
8604 this.sm = this.sm || {xtype: 'RowSelectionModel'};
8606 this.sm.grid = this;
8607 this.selModel = Roo.factory(this.sm, Roo.grid);
8608 this.sm = this.selModel;
8609 this.sm.xmodule = this.xmodule || false;
8612 if (this.cm && typeof(this.cm.config) == 'undefined') {
8613 this.colModel = new Roo.grid.ColumnModel(this.cm);
8614 this.cm = this.colModel;
8615 this.cm.xmodule = this.xmodule || false;
8618 this.store= Roo.factory(this.store, Roo.data);
8619 this.ds = this.store;
8620 this.ds.xmodule = this.xmodule || false;
8623 if (this.footer && this.store) {
8624 this.footer.dataSource = this.ds;
8625 this.footer = Roo.factory(this.footer);
8632 * Fires when a cell is clicked
8633 * @param {Roo.bootstrap.Table} this
8634 * @param {Roo.Element} el
8635 * @param {Number} rowIndex
8636 * @param {Number} columnIndex
8637 * @param {Roo.EventObject} e
8641 * @event celldblclick
8642 * Fires when a cell is double clicked
8643 * @param {Roo.bootstrap.Table} this
8644 * @param {Roo.Element} el
8645 * @param {Number} rowIndex
8646 * @param {Number} columnIndex
8647 * @param {Roo.EventObject} e
8649 "celldblclick" : true,
8652 * Fires when a row is clicked
8653 * @param {Roo.bootstrap.Table} this
8654 * @param {Roo.Element} el
8655 * @param {Number} rowIndex
8656 * @param {Roo.EventObject} e
8660 * @event rowdblclick
8661 * Fires when a row is double clicked
8662 * @param {Roo.bootstrap.Table} this
8663 * @param {Roo.Element} el
8664 * @param {Number} rowIndex
8665 * @param {Roo.EventObject} e
8667 "rowdblclick" : true,
8670 * Fires when a mouseover occur
8671 * @param {Roo.bootstrap.Table} this
8672 * @param {Roo.Element} el
8673 * @param {Number} rowIndex
8674 * @param {Number} columnIndex
8675 * @param {Roo.EventObject} e
8680 * Fires when a mouseout occur
8681 * @param {Roo.bootstrap.Table} this
8682 * @param {Roo.Element} el
8683 * @param {Number} rowIndex
8684 * @param {Number} columnIndex
8685 * @param {Roo.EventObject} e
8690 * Fires when a row is rendered, so you can change add a style to it.
8691 * @param {Roo.bootstrap.Table} this
8692 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
8696 * @event rowsrendered
8697 * Fires when all the rows have been rendered
8698 * @param {Roo.bootstrap.Table} this
8700 'rowsrendered' : true,
8702 * @event contextmenu
8703 * The raw contextmenu event for the entire grid.
8704 * @param {Roo.EventObject} e
8706 "contextmenu" : true,
8708 * @event rowcontextmenu
8709 * Fires when a row is right clicked
8710 * @param {Roo.bootstrap.Table} this
8711 * @param {Number} rowIndex
8712 * @param {Roo.EventObject} e
8714 "rowcontextmenu" : true,
8716 * @event cellcontextmenu
8717 * Fires when a cell is right clicked
8718 * @param {Roo.bootstrap.Table} this
8719 * @param {Number} rowIndex
8720 * @param {Number} cellIndex
8721 * @param {Roo.EventObject} e
8723 "cellcontextmenu" : true,
8725 * @event headercontextmenu
8726 * Fires when a header is right clicked
8727 * @param {Roo.bootstrap.Table} this
8728 * @param {Number} columnIndex
8729 * @param {Roo.EventObject} e
8731 "headercontextmenu" : true,
8734 * The raw mousedown event for the entire grid.
8735 * @param {Roo.EventObject} e
8742 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
8759 rowSelection : false,
8760 cellSelection : false,
8763 // Roo.Element - the tbody
8765 // Roo.Element - thead element
8768 container: false, // used by gridpanel...
8774 auto_hide_footer : false,
8776 getAutoCreate : function()
8778 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8785 // this get's auto added by panel.Grid
8786 if (this.scrollBody) {
8787 cfg.cls += ' table-body-fixed';
8790 cfg.cls += ' table-striped';
8794 cfg.cls += ' table-hover';
8796 if (this.bordered) {
8797 cfg.cls += ' table-bordered';
8799 if (this.condensed) {
8800 cfg.cls += ' table-condensed';
8803 if (this.responsive) {
8804 cfg.cls += ' table-responsive';
8808 cfg.cls+= ' ' +this.cls;
8814 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8817 if(this.store || this.cm){
8818 if(this.headerShow){
8819 cfg.cn.push(this.renderHeader());
8822 cfg.cn.push(this.renderBody());
8824 if(this.footerShow){
8825 cfg.cn.push(this.renderFooter());
8827 // where does this come from?
8828 //cfg.cls+= ' TableGrid';
8831 return { cn : [ cfg ] };
8834 initEvents : function()
8836 if(!this.store || !this.cm){
8839 if (this.selModel) {
8840 this.selModel.initEvents();
8844 //Roo.log('initEvents with ds!!!!');
8846 this.mainBody = this.el.select('tbody', true).first();
8847 this.mainHead = this.el.select('thead', true).first();
8848 this.mainFoot = this.el.select('tfoot', true).first();
8853 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8854 e.on('click', this.sort, this);
8858 // why is this done????? = it breaks dialogs??
8859 //this.parent().el.setStyle('position', 'relative');
8863 this.footer.parentId = this.id;
8864 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8867 this.el.select('tfoot tr td').first().addClass('hide');
8872 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8875 this.store.on('load', this.onLoad, this);
8876 this.store.on('beforeload', this.onBeforeLoad, this);
8877 this.store.on('update', this.onUpdate, this);
8878 this.store.on('add', this.onAdd, this);
8879 this.store.on("clear", this.clear, this);
8881 this.el.on("contextmenu", this.onContextMenu, this);
8884 this.cm.on("headerchange", this.onHeaderChange, this);
8885 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8888 this.mainBody.on("click", this.onClick, this);
8889 this.mainBody.on("dblclick", this.onDblClick, this);
8890 this.mainBody.on('scroll', this.onBodyScroll, this);
8892 // guessing mainbody will work - this relays usually caught by selmodel at present.
8893 this.relayEvents(this.mainBody, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
8900 onContextMenu : function(e, t)
8902 this.processEvent("contextmenu", e);
8905 processEvent : function(name, e)
8907 if (name != 'touchstart' ) {
8908 this.fireEvent(name, e);
8911 var t = e.getTarget();
8913 var cell = Roo.get(t);
8919 if(cell.findParent('tfoot', false, true)){
8923 if(cell.findParent('thead', false, true)){
8925 if(e.getTarget().nodeName.toLowerCase() != 'th'){
8926 cell = Roo.get(t).findParent('th', false, true);
8928 Roo.log("failed to find th in thead?");
8929 Roo.log(e.getTarget());
8934 var cellIndex = cell.dom.cellIndex;
8936 var ename = name == 'touchstart' ? 'click' : name;
8937 this.fireEvent("header" + ename, this, cellIndex, e);
8942 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8943 cell = Roo.get(t).findParent('td', false, true);
8945 Roo.log("failed to find th in tbody?");
8946 Roo.log(e.getTarget());
8951 var row = cell.findParent('tr', false, true);
8952 var cellIndex = cell.dom.cellIndex;
8953 var rowIndex = row.dom.rowIndex - 1;
8957 this.fireEvent("row" + name, this, rowIndex, e);
8961 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8967 onMouseover : function(e, el)
8969 var cell = Roo.get(el);
8975 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8976 cell = cell.findParent('td', false, true);
8979 var row = cell.findParent('tr', false, true);
8980 var cellIndex = cell.dom.cellIndex;
8981 var rowIndex = row.dom.rowIndex - 1; // start from 0
8983 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8987 onMouseout : function(e, el)
8989 var cell = Roo.get(el);
8995 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8996 cell = cell.findParent('td', false, true);
8999 var row = cell.findParent('tr', false, true);
9000 var cellIndex = cell.dom.cellIndex;
9001 var rowIndex = row.dom.rowIndex - 1; // start from 0
9003 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9007 onClick : function(e, el)
9009 var cell = Roo.get(el);
9011 if(!cell || (!this.cellSelection && !this.rowSelection)){
9015 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9016 cell = cell.findParent('td', false, true);
9019 if(!cell || typeof(cell) == 'undefined'){
9023 var row = cell.findParent('tr', false, true);
9025 if(!row || typeof(row) == 'undefined'){
9029 var cellIndex = cell.dom.cellIndex;
9030 var rowIndex = this.getRowIndex(row);
9032 // why??? - should these not be based on SelectionModel?
9033 //if(this.cellSelection){
9034 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9037 //if(this.rowSelection){
9038 this.fireEvent('rowclick', this, row, rowIndex, e);
9043 onDblClick : function(e,el)
9045 var cell = Roo.get(el);
9047 if(!cell || (!this.cellSelection && !this.rowSelection)){
9051 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9052 cell = cell.findParent('td', false, true);
9055 if(!cell || typeof(cell) == 'undefined'){
9059 var row = cell.findParent('tr', false, true);
9061 if(!row || typeof(row) == 'undefined'){
9065 var cellIndex = cell.dom.cellIndex;
9066 var rowIndex = this.getRowIndex(row);
9068 if(this.cellSelection){
9069 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9072 if(this.rowSelection){
9073 this.fireEvent('rowdblclick', this, row, rowIndex, e);
9076 findRowIndex : function(el)
9078 var cell = Roo.get(el);
9082 var row = cell.findParent('tr', false, true);
9084 if(!row || typeof(row) == 'undefined'){
9087 return this.getRowIndex(row);
9089 sort : function(e,el)
9091 var col = Roo.get(el);
9093 if(!col.hasClass('sortable')){
9097 var sort = col.attr('sort');
9100 if(col.select('i', true).first().hasClass('fa-arrow-up')){
9104 this.store.sortInfo = {field : sort, direction : dir};
9107 Roo.log("calling footer first");
9108 this.footer.onClick('first');
9111 this.store.load({ params : { start : 0 } });
9115 renderHeader : function()
9123 this.totalWidth = 0;
9125 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9127 var config = cm.config[i];
9131 cls : 'x-hcol-' + i,
9134 html: cm.getColumnHeader(i)
9137 var tooltip = cm.getColumnTooltip(i);
9139 c.tooltip = tooltip;
9145 if(typeof(config.sortable) != 'undefined' && config.sortable){
9147 c.html = '<i class="fa"></i>' + c.html;
9150 // could use BS4 hidden-..-down
9152 if(typeof(config.lgHeader) != 'undefined'){
9153 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9156 if(typeof(config.mdHeader) != 'undefined'){
9157 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9160 if(typeof(config.smHeader) != 'undefined'){
9161 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9164 if(typeof(config.xsHeader) != 'undefined'){
9165 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9172 if(typeof(config.tooltip) != 'undefined'){
9173 c.tooltip = config.tooltip;
9176 if(typeof(config.colspan) != 'undefined'){
9177 c.colspan = config.colspan;
9180 if(typeof(config.hidden) != 'undefined' && config.hidden){
9181 c.style += ' display:none;';
9184 if(typeof(config.dataIndex) != 'undefined'){
9185 c.sort = config.dataIndex;
9190 if(typeof(config.align) != 'undefined' && config.align.length){
9191 c.style += ' text-align:' + config.align + ';';
9194 if(typeof(config.width) != 'undefined'){
9195 c.style += ' width:' + config.width + 'px;';
9196 this.totalWidth += config.width;
9198 this.totalWidth += 100; // assume minimum of 100 per column?
9201 if(typeof(config.cls) != 'undefined'){
9202 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9205 ['xs','sm','md','lg'].map(function(size){
9207 if(typeof(config[size]) == 'undefined'){
9211 if (!config[size]) { // 0 = hidden
9212 // BS 4 '0' is treated as hide that column and below.
9213 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9217 c.cls += ' col-' + size + '-' + config[size] + (
9218 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9230 renderBody : function()
9240 colspan : this.cm.getColumnCount()
9250 renderFooter : function()
9260 colspan : this.cm.getColumnCount()
9274 // Roo.log('ds onload');
9279 var ds = this.store;
9281 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9282 e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9283 if (_this.store.sortInfo) {
9285 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9286 e.select('i', true).addClass(['fa-arrow-up']);
9289 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9290 e.select('i', true).addClass(['fa-arrow-down']);
9295 var tbody = this.mainBody;
9297 if(ds.getCount() > 0){
9298 ds.data.each(function(d,rowIndex){
9299 var row = this.renderRow(cm, ds, rowIndex);
9301 tbody.createChild(row);
9305 if(row.cellObjects.length){
9306 Roo.each(row.cellObjects, function(r){
9307 _this.renderCellObject(r);
9314 var tfoot = this.el.select('tfoot', true).first();
9316 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
9318 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
9320 var total = this.ds.getTotalCount();
9322 if(this.footer.pageSize < total){
9323 this.mainFoot.show();
9327 Roo.each(this.el.select('tbody td', true).elements, function(e){
9328 e.on('mouseover', _this.onMouseover, _this);
9331 Roo.each(this.el.select('tbody td', true).elements, function(e){
9332 e.on('mouseout', _this.onMouseout, _this);
9334 this.fireEvent('rowsrendered', this);
9340 onUpdate : function(ds,record)
9342 this.refreshRow(record);
9346 onRemove : function(ds, record, index, isUpdate){
9347 if(isUpdate !== true){
9348 this.fireEvent("beforerowremoved", this, index, record);
9350 var bt = this.mainBody.dom;
9352 var rows = this.el.select('tbody > tr', true).elements;
9354 if(typeof(rows[index]) != 'undefined'){
9355 bt.removeChild(rows[index].dom);
9358 // if(bt.rows[index]){
9359 // bt.removeChild(bt.rows[index]);
9362 if(isUpdate !== true){
9363 //this.stripeRows(index);
9364 //this.syncRowHeights(index, index);
9366 this.fireEvent("rowremoved", this, index, record);
9370 onAdd : function(ds, records, rowIndex)
9372 //Roo.log('on Add called');
9373 // - note this does not handle multiple adding very well..
9374 var bt = this.mainBody.dom;
9375 for (var i =0 ; i < records.length;i++) {
9376 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
9377 //Roo.log(records[i]);
9378 //Roo.log(this.store.getAt(rowIndex+i));
9379 this.insertRow(this.store, rowIndex + i, false);
9386 refreshRow : function(record){
9387 var ds = this.store, index;
9388 if(typeof record == 'number'){
9390 record = ds.getAt(index);
9392 index = ds.indexOf(record);
9394 return; // should not happen - but seems to
9397 this.insertRow(ds, index, true);
9399 this.onRemove(ds, record, index+1, true);
9401 //this.syncRowHeights(index, index);
9403 this.fireEvent("rowupdated", this, index, record);
9406 insertRow : function(dm, rowIndex, isUpdate){
9409 this.fireEvent("beforerowsinserted", this, rowIndex);
9411 //var s = this.getScrollState();
9412 var row = this.renderRow(this.cm, this.store, rowIndex);
9413 // insert before rowIndex..
9414 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
9418 if(row.cellObjects.length){
9419 Roo.each(row.cellObjects, function(r){
9420 _this.renderCellObject(r);
9425 this.fireEvent("rowsinserted", this, rowIndex);
9426 //this.syncRowHeights(firstRow, lastRow);
9427 //this.stripeRows(firstRow);
9434 getRowDom : function(rowIndex)
9436 var rows = this.el.select('tbody > tr', true).elements;
9438 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
9441 // returns the object tree for a tr..
9444 renderRow : function(cm, ds, rowIndex)
9446 var d = ds.getAt(rowIndex);
9450 cls : 'x-row-' + rowIndex,
9454 var cellObjects = [];
9456 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9457 var config = cm.config[i];
9459 var renderer = cm.getRenderer(i);
9463 if(typeof(renderer) !== 'undefined'){
9464 value = renderer(d.data[cm.getDataIndex(i)], false, d);
9466 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
9467 // and are rendered into the cells after the row is rendered - using the id for the element.
9469 if(typeof(value) === 'object'){
9479 rowIndex : rowIndex,
9484 this.fireEvent('rowclass', this, rowcfg);
9488 // this might end up displaying HTML?
9489 // this is too messy... - better to only do it on columsn you know are going to be too long
9490 //tooltip : (typeof(value) === 'object') ? '' : value,
9491 cls : rowcfg.rowClass + ' x-col-' + i,
9493 html: (typeof(value) === 'object') ? '' : value
9500 if(typeof(config.colspan) != 'undefined'){
9501 td.colspan = config.colspan;
9504 if(typeof(config.hidden) != 'undefined' && config.hidden){
9505 td.style += ' display:none;';
9508 if(typeof(config.align) != 'undefined' && config.align.length){
9509 td.style += ' text-align:' + config.align + ';';
9511 if(typeof(config.valign) != 'undefined' && config.valign.length){
9512 td.style += ' vertical-align:' + config.valign + ';';
9515 if(typeof(config.width) != 'undefined'){
9516 td.style += ' width:' + config.width + 'px;';
9519 if(typeof(config.cursor) != 'undefined'){
9520 td.style += ' cursor:' + config.cursor + ';';
9523 if(typeof(config.cls) != 'undefined'){
9524 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
9527 ['xs','sm','md','lg'].map(function(size){
9529 if(typeof(config[size]) == 'undefined'){
9535 if (!config[size]) { // 0 = hidden
9536 // BS 4 '0' is treated as hide that column and below.
9537 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
9541 td.cls += ' col-' + size + '-' + config[size] + (
9542 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9552 row.cellObjects = cellObjects;
9560 onBeforeLoad : function()
9569 this.el.select('tbody', true).first().dom.innerHTML = '';
9572 * Show or hide a row.
9573 * @param {Number} rowIndex to show or hide
9574 * @param {Boolean} state hide
9576 setRowVisibility : function(rowIndex, state)
9578 var bt = this.mainBody.dom;
9580 var rows = this.el.select('tbody > tr', true).elements;
9582 if(typeof(rows[rowIndex]) == 'undefined'){
9585 rows[rowIndex].dom.style.display = state ? '' : 'none';
9589 getSelectionModel : function(){
9591 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
9593 return this.selModel;
9596 * Render the Roo.bootstrap object from renderder
9598 renderCellObject : function(r)
9602 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
9604 var t = r.cfg.render(r.container);
9607 Roo.each(r.cfg.cn, function(c){
9609 container: t.getChildContainer(),
9612 _this.renderCellObject(child);
9617 getRowIndex : function(row)
9621 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
9632 * Returns the grid's underlying element = used by panel.Grid
9633 * @return {Element} The element
9635 getGridEl : function(){
9639 * Forces a resize - used by panel.Grid
9640 * @return {Element} The element
9642 autoSize : function()
9644 //var ctr = Roo.get(this.container.dom.parentElement);
9645 var ctr = Roo.get(this.el.dom);
9647 var thd = this.getGridEl().select('thead',true).first();
9648 var tbd = this.getGridEl().select('tbody', true).first();
9649 var tfd = this.getGridEl().select('tfoot', true).first();
9651 var cw = ctr.getWidth();
9652 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
9656 tbd.setWidth(ctr.getWidth());
9657 // if the body has a max height - and then scrolls - we should perhaps set up the height here
9658 // this needs fixing for various usage - currently only hydra job advers I think..
9660 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
9662 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
9665 cw = Math.max(cw, this.totalWidth);
9666 this.getGridEl().select('tbody tr',true).setWidth(cw);
9668 // resize 'expandable coloumn?
9670 return; // we doe not have a view in this design..
9673 onBodyScroll: function()
9675 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
9677 this.mainHead.setStyle({
9678 'position' : 'relative',
9679 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
9685 var scrollHeight = this.mainBody.dom.scrollHeight;
9687 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
9689 var height = this.mainBody.getHeight();
9691 if(scrollHeight - height == scrollTop) {
9693 var total = this.ds.getTotalCount();
9695 if(this.footer.cursor + this.footer.pageSize < total){
9697 this.footer.ds.load({
9699 start : this.footer.cursor + this.footer.pageSize,
9700 limit : this.footer.pageSize
9710 onHeaderChange : function()
9712 var header = this.renderHeader();
9713 var table = this.el.select('table', true).first();
9715 this.mainHead.remove();
9716 this.mainHead = table.createChild(header, this.mainBody, false);
9718 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9719 e.on('click', this.sort, this);
9725 onHiddenChange : function(colModel, colIndex, hidden)
9727 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
9728 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
9730 this.CSS.updateRule(thSelector, "display", "");
9731 this.CSS.updateRule(tdSelector, "display", "");
9734 this.CSS.updateRule(thSelector, "display", "none");
9735 this.CSS.updateRule(tdSelector, "display", "none");
9738 this.onHeaderChange();
9742 setColumnWidth: function(col_index, width)
9744 // width = "md-2 xs-2..."
9745 if(!this.colModel.config[col_index]) {
9749 var w = width.split(" ");
9751 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
9753 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
9756 for(var j = 0; j < w.length; j++) {
9762 var size_cls = w[j].split("-");
9764 if(!Number.isInteger(size_cls[1] * 1)) {
9768 if(!this.colModel.config[col_index][size_cls[0]]) {
9772 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9776 h_row[0].classList.replace(
9777 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9778 "col-"+size_cls[0]+"-"+size_cls[1]
9781 for(var i = 0; i < rows.length; i++) {
9783 var size_cls = w[j].split("-");
9785 if(!Number.isInteger(size_cls[1] * 1)) {
9789 if(!this.colModel.config[col_index][size_cls[0]]) {
9793 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9797 rows[i].classList.replace(
9798 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9799 "col-"+size_cls[0]+"-"+size_cls[1]
9803 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
9813 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
9814 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;/*
9822 * @class Roo.bootstrap.TableCell
9823 * @extends Roo.bootstrap.Component
9824 * Bootstrap TableCell class
9825 * @cfg {String} html cell contain text
9826 * @cfg {String} cls cell class
9827 * @cfg {String} tag cell tag (td|th) default td
9828 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
9829 * @cfg {String} align Aligns the content in a cell
9830 * @cfg {String} axis Categorizes cells
9831 * @cfg {String} bgcolor Specifies the background color of a cell
9832 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9833 * @cfg {Number} colspan Specifies the number of columns a cell should span
9834 * @cfg {String} headers Specifies one or more header cells a cell is related to
9835 * @cfg {Number} height Sets the height of a cell
9836 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
9837 * @cfg {Number} rowspan Sets the number of rows a cell should span
9838 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
9839 * @cfg {String} valign Vertical aligns the content in a cell
9840 * @cfg {Number} width Specifies the width of a cell
9843 * Create a new TableCell
9844 * @param {Object} config The config object
9847 Roo.bootstrap.TableCell = function(config){
9848 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
9851 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
9871 getAutoCreate : function(){
9872 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
9892 cfg.align=this.align
9898 cfg.bgcolor=this.bgcolor
9901 cfg.charoff=this.charoff
9904 cfg.colspan=this.colspan
9907 cfg.headers=this.headers
9910 cfg.height=this.height
9913 cfg.nowrap=this.nowrap
9916 cfg.rowspan=this.rowspan
9919 cfg.scope=this.scope
9922 cfg.valign=this.valign
9925 cfg.width=this.width
9944 * @class Roo.bootstrap.TableRow
9945 * @extends Roo.bootstrap.Component
9946 * Bootstrap TableRow class
9947 * @cfg {String} cls row class
9948 * @cfg {String} align Aligns the content in a table row
9949 * @cfg {String} bgcolor Specifies a background color for a table row
9950 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9951 * @cfg {String} valign Vertical aligns the content in a table row
9954 * Create a new TableRow
9955 * @param {Object} config The config object
9958 Roo.bootstrap.TableRow = function(config){
9959 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9962 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
9970 getAutoCreate : function(){
9971 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9981 cfg.align = this.align;
9984 cfg.bgcolor = this.bgcolor;
9987 cfg.charoff = this.charoff;
9990 cfg.valign = this.valign;
10008 * @class Roo.bootstrap.TableBody
10009 * @extends Roo.bootstrap.Component
10010 * Bootstrap TableBody class
10011 * @cfg {String} cls element class
10012 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10013 * @cfg {String} align Aligns the content inside the element
10014 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10015 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10018 * Create a new TableBody
10019 * @param {Object} config The config object
10022 Roo.bootstrap.TableBody = function(config){
10023 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10026 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
10034 getAutoCreate : function(){
10035 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10045 cfg.tag = this.tag;
10049 cfg.align = this.align;
10052 cfg.charoff = this.charoff;
10055 cfg.valign = this.valign;
10062 // initEvents : function()
10065 // if(!this.store){
10069 // this.store = Roo.factory(this.store, Roo.data);
10070 // this.store.on('load', this.onLoad, this);
10072 // this.store.load();
10076 // onLoad: function ()
10078 // this.fireEvent('load', this);
10088 * Ext JS Library 1.1.1
10089 * Copyright(c) 2006-2007, Ext JS, LLC.
10091 * Originally Released Under LGPL - original licence link has changed is not relivant.
10094 * <script type="text/javascript">
10097 // as we use this in bootstrap.
10098 Roo.namespace('Roo.form');
10100 * @class Roo.form.Action
10101 * Internal Class used to handle form actions
10103 * @param {Roo.form.BasicForm} el The form element or its id
10104 * @param {Object} config Configuration options
10109 // define the action interface
10110 Roo.form.Action = function(form, options){
10112 this.options = options || {};
10115 * Client Validation Failed
10118 Roo.form.Action.CLIENT_INVALID = 'client';
10120 * Server Validation Failed
10123 Roo.form.Action.SERVER_INVALID = 'server';
10125 * Connect to Server Failed
10128 Roo.form.Action.CONNECT_FAILURE = 'connect';
10130 * Reading Data from Server Failed
10133 Roo.form.Action.LOAD_FAILURE = 'load';
10135 Roo.form.Action.prototype = {
10137 failureType : undefined,
10138 response : undefined,
10139 result : undefined,
10141 // interface method
10142 run : function(options){
10146 // interface method
10147 success : function(response){
10151 // interface method
10152 handleResponse : function(response){
10156 // default connection failure
10157 failure : function(response){
10159 this.response = response;
10160 this.failureType = Roo.form.Action.CONNECT_FAILURE;
10161 this.form.afterAction(this, false);
10164 processResponse : function(response){
10165 this.response = response;
10166 if(!response.responseText){
10169 this.result = this.handleResponse(response);
10170 return this.result;
10173 // utility functions used internally
10174 getUrl : function(appendParams){
10175 var url = this.options.url || this.form.url || this.form.el.dom.action;
10177 var p = this.getParams();
10179 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
10185 getMethod : function(){
10186 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
10189 getParams : function(){
10190 var bp = this.form.baseParams;
10191 var p = this.options.params;
10193 if(typeof p == "object"){
10194 p = Roo.urlEncode(Roo.applyIf(p, bp));
10195 }else if(typeof p == 'string' && bp){
10196 p += '&' + Roo.urlEncode(bp);
10199 p = Roo.urlEncode(bp);
10204 createCallback : function(){
10206 success: this.success,
10207 failure: this.failure,
10209 timeout: (this.form.timeout*1000),
10210 upload: this.form.fileUpload ? this.success : undefined
10215 Roo.form.Action.Submit = function(form, options){
10216 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
10219 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
10222 haveProgress : false,
10223 uploadComplete : false,
10225 // uploadProgress indicator.
10226 uploadProgress : function()
10228 if (!this.form.progressUrl) {
10232 if (!this.haveProgress) {
10233 Roo.MessageBox.progress("Uploading", "Uploading");
10235 if (this.uploadComplete) {
10236 Roo.MessageBox.hide();
10240 this.haveProgress = true;
10242 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
10244 var c = new Roo.data.Connection();
10246 url : this.form.progressUrl,
10251 success : function(req){
10252 //console.log(data);
10256 rdata = Roo.decode(req.responseText)
10258 Roo.log("Invalid data from server..");
10262 if (!rdata || !rdata.success) {
10264 Roo.MessageBox.alert(Roo.encode(rdata));
10267 var data = rdata.data;
10269 if (this.uploadComplete) {
10270 Roo.MessageBox.hide();
10275 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
10276 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
10279 this.uploadProgress.defer(2000,this);
10282 failure: function(data) {
10283 Roo.log('progress url failed ');
10294 // run get Values on the form, so it syncs any secondary forms.
10295 this.form.getValues();
10297 var o = this.options;
10298 var method = this.getMethod();
10299 var isPost = method == 'POST';
10300 if(o.clientValidation === false || this.form.isValid()){
10302 if (this.form.progressUrl) {
10303 this.form.findField('UPLOAD_IDENTIFIER').setValue(
10304 (new Date() * 1) + '' + Math.random());
10309 Roo.Ajax.request(Roo.apply(this.createCallback(), {
10310 form:this.form.el.dom,
10311 url:this.getUrl(!isPost),
10313 params:isPost ? this.getParams() : null,
10314 isUpload: this.form.fileUpload,
10315 formData : this.form.formData
10318 this.uploadProgress();
10320 }else if (o.clientValidation !== false){ // client validation failed
10321 this.failureType = Roo.form.Action.CLIENT_INVALID;
10322 this.form.afterAction(this, false);
10326 success : function(response)
10328 this.uploadComplete= true;
10329 if (this.haveProgress) {
10330 Roo.MessageBox.hide();
10334 var result = this.processResponse(response);
10335 if(result === true || result.success){
10336 this.form.afterAction(this, true);
10340 this.form.markInvalid(result.errors);
10341 this.failureType = Roo.form.Action.SERVER_INVALID;
10343 this.form.afterAction(this, false);
10345 failure : function(response)
10347 this.uploadComplete= true;
10348 if (this.haveProgress) {
10349 Roo.MessageBox.hide();
10352 this.response = response;
10353 this.failureType = Roo.form.Action.CONNECT_FAILURE;
10354 this.form.afterAction(this, false);
10357 handleResponse : function(response){
10358 if(this.form.errorReader){
10359 var rs = this.form.errorReader.read(response);
10362 for(var i = 0, len = rs.records.length; i < len; i++) {
10363 var r = rs.records[i];
10364 errors[i] = r.data;
10367 if(errors.length < 1){
10371 success : rs.success,
10377 ret = Roo.decode(response.responseText);
10381 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
10391 Roo.form.Action.Load = function(form, options){
10392 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
10393 this.reader = this.form.reader;
10396 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
10401 Roo.Ajax.request(Roo.apply(
10402 this.createCallback(), {
10403 method:this.getMethod(),
10404 url:this.getUrl(false),
10405 params:this.getParams()
10409 success : function(response){
10411 var result = this.processResponse(response);
10412 if(result === true || !result.success || !result.data){
10413 this.failureType = Roo.form.Action.LOAD_FAILURE;
10414 this.form.afterAction(this, false);
10417 this.form.clearInvalid();
10418 this.form.setValues(result.data);
10419 this.form.afterAction(this, true);
10422 handleResponse : function(response){
10423 if(this.form.reader){
10424 var rs = this.form.reader.read(response);
10425 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
10427 success : rs.success,
10431 return Roo.decode(response.responseText);
10435 Roo.form.Action.ACTION_TYPES = {
10436 'load' : Roo.form.Action.Load,
10437 'submit' : Roo.form.Action.Submit
10446 * @class Roo.bootstrap.Form
10447 * @extends Roo.bootstrap.Component
10448 * Bootstrap Form class
10449 * @cfg {String} method GET | POST (default POST)
10450 * @cfg {String} labelAlign top | left (default top)
10451 * @cfg {String} align left | right - for navbars
10452 * @cfg {Boolean} loadMask load mask when submit (default true)
10456 * Create a new Form
10457 * @param {Object} config The config object
10461 Roo.bootstrap.Form = function(config){
10463 Roo.bootstrap.Form.superclass.constructor.call(this, config);
10465 Roo.bootstrap.Form.popover.apply();
10469 * @event clientvalidation
10470 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
10471 * @param {Form} this
10472 * @param {Boolean} valid true if the form has passed client-side validation
10474 clientvalidation: true,
10476 * @event beforeaction
10477 * Fires before any action is performed. Return false to cancel the action.
10478 * @param {Form} this
10479 * @param {Action} action The action to be performed
10481 beforeaction: true,
10483 * @event actionfailed
10484 * Fires when an action fails.
10485 * @param {Form} this
10486 * @param {Action} action The action that failed
10488 actionfailed : true,
10490 * @event actioncomplete
10491 * Fires when an action is completed.
10492 * @param {Form} this
10493 * @param {Action} action The action that completed
10495 actioncomplete : true
10499 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
10502 * @cfg {String} method
10503 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
10507 * @cfg {String} url
10508 * The URL to use for form actions if one isn't supplied in the action options.
10511 * @cfg {Boolean} fileUpload
10512 * Set to true if this form is a file upload.
10516 * @cfg {Object} baseParams
10517 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
10521 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
10525 * @cfg {Sting} align (left|right) for navbar forms
10530 activeAction : null,
10533 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
10534 * element by passing it or its id or mask the form itself by passing in true.
10537 waitMsgTarget : false,
10542 * @cfg {Boolean} errorMask (true|false) default false
10547 * @cfg {Number} maskOffset Default 100
10552 * @cfg {Boolean} maskBody
10556 getAutoCreate : function(){
10560 method : this.method || 'POST',
10561 id : this.id || Roo.id(),
10564 if (this.parent().xtype.match(/^Nav/)) {
10565 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
10569 if (this.labelAlign == 'left' ) {
10570 cfg.cls += ' form-horizontal';
10576 initEvents : function()
10578 this.el.on('submit', this.onSubmit, this);
10579 // this was added as random key presses on the form where triggering form submit.
10580 this.el.on('keypress', function(e) {
10581 if (e.getCharCode() != 13) {
10584 // we might need to allow it for textareas.. and some other items.
10585 // check e.getTarget().
10587 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
10591 Roo.log("keypress blocked");
10593 e.preventDefault();
10599 onSubmit : function(e){
10604 * Returns true if client-side validation on the form is successful.
10607 isValid : function(){
10608 var items = this.getItems();
10610 var target = false;
10612 items.each(function(f){
10618 Roo.log('invalid field: ' + f.name);
10622 if(!target && f.el.isVisible(true)){
10628 if(this.errorMask && !valid){
10629 Roo.bootstrap.Form.popover.mask(this, target);
10636 * Returns true if any fields in this form have changed since their original load.
10639 isDirty : function(){
10641 var items = this.getItems();
10642 items.each(function(f){
10652 * Performs a predefined action (submit or load) or custom actions you define on this form.
10653 * @param {String} actionName The name of the action type
10654 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
10655 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
10656 * accept other config options):
10658 Property Type Description
10659 ---------------- --------------- ----------------------------------------------------------------------------------
10660 url String The url for the action (defaults to the form's url)
10661 method String The form method to use (defaults to the form's method, or POST if not defined)
10662 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
10663 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
10664 validate the form on the client (defaults to false)
10666 * @return {BasicForm} this
10668 doAction : function(action, options){
10669 if(typeof action == 'string'){
10670 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
10672 if(this.fireEvent('beforeaction', this, action) !== false){
10673 this.beforeAction(action);
10674 action.run.defer(100, action);
10680 beforeAction : function(action){
10681 var o = action.options;
10686 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
10688 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10691 // not really supported yet.. ??
10693 //if(this.waitMsgTarget === true){
10694 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10695 //}else if(this.waitMsgTarget){
10696 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
10697 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
10699 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
10705 afterAction : function(action, success){
10706 this.activeAction = null;
10707 var o = action.options;
10712 Roo.get(document.body).unmask();
10718 //if(this.waitMsgTarget === true){
10719 // this.el.unmask();
10720 //}else if(this.waitMsgTarget){
10721 // this.waitMsgTarget.unmask();
10723 // Roo.MessageBox.updateProgress(1);
10724 // Roo.MessageBox.hide();
10731 Roo.callback(o.success, o.scope, [this, action]);
10732 this.fireEvent('actioncomplete', this, action);
10736 // failure condition..
10737 // we have a scenario where updates need confirming.
10738 // eg. if a locking scenario exists..
10739 // we look for { errors : { needs_confirm : true }} in the response.
10741 (typeof(action.result) != 'undefined') &&
10742 (typeof(action.result.errors) != 'undefined') &&
10743 (typeof(action.result.errors.needs_confirm) != 'undefined')
10746 Roo.log("not supported yet");
10749 Roo.MessageBox.confirm(
10750 "Change requires confirmation",
10751 action.result.errorMsg,
10756 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
10766 Roo.callback(o.failure, o.scope, [this, action]);
10767 // show an error message if no failed handler is set..
10768 if (!this.hasListener('actionfailed')) {
10769 Roo.log("need to add dialog support");
10771 Roo.MessageBox.alert("Error",
10772 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
10773 action.result.errorMsg :
10774 "Saving Failed, please check your entries or try again"
10779 this.fireEvent('actionfailed', this, action);
10784 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
10785 * @param {String} id The value to search for
10788 findField : function(id){
10789 var items = this.getItems();
10790 var field = items.get(id);
10792 items.each(function(f){
10793 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
10800 return field || null;
10803 * Mark fields in this form invalid in bulk.
10804 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
10805 * @return {BasicForm} this
10807 markInvalid : function(errors){
10808 if(errors instanceof Array){
10809 for(var i = 0, len = errors.length; i < len; i++){
10810 var fieldError = errors[i];
10811 var f = this.findField(fieldError.id);
10813 f.markInvalid(fieldError.msg);
10819 if(typeof errors[id] != 'function' && (field = this.findField(id))){
10820 field.markInvalid(errors[id]);
10824 //Roo.each(this.childForms || [], function (f) {
10825 // f.markInvalid(errors);
10832 * Set values for fields in this form in bulk.
10833 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
10834 * @return {BasicForm} this
10836 setValues : function(values){
10837 if(values instanceof Array){ // array of objects
10838 for(var i = 0, len = values.length; i < len; i++){
10840 var f = this.findField(v.id);
10842 f.setValue(v.value);
10843 if(this.trackResetOnLoad){
10844 f.originalValue = f.getValue();
10848 }else{ // object hash
10851 if(typeof values[id] != 'function' && (field = this.findField(id))){
10853 if (field.setFromData &&
10854 field.valueField &&
10855 field.displayField &&
10856 // combos' with local stores can
10857 // be queried via setValue()
10858 // to set their value..
10859 (field.store && !field.store.isLocal)
10863 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
10864 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
10865 field.setFromData(sd);
10867 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
10869 field.setFromData(values);
10872 field.setValue(values[id]);
10876 if(this.trackResetOnLoad){
10877 field.originalValue = field.getValue();
10883 //Roo.each(this.childForms || [], function (f) {
10884 // f.setValues(values);
10891 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
10892 * they are returned as an array.
10893 * @param {Boolean} asString
10896 getValues : function(asString){
10897 //if (this.childForms) {
10898 // copy values from the child forms
10899 // Roo.each(this.childForms, function (f) {
10900 // this.setValues(f.getValues());
10906 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10907 if(asString === true){
10910 return Roo.urlDecode(fs);
10914 * Returns the fields in this form as an object with key/value pairs.
10915 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10918 getFieldValues : function(with_hidden)
10920 var items = this.getItems();
10922 items.each(function(f){
10924 if (!f.getName()) {
10928 var v = f.getValue();
10930 if (f.inputType =='radio') {
10931 if (typeof(ret[f.getName()]) == 'undefined') {
10932 ret[f.getName()] = ''; // empty..
10935 if (!f.el.dom.checked) {
10939 v = f.el.dom.value;
10943 if(f.xtype == 'MoneyField'){
10944 ret[f.currencyName] = f.getCurrency();
10947 // not sure if this supported any more..
10948 if ((typeof(v) == 'object') && f.getRawValue) {
10949 v = f.getRawValue() ; // dates..
10951 // combo boxes where name != hiddenName...
10952 if (f.name !== false && f.name != '' && f.name != f.getName()) {
10953 ret[f.name] = f.getRawValue();
10955 ret[f.getName()] = v;
10962 * Clears all invalid messages in this form.
10963 * @return {BasicForm} this
10965 clearInvalid : function(){
10966 var items = this.getItems();
10968 items.each(function(f){
10976 * Resets this form.
10977 * @return {BasicForm} this
10979 reset : function(){
10980 var items = this.getItems();
10981 items.each(function(f){
10985 Roo.each(this.childForms || [], function (f) {
10993 getItems : function()
10995 var r=new Roo.util.MixedCollection(false, function(o){
10996 return o.id || (o.id = Roo.id());
10998 var iter = function(el) {
11005 Roo.each(el.items,function(e) {
11014 hideFields : function(items)
11016 Roo.each(items, function(i){
11018 var f = this.findField(i);
11029 showFields : function(items)
11031 Roo.each(items, function(i){
11033 var f = this.findField(i);
11046 Roo.apply(Roo.bootstrap.Form, {
11062 intervalID : false,
11068 if(this.isApplied){
11073 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11074 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11075 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11076 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11079 this.maskEl.top.enableDisplayMode("block");
11080 this.maskEl.left.enableDisplayMode("block");
11081 this.maskEl.bottom.enableDisplayMode("block");
11082 this.maskEl.right.enableDisplayMode("block");
11084 this.toolTip = new Roo.bootstrap.Tooltip({
11085 cls : 'roo-form-error-popover',
11087 'left' : ['r-l', [-2,0], 'right'],
11088 'right' : ['l-r', [2,0], 'left'],
11089 'bottom' : ['tl-bl', [0,2], 'top'],
11090 'top' : [ 'bl-tl', [0,-2], 'bottom']
11094 this.toolTip.render(Roo.get(document.body));
11096 this.toolTip.el.enableDisplayMode("block");
11098 Roo.get(document.body).on('click', function(){
11102 Roo.get(document.body).on('touchstart', function(){
11106 this.isApplied = true
11109 mask : function(form, target)
11113 this.target = target;
11115 if(!this.form.errorMask || !target.el){
11119 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
11121 Roo.log(scrollable);
11123 var ot = this.target.el.calcOffsetsTo(scrollable);
11125 var scrollTo = ot[1] - this.form.maskOffset;
11127 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
11129 scrollable.scrollTo('top', scrollTo);
11131 var box = this.target.el.getBox();
11133 var zIndex = Roo.bootstrap.Modal.zIndex++;
11136 this.maskEl.top.setStyle('position', 'absolute');
11137 this.maskEl.top.setStyle('z-index', zIndex);
11138 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
11139 this.maskEl.top.setLeft(0);
11140 this.maskEl.top.setTop(0);
11141 this.maskEl.top.show();
11143 this.maskEl.left.setStyle('position', 'absolute');
11144 this.maskEl.left.setStyle('z-index', zIndex);
11145 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
11146 this.maskEl.left.setLeft(0);
11147 this.maskEl.left.setTop(box.y - this.padding);
11148 this.maskEl.left.show();
11150 this.maskEl.bottom.setStyle('position', 'absolute');
11151 this.maskEl.bottom.setStyle('z-index', zIndex);
11152 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
11153 this.maskEl.bottom.setLeft(0);
11154 this.maskEl.bottom.setTop(box.bottom + this.padding);
11155 this.maskEl.bottom.show();
11157 this.maskEl.right.setStyle('position', 'absolute');
11158 this.maskEl.right.setStyle('z-index', zIndex);
11159 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
11160 this.maskEl.right.setLeft(box.right + this.padding);
11161 this.maskEl.right.setTop(box.y - this.padding);
11162 this.maskEl.right.show();
11164 this.toolTip.bindEl = this.target.el;
11166 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
11168 var tip = this.target.blankText;
11170 if(this.target.getValue() !== '' ) {
11172 if (this.target.invalidText.length) {
11173 tip = this.target.invalidText;
11174 } else if (this.target.regexText.length){
11175 tip = this.target.regexText;
11179 this.toolTip.show(tip);
11181 this.intervalID = window.setInterval(function() {
11182 Roo.bootstrap.Form.popover.unmask();
11185 window.onwheel = function(){ return false;};
11187 (function(){ this.isMasked = true; }).defer(500, this);
11191 unmask : function()
11193 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
11197 this.maskEl.top.setStyle('position', 'absolute');
11198 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
11199 this.maskEl.top.hide();
11201 this.maskEl.left.setStyle('position', 'absolute');
11202 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
11203 this.maskEl.left.hide();
11205 this.maskEl.bottom.setStyle('position', 'absolute');
11206 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
11207 this.maskEl.bottom.hide();
11209 this.maskEl.right.setStyle('position', 'absolute');
11210 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
11211 this.maskEl.right.hide();
11213 this.toolTip.hide();
11215 this.toolTip.el.hide();
11217 window.onwheel = function(){ return true;};
11219 if(this.intervalID){
11220 window.clearInterval(this.intervalID);
11221 this.intervalID = false;
11224 this.isMasked = false;
11234 * Ext JS Library 1.1.1
11235 * Copyright(c) 2006-2007, Ext JS, LLC.
11237 * Originally Released Under LGPL - original licence link has changed is not relivant.
11240 * <script type="text/javascript">
11243 * @class Roo.form.VTypes
11244 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
11247 Roo.form.VTypes = function(){
11248 // closure these in so they are only created once.
11249 var alpha = /^[a-zA-Z_]+$/;
11250 var alphanum = /^[a-zA-Z0-9_]+$/;
11251 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
11252 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
11254 // All these messages and functions are configurable
11257 * The function used to validate email addresses
11258 * @param {String} value The email address
11260 'email' : function(v){
11261 return email.test(v);
11264 * The error text to display when the email validation function returns false
11267 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
11269 * The keystroke filter mask to be applied on email input
11272 'emailMask' : /[a-z0-9_\.\-@]/i,
11275 * The function used to validate URLs
11276 * @param {String} value The URL
11278 'url' : function(v){
11279 return url.test(v);
11282 * The error text to display when the url validation function returns false
11285 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
11288 * The function used to validate alpha values
11289 * @param {String} value The value
11291 'alpha' : function(v){
11292 return alpha.test(v);
11295 * The error text to display when the alpha validation function returns false
11298 'alphaText' : 'This field should only contain letters and _',
11300 * The keystroke filter mask to be applied on alpha input
11303 'alphaMask' : /[a-z_]/i,
11306 * The function used to validate alphanumeric values
11307 * @param {String} value The value
11309 'alphanum' : function(v){
11310 return alphanum.test(v);
11313 * The error text to display when the alphanumeric validation function returns false
11316 'alphanumText' : 'This field should only contain letters, numbers and _',
11318 * The keystroke filter mask to be applied on alphanumeric input
11321 'alphanumMask' : /[a-z0-9_]/i
11331 * @class Roo.bootstrap.Input
11332 * @extends Roo.bootstrap.Component
11333 * Bootstrap Input class
11334 * @cfg {Boolean} disabled is it disabled
11335 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
11336 * @cfg {String} name name of the input
11337 * @cfg {string} fieldLabel - the label associated
11338 * @cfg {string} placeholder - placeholder to put in text.
11339 * @cfg {string} before - input group add on before
11340 * @cfg {string} after - input group add on after
11341 * @cfg {string} size - (lg|sm) or leave empty..
11342 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
11343 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
11344 * @cfg {Number} md colspan out of 12 for computer-sized screens
11345 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
11346 * @cfg {string} value default value of the input
11347 * @cfg {Number} labelWidth set the width of label
11348 * @cfg {Number} labellg set the width of label (1-12)
11349 * @cfg {Number} labelmd set the width of label (1-12)
11350 * @cfg {Number} labelsm set the width of label (1-12)
11351 * @cfg {Number} labelxs set the width of label (1-12)
11352 * @cfg {String} labelAlign (top|left)
11353 * @cfg {Boolean} readOnly Specifies that the field should be read-only
11354 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
11355 * @cfg {String} indicatorpos (left|right) default left
11356 * @cfg {String} capture (user|camera) use for file input only. (default empty)
11357 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
11358 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
11360 * @cfg {String} align (left|center|right) Default left
11361 * @cfg {Boolean} forceFeedback (true|false) Default false
11364 * Create a new Input
11365 * @param {Object} config The config object
11368 Roo.bootstrap.Input = function(config){
11370 Roo.bootstrap.Input.superclass.constructor.call(this, config);
11375 * Fires when this field receives input focus.
11376 * @param {Roo.form.Field} this
11381 * Fires when this field loses input focus.
11382 * @param {Roo.form.Field} this
11386 * @event specialkey
11387 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
11388 * {@link Roo.EventObject#getKey} to determine which key was pressed.
11389 * @param {Roo.form.Field} this
11390 * @param {Roo.EventObject} e The event object
11395 * Fires just before the field blurs if the field value has changed.
11396 * @param {Roo.form.Field} this
11397 * @param {Mixed} newValue The new value
11398 * @param {Mixed} oldValue The original value
11403 * Fires after the field has been marked as invalid.
11404 * @param {Roo.form.Field} this
11405 * @param {String} msg The validation message
11410 * Fires after the field has been validated with no errors.
11411 * @param {Roo.form.Field} this
11416 * Fires after the key up
11417 * @param {Roo.form.Field} this
11418 * @param {Roo.EventObject} e The event Object
11423 * Fires after the user pastes into input
11424 * @param {Roo.form.Field} this
11425 * @param {Roo.EventObject} e The event Object
11431 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
11433 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
11434 automatic validation (defaults to "keyup").
11436 validationEvent : "keyup",
11438 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
11440 validateOnBlur : true,
11442 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
11444 validationDelay : 250,
11446 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
11448 focusClass : "x-form-focus", // not needed???
11452 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11454 invalidClass : "has-warning",
11457 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11459 validClass : "has-success",
11462 * @cfg {Boolean} hasFeedback (true|false) default true
11464 hasFeedback : true,
11467 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11469 invalidFeedbackClass : "glyphicon-warning-sign",
11472 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11474 validFeedbackClass : "glyphicon-ok",
11477 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
11479 selectOnFocus : false,
11482 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
11486 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
11491 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
11493 disableKeyFilter : false,
11496 * @cfg {Boolean} disabled True to disable the field (defaults to false).
11500 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
11504 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
11506 blankText : "Please complete this mandatory field",
11509 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
11513 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
11515 maxLength : Number.MAX_VALUE,
11517 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
11519 minLengthText : "The minimum length for this field is {0}",
11521 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
11523 maxLengthText : "The maximum length for this field is {0}",
11527 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
11528 * If available, this function will be called only after the basic validators all return true, and will be passed the
11529 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
11533 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
11534 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
11535 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
11539 * @cfg {String} regexText -- Depricated - use Invalid Text
11544 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
11550 autocomplete: false,
11554 inputType : 'text',
11557 placeholder: false,
11562 preventMark: false,
11563 isFormField : true,
11566 labelAlign : false,
11569 formatedValue : false,
11570 forceFeedback : false,
11572 indicatorpos : 'left',
11582 parentLabelAlign : function()
11585 while (parent.parent()) {
11586 parent = parent.parent();
11587 if (typeof(parent.labelAlign) !='undefined') {
11588 return parent.labelAlign;
11595 getAutoCreate : function()
11597 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11603 if(this.inputType != 'hidden'){
11604 cfg.cls = 'form-group' //input-group
11610 type : this.inputType,
11611 value : this.value,
11612 cls : 'form-control',
11613 placeholder : this.placeholder || '',
11614 autocomplete : this.autocomplete || 'new-password'
11616 if (this.inputType == 'file') {
11617 input.style = 'overflow:hidden'; // why not in CSS?
11620 if(this.capture.length){
11621 input.capture = this.capture;
11624 if(this.accept.length){
11625 input.accept = this.accept + "/*";
11629 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
11632 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11633 input.maxLength = this.maxLength;
11636 if (this.disabled) {
11637 input.disabled=true;
11640 if (this.readOnly) {
11641 input.readonly=true;
11645 input.name = this.name;
11649 input.cls += ' input-' + this.size;
11653 ['xs','sm','md','lg'].map(function(size){
11654 if (settings[size]) {
11655 cfg.cls += ' col-' + size + '-' + settings[size];
11659 var inputblock = input;
11663 cls: 'glyphicon form-control-feedback'
11666 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11669 cls : 'has-feedback',
11677 if (this.before || this.after) {
11680 cls : 'input-group',
11684 if (this.before && typeof(this.before) == 'string') {
11686 inputblock.cn.push({
11688 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
11692 if (this.before && typeof(this.before) == 'object') {
11693 this.before = Roo.factory(this.before);
11695 inputblock.cn.push({
11697 cls : 'roo-input-before input-group-prepend input-group-' +
11698 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
11702 inputblock.cn.push(input);
11704 if (this.after && typeof(this.after) == 'string') {
11705 inputblock.cn.push({
11707 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
11711 if (this.after && typeof(this.after) == 'object') {
11712 this.after = Roo.factory(this.after);
11714 inputblock.cn.push({
11716 cls : 'roo-input-after input-group-append input-group-' +
11717 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
11721 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11722 inputblock.cls += ' has-feedback';
11723 inputblock.cn.push(feedback);
11728 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11729 tooltip : 'This field is required'
11731 if (this.allowBlank ) {
11732 indicator.style = this.allowBlank ? ' display:none' : '';
11734 if (align ==='left' && this.fieldLabel.length) {
11736 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
11743 cls : 'control-label col-form-label',
11744 html : this.fieldLabel
11755 var labelCfg = cfg.cn[1];
11756 var contentCfg = cfg.cn[2];
11758 if(this.indicatorpos == 'right'){
11763 cls : 'control-label col-form-label',
11767 html : this.fieldLabel
11781 labelCfg = cfg.cn[0];
11782 contentCfg = cfg.cn[1];
11786 if(this.labelWidth > 12){
11787 labelCfg.style = "width: " + this.labelWidth + 'px';
11790 if(this.labelWidth < 13 && this.labelmd == 0){
11791 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
11794 if(this.labellg > 0){
11795 labelCfg.cls += ' col-lg-' + this.labellg;
11796 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11799 if(this.labelmd > 0){
11800 labelCfg.cls += ' col-md-' + this.labelmd;
11801 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11804 if(this.labelsm > 0){
11805 labelCfg.cls += ' col-sm-' + this.labelsm;
11806 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11809 if(this.labelxs > 0){
11810 labelCfg.cls += ' col-xs-' + this.labelxs;
11811 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11815 } else if ( this.fieldLabel.length) {
11822 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
11823 tooltip : 'This field is required',
11824 style : this.allowBlank ? ' display:none' : ''
11828 //cls : 'input-group-addon',
11829 html : this.fieldLabel
11837 if(this.indicatorpos == 'right'){
11842 //cls : 'input-group-addon',
11843 html : this.fieldLabel
11848 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
11849 tooltip : 'This field is required',
11850 style : this.allowBlank ? ' display:none' : ''
11870 if (this.parentType === 'Navbar' && this.parent().bar) {
11871 cfg.cls += ' navbar-form';
11874 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
11875 // on BS4 we do this only if not form
11876 cfg.cls += ' navbar-form';
11884 * return the real input element.
11886 inputEl: function ()
11888 return this.el.select('input.form-control',true).first();
11891 tooltipEl : function()
11893 return this.inputEl();
11896 indicatorEl : function()
11898 if (Roo.bootstrap.version == 4) {
11899 return false; // not enabled in v4 yet.
11902 var indicator = this.el.select('i.roo-required-indicator',true).first();
11912 setDisabled : function(v)
11914 var i = this.inputEl().dom;
11916 i.removeAttribute('disabled');
11920 i.setAttribute('disabled','true');
11922 initEvents : function()
11925 this.inputEl().on("keydown" , this.fireKey, this);
11926 this.inputEl().on("focus", this.onFocus, this);
11927 this.inputEl().on("blur", this.onBlur, this);
11929 this.inputEl().relayEvent('keyup', this);
11930 this.inputEl().relayEvent('paste', this);
11932 this.indicator = this.indicatorEl();
11934 if(this.indicator){
11935 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
11938 // reference to original value for reset
11939 this.originalValue = this.getValue();
11940 //Roo.form.TextField.superclass.initEvents.call(this);
11941 if(this.validationEvent == 'keyup'){
11942 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11943 this.inputEl().on('keyup', this.filterValidation, this);
11945 else if(this.validationEvent !== false){
11946 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11949 if(this.selectOnFocus){
11950 this.on("focus", this.preFocus, this);
11953 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11954 this.inputEl().on("keypress", this.filterKeys, this);
11956 this.inputEl().relayEvent('keypress', this);
11959 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
11960 this.el.on("click", this.autoSize, this);
11963 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11964 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11967 if (typeof(this.before) == 'object') {
11968 this.before.render(this.el.select('.roo-input-before',true).first());
11970 if (typeof(this.after) == 'object') {
11971 this.after.render(this.el.select('.roo-input-after',true).first());
11974 this.inputEl().on('change', this.onChange, this);
11977 filterValidation : function(e){
11978 if(!e.isNavKeyPress()){
11979 this.validationTask.delay(this.validationDelay);
11983 * Validates the field value
11984 * @return {Boolean} True if the value is valid, else false
11986 validate : function(){
11987 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11988 if(this.disabled || this.validateValue(this.getRawValue())){
11993 this.markInvalid();
11999 * Validates a value according to the field's validation rules and marks the field as invalid
12000 * if the validation fails
12001 * @param {Mixed} value The value to validate
12002 * @return {Boolean} True if the value is valid, else false
12004 validateValue : function(value)
12006 if(this.getVisibilityEl().hasClass('hidden')){
12010 if(value.length < 1) { // if it's blank
12011 if(this.allowBlank){
12017 if(value.length < this.minLength){
12020 if(value.length > this.maxLength){
12024 var vt = Roo.form.VTypes;
12025 if(!vt[this.vtype](value, this)){
12029 if(typeof this.validator == "function"){
12030 var msg = this.validator(value);
12034 if (typeof(msg) == 'string') {
12035 this.invalidText = msg;
12039 if(this.regex && !this.regex.test(value)){
12047 fireKey : function(e){
12048 //Roo.log('field ' + e.getKey());
12049 if(e.isNavKeyPress()){
12050 this.fireEvent("specialkey", this, e);
12053 focus : function (selectText){
12055 this.inputEl().focus();
12056 if(selectText === true){
12057 this.inputEl().dom.select();
12063 onFocus : function(){
12064 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12065 // this.el.addClass(this.focusClass);
12067 if(!this.hasFocus){
12068 this.hasFocus = true;
12069 this.startValue = this.getValue();
12070 this.fireEvent("focus", this);
12074 beforeBlur : Roo.emptyFn,
12078 onBlur : function(){
12080 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12081 //this.el.removeClass(this.focusClass);
12083 this.hasFocus = false;
12084 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12087 var v = this.getValue();
12088 if(String(v) !== String(this.startValue)){
12089 this.fireEvent('change', this, v, this.startValue);
12091 this.fireEvent("blur", this);
12094 onChange : function(e)
12096 var v = this.getValue();
12097 if(String(v) !== String(this.startValue)){
12098 this.fireEvent('change', this, v, this.startValue);
12104 * Resets the current field value to the originally loaded value and clears any validation messages
12106 reset : function(){
12107 this.setValue(this.originalValue);
12111 * Returns the name of the field
12112 * @return {Mixed} name The name field
12114 getName: function(){
12118 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
12119 * @return {Mixed} value The field value
12121 getValue : function(){
12123 var v = this.inputEl().getValue();
12128 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
12129 * @return {Mixed} value The field value
12131 getRawValue : function(){
12132 var v = this.inputEl().getValue();
12138 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
12139 * @param {Mixed} value The value to set
12141 setRawValue : function(v){
12142 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12145 selectText : function(start, end){
12146 var v = this.getRawValue();
12148 start = start === undefined ? 0 : start;
12149 end = end === undefined ? v.length : end;
12150 var d = this.inputEl().dom;
12151 if(d.setSelectionRange){
12152 d.setSelectionRange(start, end);
12153 }else if(d.createTextRange){
12154 var range = d.createTextRange();
12155 range.moveStart("character", start);
12156 range.moveEnd("character", v.length-end);
12163 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
12164 * @param {Mixed} value The value to set
12166 setValue : function(v){
12169 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12175 processValue : function(value){
12176 if(this.stripCharsRe){
12177 var newValue = value.replace(this.stripCharsRe, '');
12178 if(newValue !== value){
12179 this.setRawValue(newValue);
12186 preFocus : function(){
12188 if(this.selectOnFocus){
12189 this.inputEl().dom.select();
12192 filterKeys : function(e){
12193 var k = e.getKey();
12194 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
12197 var c = e.getCharCode(), cc = String.fromCharCode(c);
12198 if(Roo.isIE && (e.isSpecialKey() || !cc)){
12201 if(!this.maskRe.test(cc)){
12206 * Clear any invalid styles/messages for this field
12208 clearInvalid : function(){
12210 if(!this.el || this.preventMark){ // not rendered
12215 this.el.removeClass([this.invalidClass, 'is-invalid']);
12217 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12219 var feedback = this.el.select('.form-control-feedback', true).first();
12222 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12227 if(this.indicator){
12228 this.indicator.removeClass('visible');
12229 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12232 this.fireEvent('valid', this);
12236 * Mark this field as valid
12238 markValid : function()
12240 if(!this.el || this.preventMark){ // not rendered...
12244 this.el.removeClass([this.invalidClass, this.validClass]);
12245 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12247 var feedback = this.el.select('.form-control-feedback', true).first();
12250 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12253 if(this.indicator){
12254 this.indicator.removeClass('visible');
12255 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12263 if(this.allowBlank && !this.getRawValue().length){
12266 if (Roo.bootstrap.version == 3) {
12267 this.el.addClass(this.validClass);
12269 this.inputEl().addClass('is-valid');
12272 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12274 var feedback = this.el.select('.form-control-feedback', true).first();
12277 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12278 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12283 this.fireEvent('valid', this);
12287 * Mark this field as invalid
12288 * @param {String} msg The validation message
12290 markInvalid : function(msg)
12292 if(!this.el || this.preventMark){ // not rendered
12296 this.el.removeClass([this.invalidClass, this.validClass]);
12297 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12299 var feedback = this.el.select('.form-control-feedback', true).first();
12302 this.el.select('.form-control-feedback', true).first().removeClass(
12303 [this.invalidFeedbackClass, this.validFeedbackClass]);
12310 if(this.allowBlank && !this.getRawValue().length){
12314 if(this.indicator){
12315 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12316 this.indicator.addClass('visible');
12318 if (Roo.bootstrap.version == 3) {
12319 this.el.addClass(this.invalidClass);
12321 this.inputEl().addClass('is-invalid');
12326 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12328 var feedback = this.el.select('.form-control-feedback', true).first();
12331 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12333 if(this.getValue().length || this.forceFeedback){
12334 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12341 this.fireEvent('invalid', this, msg);
12344 SafariOnKeyDown : function(event)
12346 // this is a workaround for a password hang bug on chrome/ webkit.
12347 if (this.inputEl().dom.type != 'password') {
12351 var isSelectAll = false;
12353 if(this.inputEl().dom.selectionEnd > 0){
12354 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
12356 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
12357 event.preventDefault();
12362 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
12364 event.preventDefault();
12365 // this is very hacky as keydown always get's upper case.
12367 var cc = String.fromCharCode(event.getCharCode());
12368 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
12372 adjustWidth : function(tag, w){
12373 tag = tag.toLowerCase();
12374 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
12375 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
12376 if(tag == 'input'){
12379 if(tag == 'textarea'){
12382 }else if(Roo.isOpera){
12383 if(tag == 'input'){
12386 if(tag == 'textarea'){
12394 setFieldLabel : function(v)
12396 if(!this.rendered){
12400 if(this.indicatorEl()){
12401 var ar = this.el.select('label > span',true);
12403 if (ar.elements.length) {
12404 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12405 this.fieldLabel = v;
12409 var br = this.el.select('label',true);
12411 if(br.elements.length) {
12412 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12413 this.fieldLabel = v;
12417 Roo.log('Cannot Found any of label > span || label in input');
12421 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12422 this.fieldLabel = v;
12437 * @class Roo.bootstrap.TextArea
12438 * @extends Roo.bootstrap.Input
12439 * Bootstrap TextArea class
12440 * @cfg {Number} cols Specifies the visible width of a text area
12441 * @cfg {Number} rows Specifies the visible number of lines in a text area
12442 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
12443 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
12444 * @cfg {string} html text
12447 * Create a new TextArea
12448 * @param {Object} config The config object
12451 Roo.bootstrap.TextArea = function(config){
12452 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
12456 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
12466 getAutoCreate : function(){
12468 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12474 if(this.inputType != 'hidden'){
12475 cfg.cls = 'form-group' //input-group
12483 value : this.value || '',
12484 html: this.html || '',
12485 cls : 'form-control',
12486 placeholder : this.placeholder || ''
12490 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12491 input.maxLength = this.maxLength;
12495 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
12499 input.cols = this.cols;
12502 if (this.readOnly) {
12503 input.readonly = true;
12507 input.name = this.name;
12511 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
12515 ['xs','sm','md','lg'].map(function(size){
12516 if (settings[size]) {
12517 cfg.cls += ' col-' + size + '-' + settings[size];
12521 var inputblock = input;
12523 if(this.hasFeedback && !this.allowBlank){
12527 cls: 'glyphicon form-control-feedback'
12531 cls : 'has-feedback',
12540 if (this.before || this.after) {
12543 cls : 'input-group',
12547 inputblock.cn.push({
12549 cls : 'input-group-addon',
12554 inputblock.cn.push(input);
12556 if(this.hasFeedback && !this.allowBlank){
12557 inputblock.cls += ' has-feedback';
12558 inputblock.cn.push(feedback);
12562 inputblock.cn.push({
12564 cls : 'input-group-addon',
12571 if (align ==='left' && this.fieldLabel.length) {
12576 cls : 'control-label',
12577 html : this.fieldLabel
12588 if(this.labelWidth > 12){
12589 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
12592 if(this.labelWidth < 13 && this.labelmd == 0){
12593 this.labelmd = this.labelWidth;
12596 if(this.labellg > 0){
12597 cfg.cn[0].cls += ' col-lg-' + this.labellg;
12598 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
12601 if(this.labelmd > 0){
12602 cfg.cn[0].cls += ' col-md-' + this.labelmd;
12603 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
12606 if(this.labelsm > 0){
12607 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
12608 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
12611 if(this.labelxs > 0){
12612 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
12613 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
12616 } else if ( this.fieldLabel.length) {
12621 //cls : 'input-group-addon',
12622 html : this.fieldLabel
12640 if (this.disabled) {
12641 input.disabled=true;
12648 * return the real textarea element.
12650 inputEl: function ()
12652 return this.el.select('textarea.form-control',true).first();
12656 * Clear any invalid styles/messages for this field
12658 clearInvalid : function()
12661 if(!this.el || this.preventMark){ // not rendered
12665 var label = this.el.select('label', true).first();
12666 var icon = this.el.select('i.fa-star', true).first();
12671 this.el.removeClass( this.validClass);
12672 this.inputEl().removeClass('is-invalid');
12674 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12676 var feedback = this.el.select('.form-control-feedback', true).first();
12679 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12684 this.fireEvent('valid', this);
12688 * Mark this field as valid
12690 markValid : function()
12692 if(!this.el || this.preventMark){ // not rendered
12696 this.el.removeClass([this.invalidClass, this.validClass]);
12697 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12699 var feedback = this.el.select('.form-control-feedback', true).first();
12702 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12705 if(this.disabled || this.allowBlank){
12709 var label = this.el.select('label', true).first();
12710 var icon = this.el.select('i.fa-star', true).first();
12715 if (Roo.bootstrap.version == 3) {
12716 this.el.addClass(this.validClass);
12718 this.inputEl().addClass('is-valid');
12722 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12724 var feedback = this.el.select('.form-control-feedback', true).first();
12727 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12728 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12733 this.fireEvent('valid', this);
12737 * Mark this field as invalid
12738 * @param {String} msg The validation message
12740 markInvalid : function(msg)
12742 if(!this.el || this.preventMark){ // not rendered
12746 this.el.removeClass([this.invalidClass, this.validClass]);
12747 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12749 var feedback = this.el.select('.form-control-feedback', true).first();
12752 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12755 if(this.disabled || this.allowBlank){
12759 var label = this.el.select('label', true).first();
12760 var icon = this.el.select('i.fa-star', true).first();
12762 if(!this.getValue().length && label && !icon){
12763 this.el.createChild({
12765 cls : 'text-danger fa fa-lg fa-star',
12766 tooltip : 'This field is required',
12767 style : 'margin-right:5px;'
12771 if (Roo.bootstrap.version == 3) {
12772 this.el.addClass(this.invalidClass);
12774 this.inputEl().addClass('is-invalid');
12777 // fixme ... this may be depricated need to test..
12778 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12780 var feedback = this.el.select('.form-control-feedback', true).first();
12783 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12785 if(this.getValue().length || this.forceFeedback){
12786 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12793 this.fireEvent('invalid', this, msg);
12801 * trigger field - base class for combo..
12806 * @class Roo.bootstrap.TriggerField
12807 * @extends Roo.bootstrap.Input
12808 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
12809 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
12810 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
12811 * for which you can provide a custom implementation. For example:
12813 var trigger = new Roo.bootstrap.TriggerField();
12814 trigger.onTriggerClick = myTriggerFn;
12815 trigger.applyTo('my-field');
12818 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
12819 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
12820 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
12821 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
12822 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
12825 * Create a new TriggerField.
12826 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
12827 * to the base TextField)
12829 Roo.bootstrap.TriggerField = function(config){
12830 this.mimicing = false;
12831 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
12834 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
12836 * @cfg {String} triggerClass A CSS class to apply to the trigger
12839 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
12844 * @cfg {Boolean} removable (true|false) special filter default false
12848 /** @cfg {Boolean} grow @hide */
12849 /** @cfg {Number} growMin @hide */
12850 /** @cfg {Number} growMax @hide */
12856 autoSize: Roo.emptyFn,
12860 deferHeight : true,
12863 actionMode : 'wrap',
12868 getAutoCreate : function(){
12870 var align = this.labelAlign || this.parentLabelAlign();
12875 cls: 'form-group' //input-group
12882 type : this.inputType,
12883 cls : 'form-control',
12884 autocomplete: 'new-password',
12885 placeholder : this.placeholder || ''
12889 input.name = this.name;
12892 input.cls += ' input-' + this.size;
12895 if (this.disabled) {
12896 input.disabled=true;
12899 var inputblock = input;
12901 if(this.hasFeedback && !this.allowBlank){
12905 cls: 'glyphicon form-control-feedback'
12908 if(this.removable && !this.editable ){
12910 cls : 'has-feedback',
12916 cls : 'roo-combo-removable-btn close'
12923 cls : 'has-feedback',
12932 if(this.removable && !this.editable ){
12934 cls : 'roo-removable',
12940 cls : 'roo-combo-removable-btn close'
12947 if (this.before || this.after) {
12950 cls : 'input-group',
12954 inputblock.cn.push({
12956 cls : 'input-group-addon input-group-prepend input-group-text',
12961 inputblock.cn.push(input);
12963 if(this.hasFeedback && !this.allowBlank){
12964 inputblock.cls += ' has-feedback';
12965 inputblock.cn.push(feedback);
12969 inputblock.cn.push({
12971 cls : 'input-group-addon input-group-append input-group-text',
12980 var ibwrap = inputblock;
12985 cls: 'roo-select2-choices',
12989 cls: 'roo-select2-search-field',
13001 cls: 'roo-select2-container input-group',
13006 cls: 'form-hidden-field'
13012 if(!this.multiple && this.showToggleBtn){
13018 if (this.caret != false) {
13021 cls: 'fa fa-' + this.caret
13028 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13030 Roo.bootstrap.version == 3 ? caret : '',
13033 cls: 'combobox-clear',
13047 combobox.cls += ' roo-select2-container-multi';
13051 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13052 tooltip : 'This field is required'
13054 if (Roo.bootstrap.version == 4) {
13057 style : 'display:none'
13062 if (align ==='left' && this.fieldLabel.length) {
13064 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
13071 cls : 'control-label',
13072 html : this.fieldLabel
13084 var labelCfg = cfg.cn[1];
13085 var contentCfg = cfg.cn[2];
13087 if(this.indicatorpos == 'right'){
13092 cls : 'control-label',
13096 html : this.fieldLabel
13110 labelCfg = cfg.cn[0];
13111 contentCfg = cfg.cn[1];
13114 if(this.labelWidth > 12){
13115 labelCfg.style = "width: " + this.labelWidth + 'px';
13118 if(this.labelWidth < 13 && this.labelmd == 0){
13119 this.labelmd = this.labelWidth;
13122 if(this.labellg > 0){
13123 labelCfg.cls += ' col-lg-' + this.labellg;
13124 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13127 if(this.labelmd > 0){
13128 labelCfg.cls += ' col-md-' + this.labelmd;
13129 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13132 if(this.labelsm > 0){
13133 labelCfg.cls += ' col-sm-' + this.labelsm;
13134 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13137 if(this.labelxs > 0){
13138 labelCfg.cls += ' col-xs-' + this.labelxs;
13139 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13142 } else if ( this.fieldLabel.length) {
13143 // Roo.log(" label");
13148 //cls : 'input-group-addon',
13149 html : this.fieldLabel
13157 if(this.indicatorpos == 'right'){
13165 html : this.fieldLabel
13179 // Roo.log(" no label && no align");
13186 ['xs','sm','md','lg'].map(function(size){
13187 if (settings[size]) {
13188 cfg.cls += ' col-' + size + '-' + settings[size];
13199 onResize : function(w, h){
13200 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
13201 // if(typeof w == 'number'){
13202 // var x = w - this.trigger.getWidth();
13203 // this.inputEl().setWidth(this.adjustWidth('input', x));
13204 // this.trigger.setStyle('left', x+'px');
13209 adjustSize : Roo.BoxComponent.prototype.adjustSize,
13212 getResizeEl : function(){
13213 return this.inputEl();
13217 getPositionEl : function(){
13218 return this.inputEl();
13222 alignErrorIcon : function(){
13223 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
13227 initEvents : function(){
13231 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
13232 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
13233 if(!this.multiple && this.showToggleBtn){
13234 this.trigger = this.el.select('span.dropdown-toggle',true).first();
13235 if(this.hideTrigger){
13236 this.trigger.setDisplayed(false);
13238 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
13242 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
13245 if(this.removable && !this.editable && !this.tickable){
13246 var close = this.closeTriggerEl();
13249 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13250 close.on('click', this.removeBtnClick, this, close);
13254 //this.trigger.addClassOnOver('x-form-trigger-over');
13255 //this.trigger.addClassOnClick('x-form-trigger-click');
13258 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
13262 closeTriggerEl : function()
13264 var close = this.el.select('.roo-combo-removable-btn', true).first();
13265 return close ? close : false;
13268 removeBtnClick : function(e, h, el)
13270 e.preventDefault();
13272 if(this.fireEvent("remove", this) !== false){
13274 this.fireEvent("afterremove", this)
13278 createList : function()
13280 this.list = Roo.get(document.body).createChild({
13281 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
13282 cls: 'typeahead typeahead-long dropdown-menu shadow',
13283 style: 'display:none'
13286 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
13291 initTrigger : function(){
13296 onDestroy : function(){
13298 this.trigger.removeAllListeners();
13299 // this.trigger.remove();
13302 // this.wrap.remove();
13304 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
13308 onFocus : function(){
13309 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
13311 if(!this.mimicing){
13312 this.wrap.addClass('x-trigger-wrap-focus');
13313 this.mimicing = true;
13314 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
13315 if(this.monitorTab){
13316 this.el.on("keydown", this.checkTab, this);
13323 checkTab : function(e){
13324 if(e.getKey() == e.TAB){
13325 this.triggerBlur();
13330 onBlur : function(){
13335 mimicBlur : function(e, t){
13337 if(!this.wrap.contains(t) && this.validateBlur()){
13338 this.triggerBlur();
13344 triggerBlur : function(){
13345 this.mimicing = false;
13346 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
13347 if(this.monitorTab){
13348 this.el.un("keydown", this.checkTab, this);
13350 //this.wrap.removeClass('x-trigger-wrap-focus');
13351 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
13355 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
13356 validateBlur : function(e, t){
13361 onDisable : function(){
13362 this.inputEl().dom.disabled = true;
13363 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
13365 // this.wrap.addClass('x-item-disabled');
13370 onEnable : function(){
13371 this.inputEl().dom.disabled = false;
13372 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
13374 // this.el.removeClass('x-item-disabled');
13379 onShow : function(){
13380 var ae = this.getActionEl();
13383 ae.dom.style.display = '';
13384 ae.dom.style.visibility = 'visible';
13390 onHide : function(){
13391 var ae = this.getActionEl();
13392 ae.dom.style.display = 'none';
13396 * The function that should handle the trigger's click event. This method does nothing by default until overridden
13397 * by an implementing function.
13399 * @param {EventObject} e
13401 onTriggerClick : Roo.emptyFn
13409 * @class Roo.bootstrap.CardUploader
13410 * @extends Roo.bootstrap.Button
13411 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
13412 * @cfg {Number} errorTimeout default 3000
13413 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
13414 * @cfg {Array} html The button text.
13418 * Create a new CardUploader
13419 * @param {Object} config The config object
13422 Roo.bootstrap.CardUploader = function(config){
13426 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
13429 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
13437 * When a image is clicked on - and needs to display a slideshow or similar..
13438 * @param {Roo.bootstrap.Card} this
13439 * @param {Object} The image information data
13445 * When a the download link is clicked
13446 * @param {Roo.bootstrap.Card} this
13447 * @param {Object} The image information data contains
13454 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
13457 errorTimeout : 3000,
13461 fileCollection : false,
13464 getAutoCreate : function()
13468 cls :'form-group' ,
13473 //cls : 'input-group-addon',
13474 html : this.fieldLabel
13482 value : this.value,
13483 cls : 'd-none form-control'
13488 multiple : 'multiple',
13490 cls : 'd-none roo-card-upload-selector'
13494 cls : 'roo-card-uploader-button-container w-100 mb-2'
13497 cls : 'card-columns roo-card-uploader-container'
13507 getChildContainer : function() /// what children are added to.
13509 return this.containerEl;
13512 getButtonContainer : function() /// what children are added to.
13514 return this.el.select(".roo-card-uploader-button-container").first();
13517 initEvents : function()
13520 Roo.bootstrap.Input.prototype.initEvents.call(this);
13524 xns: Roo.bootstrap,
13527 container_method : 'getButtonContainer' ,
13528 html : this.html, // fix changable?
13531 'click' : function(btn, e) {
13540 this.urlAPI = (window.createObjectURL && window) ||
13541 (window.URL && URL.revokeObjectURL && URL) ||
13542 (window.webkitURL && webkitURL);
13547 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
13549 this.selectorEl.on('change', this.onFileSelected, this);
13552 this.images.forEach(function(img) {
13555 this.images = false;
13557 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
13563 onClick : function(e)
13565 e.preventDefault();
13567 this.selectorEl.dom.click();
13571 onFileSelected : function(e)
13573 e.preventDefault();
13575 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
13579 Roo.each(this.selectorEl.dom.files, function(file){
13580 this.addFile(file);
13589 addFile : function(file)
13592 if(typeof(file) === 'string'){
13593 throw "Add file by name?"; // should not happen
13597 if(!file || !this.urlAPI){
13607 var url = _this.urlAPI.createObjectURL( file);
13610 id : Roo.bootstrap.CardUploader.ID--,
13611 is_uploaded : false,
13615 mimetype : file.type,
13623 * addCard - add an Attachment to the uploader
13624 * @param data - the data about the image to upload
13628 title : "Title of file",
13629 is_uploaded : false,
13630 src : "http://.....",
13631 srcfile : { the File upload object },
13632 mimetype : file.type,
13635 .. any other data...
13641 addCard : function (data)
13643 // hidden input element?
13644 // if the file is not an image...
13645 //then we need to use something other that and header_image
13650 xns : Roo.bootstrap,
13651 xtype : 'CardFooter',
13654 xns : Roo.bootstrap,
13660 xns : Roo.bootstrap,
13662 html : String.format("<small>{0}</small>", data.title),
13663 cls : 'col-10 text-left',
13668 click : function() {
13670 t.fireEvent( "download", t, data );
13676 xns : Roo.bootstrap,
13678 style: 'max-height: 28px; ',
13684 click : function() {
13685 t.removeCard(data.id)
13697 var cn = this.addxtype(
13700 xns : Roo.bootstrap,
13703 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
13704 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
13705 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
13710 initEvents : function() {
13711 Roo.bootstrap.Card.prototype.initEvents.call(this);
13713 this.imgEl = this.el.select('.card-img-top').first();
13715 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
13716 this.imgEl.set({ 'pointer' : 'cursor' });
13719 this.getCardFooter().addClass('p-1');
13726 // dont' really need ot update items.
13727 // this.items.push(cn);
13728 this.fileCollection.add(cn);
13730 if (!data.srcfile) {
13731 this.updateInput();
13736 var reader = new FileReader();
13737 reader.addEventListener("load", function() {
13738 data.srcdata = reader.result;
13741 reader.readAsDataURL(data.srcfile);
13746 removeCard : function(id)
13749 var card = this.fileCollection.get(id);
13750 card.data.is_deleted = 1;
13751 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
13752 //this.fileCollection.remove(card);
13753 //this.items = this.items.filter(function(e) { return e != card });
13754 // dont' really need ot update items.
13755 card.el.dom.parentNode.removeChild(card.el.dom);
13756 this.updateInput();
13762 this.fileCollection.each(function(card) {
13763 if (card.el.dom && card.el.dom.parentNode) {
13764 card.el.dom.parentNode.removeChild(card.el.dom);
13767 this.fileCollection.clear();
13768 this.updateInput();
13771 updateInput : function()
13774 this.fileCollection.each(function(e) {
13778 this.inputEl().dom.value = JSON.stringify(data);
13788 Roo.bootstrap.CardUploader.ID = -1;/*
13790 * Ext JS Library 1.1.1
13791 * Copyright(c) 2006-2007, Ext JS, LLC.
13793 * Originally Released Under LGPL - original licence link has changed is not relivant.
13796 * <script type="text/javascript">
13801 * @class Roo.data.SortTypes
13803 * Defines the default sorting (casting?) comparison functions used when sorting data.
13805 Roo.data.SortTypes = {
13807 * Default sort that does nothing
13808 * @param {Mixed} s The value being converted
13809 * @return {Mixed} The comparison value
13811 none : function(s){
13816 * The regular expression used to strip tags
13820 stripTagsRE : /<\/?[^>]+>/gi,
13823 * Strips all HTML tags to sort on text only
13824 * @param {Mixed} s The value being converted
13825 * @return {String} The comparison value
13827 asText : function(s){
13828 return String(s).replace(this.stripTagsRE, "");
13832 * Strips all HTML tags to sort on text only - Case insensitive
13833 * @param {Mixed} s The value being converted
13834 * @return {String} The comparison value
13836 asUCText : function(s){
13837 return String(s).toUpperCase().replace(this.stripTagsRE, "");
13841 * Case insensitive string
13842 * @param {Mixed} s The value being converted
13843 * @return {String} The comparison value
13845 asUCString : function(s) {
13846 return String(s).toUpperCase();
13851 * @param {Mixed} s The value being converted
13852 * @return {Number} The comparison value
13854 asDate : function(s) {
13858 if(s instanceof Date){
13859 return s.getTime();
13861 return Date.parse(String(s));
13866 * @param {Mixed} s The value being converted
13867 * @return {Float} The comparison value
13869 asFloat : function(s) {
13870 var val = parseFloat(String(s).replace(/,/g, ""));
13879 * @param {Mixed} s The value being converted
13880 * @return {Number} The comparison value
13882 asInt : function(s) {
13883 var val = parseInt(String(s).replace(/,/g, ""));
13891 * Ext JS Library 1.1.1
13892 * Copyright(c) 2006-2007, Ext JS, LLC.
13894 * Originally Released Under LGPL - original licence link has changed is not relivant.
13897 * <script type="text/javascript">
13901 * @class Roo.data.Record
13902 * Instances of this class encapsulate both record <em>definition</em> information, and record
13903 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13904 * to access Records cached in an {@link Roo.data.Store} object.<br>
13906 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13907 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13910 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13912 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13913 * {@link #create}. The parameters are the same.
13914 * @param {Array} data An associative Array of data values keyed by the field name.
13915 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13916 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13917 * not specified an integer id is generated.
13919 Roo.data.Record = function(data, id){
13920 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13925 * Generate a constructor for a specific record layout.
13926 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13927 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13928 * Each field definition object may contain the following properties: <ul>
13929 * <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,
13930 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13931 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13932 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13933 * is being used, then this is a string containing the javascript expression to reference the data relative to
13934 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13935 * to the data item relative to the record element. If the mapping expression is the same as the field name,
13936 * this may be omitted.</p></li>
13937 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13938 * <ul><li>auto (Default, implies no conversion)</li>
13943 * <li>date</li></ul></p></li>
13944 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13945 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13946 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13947 * by the Reader into an object that will be stored in the Record. It is passed the
13948 * following parameters:<ul>
13949 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13951 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13953 * <br>usage:<br><pre><code>
13954 var TopicRecord = Roo.data.Record.create(
13955 {name: 'title', mapping: 'topic_title'},
13956 {name: 'author', mapping: 'username'},
13957 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13958 {name: 'lastPost', mapping: 'post_time', type: 'date'},
13959 {name: 'lastPoster', mapping: 'user2'},
13960 {name: 'excerpt', mapping: 'post_text'}
13963 var myNewRecord = new TopicRecord({
13964 title: 'Do my job please',
13967 lastPost: new Date(),
13968 lastPoster: 'Animal',
13969 excerpt: 'No way dude!'
13971 myStore.add(myNewRecord);
13976 Roo.data.Record.create = function(o){
13977 var f = function(){
13978 f.superclass.constructor.apply(this, arguments);
13980 Roo.extend(f, Roo.data.Record);
13981 var p = f.prototype;
13982 p.fields = new Roo.util.MixedCollection(false, function(field){
13985 for(var i = 0, len = o.length; i < len; i++){
13986 p.fields.add(new Roo.data.Field(o[i]));
13988 f.getField = function(name){
13989 return p.fields.get(name);
13994 Roo.data.Record.AUTO_ID = 1000;
13995 Roo.data.Record.EDIT = 'edit';
13996 Roo.data.Record.REJECT = 'reject';
13997 Roo.data.Record.COMMIT = 'commit';
13999 Roo.data.Record.prototype = {
14001 * Readonly flag - true if this record has been modified.
14010 join : function(store){
14011 this.store = store;
14015 * Set the named field to the specified value.
14016 * @param {String} name The name of the field to set.
14017 * @param {Object} value The value to set the field to.
14019 set : function(name, value){
14020 if(this.data[name] == value){
14024 if(!this.modified){
14025 this.modified = {};
14027 if(typeof this.modified[name] == 'undefined'){
14028 this.modified[name] = this.data[name];
14030 this.data[name] = value;
14031 if(!this.editing && this.store){
14032 this.store.afterEdit(this);
14037 * Get the value of the named field.
14038 * @param {String} name The name of the field to get the value of.
14039 * @return {Object} The value of the field.
14041 get : function(name){
14042 return this.data[name];
14046 beginEdit : function(){
14047 this.editing = true;
14048 this.modified = {};
14052 cancelEdit : function(){
14053 this.editing = false;
14054 delete this.modified;
14058 endEdit : function(){
14059 this.editing = false;
14060 if(this.dirty && this.store){
14061 this.store.afterEdit(this);
14066 * Usually called by the {@link Roo.data.Store} which owns the Record.
14067 * Rejects all changes made to the Record since either creation, or the last commit operation.
14068 * Modified fields are reverted to their original values.
14070 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14071 * of reject operations.
14073 reject : function(){
14074 var m = this.modified;
14076 if(typeof m[n] != "function"){
14077 this.data[n] = m[n];
14080 this.dirty = false;
14081 delete this.modified;
14082 this.editing = false;
14084 this.store.afterReject(this);
14089 * Usually called by the {@link Roo.data.Store} which owns the Record.
14090 * Commits all changes made to the Record since either creation, or the last commit operation.
14092 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14093 * of commit operations.
14095 commit : function(){
14096 this.dirty = false;
14097 delete this.modified;
14098 this.editing = false;
14100 this.store.afterCommit(this);
14105 hasError : function(){
14106 return this.error != null;
14110 clearError : function(){
14115 * Creates a copy of this record.
14116 * @param {String} id (optional) A new record id if you don't want to use this record's id
14119 copy : function(newId) {
14120 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
14124 * Ext JS Library 1.1.1
14125 * Copyright(c) 2006-2007, Ext JS, LLC.
14127 * Originally Released Under LGPL - original licence link has changed is not relivant.
14130 * <script type="text/javascript">
14136 * @class Roo.data.Store
14137 * @extends Roo.util.Observable
14138 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
14139 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
14141 * 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
14142 * has no knowledge of the format of the data returned by the Proxy.<br>
14144 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
14145 * instances from the data object. These records are cached and made available through accessor functions.
14147 * Creates a new Store.
14148 * @param {Object} config A config object containing the objects needed for the Store to access data,
14149 * and read the data into Records.
14151 Roo.data.Store = function(config){
14152 this.data = new Roo.util.MixedCollection(false);
14153 this.data.getKey = function(o){
14156 this.baseParams = {};
14158 this.paramNames = {
14163 "multisort" : "_multisort"
14166 if(config && config.data){
14167 this.inlineData = config.data;
14168 delete config.data;
14171 Roo.apply(this, config);
14173 if(this.reader){ // reader passed
14174 this.reader = Roo.factory(this.reader, Roo.data);
14175 this.reader.xmodule = this.xmodule || false;
14176 if(!this.recordType){
14177 this.recordType = this.reader.recordType;
14179 if(this.reader.onMetaChange){
14180 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
14184 if(this.recordType){
14185 this.fields = this.recordType.prototype.fields;
14187 this.modified = [];
14191 * @event datachanged
14192 * Fires when the data cache has changed, and a widget which is using this Store
14193 * as a Record cache should refresh its view.
14194 * @param {Store} this
14196 datachanged : true,
14198 * @event metachange
14199 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
14200 * @param {Store} this
14201 * @param {Object} meta The JSON metadata
14206 * Fires when Records have been added to the Store
14207 * @param {Store} this
14208 * @param {Roo.data.Record[]} records The array of Records added
14209 * @param {Number} index The index at which the record(s) were added
14214 * Fires when a Record has been removed from the Store
14215 * @param {Store} this
14216 * @param {Roo.data.Record} record The Record that was removed
14217 * @param {Number} index The index at which the record was removed
14222 * Fires when a Record has been updated
14223 * @param {Store} this
14224 * @param {Roo.data.Record} record The Record that was updated
14225 * @param {String} operation The update operation being performed. Value may be one of:
14227 Roo.data.Record.EDIT
14228 Roo.data.Record.REJECT
14229 Roo.data.Record.COMMIT
14235 * Fires when the data cache has been cleared.
14236 * @param {Store} this
14240 * @event beforeload
14241 * Fires before a request is made for a new data object. If the beforeload handler returns false
14242 * the load action will be canceled.
14243 * @param {Store} this
14244 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14248 * @event beforeloadadd
14249 * Fires after a new set of Records has been loaded.
14250 * @param {Store} this
14251 * @param {Roo.data.Record[]} records The Records that were loaded
14252 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14254 beforeloadadd : true,
14257 * Fires after a new set of Records has been loaded, before they are added to the store.
14258 * @param {Store} this
14259 * @param {Roo.data.Record[]} records The Records that were loaded
14260 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14261 * @params {Object} return from reader
14265 * @event loadexception
14266 * Fires if an exception occurs in the Proxy during loading.
14267 * Called with the signature of the Proxy's "loadexception" event.
14268 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
14271 * @param {Object} return from JsonData.reader() - success, totalRecords, records
14272 * @param {Object} load options
14273 * @param {Object} jsonData from your request (normally this contains the Exception)
14275 loadexception : true
14279 this.proxy = Roo.factory(this.proxy, Roo.data);
14280 this.proxy.xmodule = this.xmodule || false;
14281 this.relayEvents(this.proxy, ["loadexception"]);
14283 this.sortToggle = {};
14284 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
14286 Roo.data.Store.superclass.constructor.call(this);
14288 if(this.inlineData){
14289 this.loadData(this.inlineData);
14290 delete this.inlineData;
14294 Roo.extend(Roo.data.Store, Roo.util.Observable, {
14296 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
14297 * without a remote query - used by combo/forms at present.
14301 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
14304 * @cfg {Array} data Inline data to be loaded when the store is initialized.
14307 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
14308 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
14311 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
14312 * on any HTTP request
14315 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
14318 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
14322 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
14323 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
14325 remoteSort : false,
14328 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
14329 * loaded or when a record is removed. (defaults to false).
14331 pruneModifiedRecords : false,
14334 lastOptions : null,
14337 * Add Records to the Store and fires the add event.
14338 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14340 add : function(records){
14341 records = [].concat(records);
14342 for(var i = 0, len = records.length; i < len; i++){
14343 records[i].join(this);
14345 var index = this.data.length;
14346 this.data.addAll(records);
14347 this.fireEvent("add", this, records, index);
14351 * Remove a Record from the Store and fires the remove event.
14352 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
14354 remove : function(record){
14355 var index = this.data.indexOf(record);
14356 this.data.removeAt(index);
14358 if(this.pruneModifiedRecords){
14359 this.modified.remove(record);
14361 this.fireEvent("remove", this, record, index);
14365 * Remove all Records from the Store and fires the clear event.
14367 removeAll : function(){
14369 if(this.pruneModifiedRecords){
14370 this.modified = [];
14372 this.fireEvent("clear", this);
14376 * Inserts Records to the Store at the given index and fires the add event.
14377 * @param {Number} index The start index at which to insert the passed Records.
14378 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14380 insert : function(index, records){
14381 records = [].concat(records);
14382 for(var i = 0, len = records.length; i < len; i++){
14383 this.data.insert(index, records[i]);
14384 records[i].join(this);
14386 this.fireEvent("add", this, records, index);
14390 * Get the index within the cache of the passed Record.
14391 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
14392 * @return {Number} The index of the passed Record. Returns -1 if not found.
14394 indexOf : function(record){
14395 return this.data.indexOf(record);
14399 * Get the index within the cache of the Record with the passed id.
14400 * @param {String} id The id of the Record to find.
14401 * @return {Number} The index of the Record. Returns -1 if not found.
14403 indexOfId : function(id){
14404 return this.data.indexOfKey(id);
14408 * Get the Record with the specified id.
14409 * @param {String} id The id of the Record to find.
14410 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
14412 getById : function(id){
14413 return this.data.key(id);
14417 * Get the Record at the specified index.
14418 * @param {Number} index The index of the Record to find.
14419 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
14421 getAt : function(index){
14422 return this.data.itemAt(index);
14426 * Returns a range of Records between specified indices.
14427 * @param {Number} startIndex (optional) The starting index (defaults to 0)
14428 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
14429 * @return {Roo.data.Record[]} An array of Records
14431 getRange : function(start, end){
14432 return this.data.getRange(start, end);
14436 storeOptions : function(o){
14437 o = Roo.apply({}, o);
14440 this.lastOptions = o;
14444 * Loads the Record cache from the configured Proxy using the configured Reader.
14446 * If using remote paging, then the first load call must specify the <em>start</em>
14447 * and <em>limit</em> properties in the options.params property to establish the initial
14448 * position within the dataset, and the number of Records to cache on each read from the Proxy.
14450 * <strong>It is important to note that for remote data sources, loading is asynchronous,
14451 * and this call will return before the new data has been loaded. Perform any post-processing
14452 * in a callback function, or in a "load" event handler.</strong>
14454 * @param {Object} options An object containing properties which control loading options:<ul>
14455 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
14456 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
14457 * passed the following arguments:<ul>
14458 * <li>r : Roo.data.Record[]</li>
14459 * <li>options: Options object from the load call</li>
14460 * <li>success: Boolean success indicator</li></ul></li>
14461 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
14462 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
14465 load : function(options){
14466 options = options || {};
14467 if(this.fireEvent("beforeload", this, options) !== false){
14468 this.storeOptions(options);
14469 var p = Roo.apply(options.params || {}, this.baseParams);
14470 // if meta was not loaded from remote source.. try requesting it.
14471 if (!this.reader.metaFromRemote) {
14472 p._requestMeta = 1;
14474 if(this.sortInfo && this.remoteSort){
14475 var pn = this.paramNames;
14476 p[pn["sort"]] = this.sortInfo.field;
14477 p[pn["dir"]] = this.sortInfo.direction;
14479 if (this.multiSort) {
14480 var pn = this.paramNames;
14481 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
14484 this.proxy.load(p, this.reader, this.loadRecords, this, options);
14489 * Reloads the Record cache from the configured Proxy using the configured Reader and
14490 * the options from the last load operation performed.
14491 * @param {Object} options (optional) An object containing properties which may override the options
14492 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
14493 * the most recently used options are reused).
14495 reload : function(options){
14496 this.load(Roo.applyIf(options||{}, this.lastOptions));
14500 // Called as a callback by the Reader during a load operation.
14501 loadRecords : function(o, options, success){
14502 if(!o || success === false){
14503 if(success !== false){
14504 this.fireEvent("load", this, [], options, o);
14506 if(options.callback){
14507 options.callback.call(options.scope || this, [], options, false);
14511 // if data returned failure - throw an exception.
14512 if (o.success === false) {
14513 // show a message if no listener is registered.
14514 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
14515 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
14517 // loadmask wil be hooked into this..
14518 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
14521 var r = o.records, t = o.totalRecords || r.length;
14523 this.fireEvent("beforeloadadd", this, r, options, o);
14525 if(!options || options.add !== true){
14526 if(this.pruneModifiedRecords){
14527 this.modified = [];
14529 for(var i = 0, len = r.length; i < len; i++){
14533 this.data = this.snapshot;
14534 delete this.snapshot;
14537 this.data.addAll(r);
14538 this.totalLength = t;
14540 this.fireEvent("datachanged", this);
14542 this.totalLength = Math.max(t, this.data.length+r.length);
14546 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
14548 var e = new Roo.data.Record({});
14550 e.set(this.parent.displayField, this.parent.emptyTitle);
14551 e.set(this.parent.valueField, '');
14556 this.fireEvent("load", this, r, options, o);
14557 if(options.callback){
14558 options.callback.call(options.scope || this, r, options, true);
14564 * Loads data from a passed data block. A Reader which understands the format of the data
14565 * must have been configured in the constructor.
14566 * @param {Object} data The data block from which to read the Records. The format of the data expected
14567 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
14568 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
14570 loadData : function(o, append){
14571 var r = this.reader.readRecords(o);
14572 this.loadRecords(r, {add: append}, true);
14576 * using 'cn' the nested child reader read the child array into it's child stores.
14577 * @param {Object} rec The record with a 'children array
14579 loadDataFromChildren : function(rec)
14581 this.loadData(this.reader.toLoadData(rec));
14586 * Gets the number of cached records.
14588 * <em>If using paging, this may not be the total size of the dataset. If the data object
14589 * used by the Reader contains the dataset size, then the getTotalCount() function returns
14590 * the data set size</em>
14592 getCount : function(){
14593 return this.data.length || 0;
14597 * Gets the total number of records in the dataset as returned by the server.
14599 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
14600 * the dataset size</em>
14602 getTotalCount : function(){
14603 return this.totalLength || 0;
14607 * Returns the sort state of the Store as an object with two properties:
14609 field {String} The name of the field by which the Records are sorted
14610 direction {String} The sort order, "ASC" or "DESC"
14613 getSortState : function(){
14614 return this.sortInfo;
14618 applySort : function(){
14619 if(this.sortInfo && !this.remoteSort){
14620 var s = this.sortInfo, f = s.field;
14621 var st = this.fields.get(f).sortType;
14622 var fn = function(r1, r2){
14623 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
14624 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
14626 this.data.sort(s.direction, fn);
14627 if(this.snapshot && this.snapshot != this.data){
14628 this.snapshot.sort(s.direction, fn);
14634 * Sets the default sort column and order to be used by the next load operation.
14635 * @param {String} fieldName The name of the field to sort by.
14636 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14638 setDefaultSort : function(field, dir){
14639 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
14643 * Sort the Records.
14644 * If remote sorting is used, the sort is performed on the server, and the cache is
14645 * reloaded. If local sorting is used, the cache is sorted internally.
14646 * @param {String} fieldName The name of the field to sort by.
14647 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14649 sort : function(fieldName, dir){
14650 var f = this.fields.get(fieldName);
14652 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
14654 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
14655 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
14660 this.sortToggle[f.name] = dir;
14661 this.sortInfo = {field: f.name, direction: dir};
14662 if(!this.remoteSort){
14664 this.fireEvent("datachanged", this);
14666 this.load(this.lastOptions);
14671 * Calls the specified function for each of the Records in the cache.
14672 * @param {Function} fn The function to call. The Record is passed as the first parameter.
14673 * Returning <em>false</em> aborts and exits the iteration.
14674 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
14676 each : function(fn, scope){
14677 this.data.each(fn, scope);
14681 * Gets all records modified since the last commit. Modified records are persisted across load operations
14682 * (e.g., during paging).
14683 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
14685 getModifiedRecords : function(){
14686 return this.modified;
14690 createFilterFn : function(property, value, anyMatch){
14691 if(!value.exec){ // not a regex
14692 value = String(value);
14693 if(value.length == 0){
14696 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
14698 return function(r){
14699 return value.test(r.data[property]);
14704 * Sums the value of <i>property</i> for each record between start and end and returns the result.
14705 * @param {String} property A field on your records
14706 * @param {Number} start The record index to start at (defaults to 0)
14707 * @param {Number} end The last record index to include (defaults to length - 1)
14708 * @return {Number} The sum
14710 sum : function(property, start, end){
14711 var rs = this.data.items, v = 0;
14712 start = start || 0;
14713 end = (end || end === 0) ? end : rs.length-1;
14715 for(var i = start; i <= end; i++){
14716 v += (rs[i].data[property] || 0);
14722 * Filter the records by a specified property.
14723 * @param {String} field A field on your records
14724 * @param {String/RegExp} value Either a string that the field
14725 * should start with or a RegExp to test against the field
14726 * @param {Boolean} anyMatch True to match any part not just the beginning
14728 filter : function(property, value, anyMatch){
14729 var fn = this.createFilterFn(property, value, anyMatch);
14730 return fn ? this.filterBy(fn) : this.clearFilter();
14734 * Filter by a function. The specified function will be called with each
14735 * record in this data source. If the function returns true the record is included,
14736 * otherwise it is filtered.
14737 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14738 * @param {Object} scope (optional) The scope of the function (defaults to this)
14740 filterBy : function(fn, scope){
14741 this.snapshot = this.snapshot || this.data;
14742 this.data = this.queryBy(fn, scope||this);
14743 this.fireEvent("datachanged", this);
14747 * Query the records by a specified property.
14748 * @param {String} field A field on your records
14749 * @param {String/RegExp} value Either a string that the field
14750 * should start with or a RegExp to test against the field
14751 * @param {Boolean} anyMatch True to match any part not just the beginning
14752 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14754 query : function(property, value, anyMatch){
14755 var fn = this.createFilterFn(property, value, anyMatch);
14756 return fn ? this.queryBy(fn) : this.data.clone();
14760 * Query by a function. The specified function will be called with each
14761 * record in this data source. If the function returns true the record is included
14763 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14764 * @param {Object} scope (optional) The scope of the function (defaults to this)
14765 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14767 queryBy : function(fn, scope){
14768 var data = this.snapshot || this.data;
14769 return data.filterBy(fn, scope||this);
14773 * Collects unique values for a particular dataIndex from this store.
14774 * @param {String} dataIndex The property to collect
14775 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
14776 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
14777 * @return {Array} An array of the unique values
14779 collect : function(dataIndex, allowNull, bypassFilter){
14780 var d = (bypassFilter === true && this.snapshot) ?
14781 this.snapshot.items : this.data.items;
14782 var v, sv, r = [], l = {};
14783 for(var i = 0, len = d.length; i < len; i++){
14784 v = d[i].data[dataIndex];
14786 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
14795 * Revert to a view of the Record cache with no filtering applied.
14796 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
14798 clearFilter : function(suppressEvent){
14799 if(this.snapshot && this.snapshot != this.data){
14800 this.data = this.snapshot;
14801 delete this.snapshot;
14802 if(suppressEvent !== true){
14803 this.fireEvent("datachanged", this);
14809 afterEdit : function(record){
14810 if(this.modified.indexOf(record) == -1){
14811 this.modified.push(record);
14813 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
14817 afterReject : function(record){
14818 this.modified.remove(record);
14819 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
14823 afterCommit : function(record){
14824 this.modified.remove(record);
14825 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
14829 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
14830 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
14832 commitChanges : function(){
14833 var m = this.modified.slice(0);
14834 this.modified = [];
14835 for(var i = 0, len = m.length; i < len; i++){
14841 * Cancel outstanding changes on all changed records.
14843 rejectChanges : function(){
14844 var m = this.modified.slice(0);
14845 this.modified = [];
14846 for(var i = 0, len = m.length; i < len; i++){
14851 onMetaChange : function(meta, rtype, o){
14852 this.recordType = rtype;
14853 this.fields = rtype.prototype.fields;
14854 delete this.snapshot;
14855 this.sortInfo = meta.sortInfo || this.sortInfo;
14856 this.modified = [];
14857 this.fireEvent('metachange', this, this.reader.meta);
14860 moveIndex : function(data, type)
14862 var index = this.indexOf(data);
14864 var newIndex = index + type;
14868 this.insert(newIndex, data);
14873 * Ext JS Library 1.1.1
14874 * Copyright(c) 2006-2007, Ext JS, LLC.
14876 * Originally Released Under LGPL - original licence link has changed is not relivant.
14879 * <script type="text/javascript">
14883 * @class Roo.data.SimpleStore
14884 * @extends Roo.data.Store
14885 * Small helper class to make creating Stores from Array data easier.
14886 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
14887 * @cfg {Array} fields An array of field definition objects, or field name strings.
14888 * @cfg {Object} an existing reader (eg. copied from another store)
14889 * @cfg {Array} data The multi-dimensional array of data
14891 * @param {Object} config
14893 Roo.data.SimpleStore = function(config)
14895 Roo.data.SimpleStore.superclass.constructor.call(this, {
14897 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
14900 Roo.data.Record.create(config.fields)
14902 proxy : new Roo.data.MemoryProxy(config.data)
14906 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
14908 * Ext JS Library 1.1.1
14909 * Copyright(c) 2006-2007, Ext JS, LLC.
14911 * Originally Released Under LGPL - original licence link has changed is not relivant.
14914 * <script type="text/javascript">
14919 * @extends Roo.data.Store
14920 * @class Roo.data.JsonStore
14921 * Small helper class to make creating Stores for JSON data easier. <br/>
14923 var store = new Roo.data.JsonStore({
14924 url: 'get-images.php',
14926 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14929 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14930 * JsonReader and HttpProxy (unless inline data is provided).</b>
14931 * @cfg {Array} fields An array of field definition objects, or field name strings.
14933 * @param {Object} config
14935 Roo.data.JsonStore = function(c){
14936 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14937 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14938 reader: new Roo.data.JsonReader(c, c.fields)
14941 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14943 * Ext JS Library 1.1.1
14944 * Copyright(c) 2006-2007, Ext JS, LLC.
14946 * Originally Released Under LGPL - original licence link has changed is not relivant.
14949 * <script type="text/javascript">
14953 Roo.data.Field = function(config){
14954 if(typeof config == "string"){
14955 config = {name: config};
14957 Roo.apply(this, config);
14960 this.type = "auto";
14963 var st = Roo.data.SortTypes;
14964 // named sortTypes are supported, here we look them up
14965 if(typeof this.sortType == "string"){
14966 this.sortType = st[this.sortType];
14969 // set default sortType for strings and dates
14970 if(!this.sortType){
14973 this.sortType = st.asUCString;
14976 this.sortType = st.asDate;
14979 this.sortType = st.none;
14984 var stripRe = /[\$,%]/g;
14986 // prebuilt conversion function for this field, instead of
14987 // switching every time we're reading a value
14989 var cv, dateFormat = this.dateFormat;
14994 cv = function(v){ return v; };
14997 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15001 return v !== undefined && v !== null && v !== '' ?
15002 parseInt(String(v).replace(stripRe, ""), 10) : '';
15007 return v !== undefined && v !== null && v !== '' ?
15008 parseFloat(String(v).replace(stripRe, ""), 10) : '';
15013 cv = function(v){ return v === true || v === "true" || v == 1; };
15020 if(v instanceof Date){
15024 if(dateFormat == "timestamp"){
15025 return new Date(v*1000);
15027 return Date.parseDate(v, dateFormat);
15029 var parsed = Date.parse(v);
15030 return parsed ? new Date(parsed) : null;
15039 Roo.data.Field.prototype = {
15047 * Ext JS Library 1.1.1
15048 * Copyright(c) 2006-2007, Ext JS, LLC.
15050 * Originally Released Under LGPL - original licence link has changed is not relivant.
15053 * <script type="text/javascript">
15056 // Base class for reading structured data from a data source. This class is intended to be
15057 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15060 * @class Roo.data.DataReader
15061 * Base class for reading structured data from a data source. This class is intended to be
15062 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15065 Roo.data.DataReader = function(meta, recordType){
15069 this.recordType = recordType instanceof Array ?
15070 Roo.data.Record.create(recordType) : recordType;
15073 Roo.data.DataReader.prototype = {
15076 readerType : 'Data',
15078 * Create an empty record
15079 * @param {Object} data (optional) - overlay some values
15080 * @return {Roo.data.Record} record created.
15082 newRow : function(d) {
15084 this.recordType.prototype.fields.each(function(c) {
15086 case 'int' : da[c.name] = 0; break;
15087 case 'date' : da[c.name] = new Date(); break;
15088 case 'float' : da[c.name] = 0.0; break;
15089 case 'boolean' : da[c.name] = false; break;
15090 default : da[c.name] = ""; break;
15094 return new this.recordType(Roo.apply(da, d));
15100 * Ext JS Library 1.1.1
15101 * Copyright(c) 2006-2007, Ext JS, LLC.
15103 * Originally Released Under LGPL - original licence link has changed is not relivant.
15106 * <script type="text/javascript">
15110 * @class Roo.data.DataProxy
15111 * @extends Roo.data.Observable
15112 * This class is an abstract base class for implementations which provide retrieval of
15113 * unformatted data objects.<br>
15115 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
15116 * (of the appropriate type which knows how to parse the data object) to provide a block of
15117 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
15119 * Custom implementations must implement the load method as described in
15120 * {@link Roo.data.HttpProxy#load}.
15122 Roo.data.DataProxy = function(){
15125 * @event beforeload
15126 * Fires before a network request is made to retrieve a data object.
15127 * @param {Object} This DataProxy object.
15128 * @param {Object} params The params parameter to the load function.
15133 * Fires before the load method's callback is called.
15134 * @param {Object} This DataProxy object.
15135 * @param {Object} o The data object.
15136 * @param {Object} arg The callback argument object passed to the load function.
15140 * @event loadexception
15141 * Fires if an Exception occurs during data retrieval.
15142 * @param {Object} This DataProxy object.
15143 * @param {Object} o The data object.
15144 * @param {Object} arg The callback argument object passed to the load function.
15145 * @param {Object} e The Exception.
15147 loadexception : true
15149 Roo.data.DataProxy.superclass.constructor.call(this);
15152 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
15155 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
15159 * Ext JS Library 1.1.1
15160 * Copyright(c) 2006-2007, Ext JS, LLC.
15162 * Originally Released Under LGPL - original licence link has changed is not relivant.
15165 * <script type="text/javascript">
15168 * @class Roo.data.MemoryProxy
15169 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
15170 * to the Reader when its load method is called.
15172 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
15174 Roo.data.MemoryProxy = function(data){
15178 Roo.data.MemoryProxy.superclass.constructor.call(this);
15182 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
15185 * Load data from the requested source (in this case an in-memory
15186 * data object passed to the constructor), read the data object into
15187 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15188 * process that block using the passed callback.
15189 * @param {Object} params This parameter is not used by the MemoryProxy class.
15190 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15191 * object into a block of Roo.data.Records.
15192 * @param {Function} callback The function into which to pass the block of Roo.data.records.
15193 * The function must be passed <ul>
15194 * <li>The Record block object</li>
15195 * <li>The "arg" argument from the load function</li>
15196 * <li>A boolean success indicator</li>
15198 * @param {Object} scope The scope in which to call the callback
15199 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15201 load : function(params, reader, callback, scope, arg){
15202 params = params || {};
15205 result = reader.readRecords(params.data ? params.data :this.data);
15207 this.fireEvent("loadexception", this, arg, null, e);
15208 callback.call(scope, null, arg, false);
15211 callback.call(scope, result, arg, true);
15215 update : function(params, records){
15220 * Ext JS Library 1.1.1
15221 * Copyright(c) 2006-2007, Ext JS, LLC.
15223 * Originally Released Under LGPL - original licence link has changed is not relivant.
15226 * <script type="text/javascript">
15229 * @class Roo.data.HttpProxy
15230 * @extends Roo.data.DataProxy
15231 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
15232 * configured to reference a certain URL.<br><br>
15234 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
15235 * from which the running page was served.<br><br>
15237 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
15239 * Be aware that to enable the browser to parse an XML document, the server must set
15240 * the Content-Type header in the HTTP response to "text/xml".
15242 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
15243 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
15244 * will be used to make the request.
15246 Roo.data.HttpProxy = function(conn){
15247 Roo.data.HttpProxy.superclass.constructor.call(this);
15248 // is conn a conn config or a real conn?
15250 this.useAjax = !conn || !conn.events;
15254 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
15255 // thse are take from connection...
15258 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
15261 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
15262 * extra parameters to each request made by this object. (defaults to undefined)
15265 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
15266 * to each request made by this object. (defaults to undefined)
15269 * @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)
15272 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
15275 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
15281 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
15285 * Return the {@link Roo.data.Connection} object being used by this Proxy.
15286 * @return {Connection} The Connection object. This object may be used to subscribe to events on
15287 * a finer-grained basis than the DataProxy events.
15289 getConnection : function(){
15290 return this.useAjax ? Roo.Ajax : this.conn;
15294 * Load data from the configured {@link Roo.data.Connection}, read the data object into
15295 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
15296 * process that block using the passed callback.
15297 * @param {Object} params An object containing properties which are to be used as HTTP parameters
15298 * for the request to the remote server.
15299 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15300 * object into a block of Roo.data.Records.
15301 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15302 * The function must be passed <ul>
15303 * <li>The Record block object</li>
15304 * <li>The "arg" argument from the load function</li>
15305 * <li>A boolean success indicator</li>
15307 * @param {Object} scope The scope in which to call the callback
15308 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15310 load : function(params, reader, callback, scope, arg){
15311 if(this.fireEvent("beforeload", this, params) !== false){
15313 params : params || {},
15315 callback : callback,
15320 callback : this.loadResponse,
15324 Roo.applyIf(o, this.conn);
15325 if(this.activeRequest){
15326 Roo.Ajax.abort(this.activeRequest);
15328 this.activeRequest = Roo.Ajax.request(o);
15330 this.conn.request(o);
15333 callback.call(scope||this, null, arg, false);
15338 loadResponse : function(o, success, response){
15339 delete this.activeRequest;
15341 this.fireEvent("loadexception", this, o, response);
15342 o.request.callback.call(o.request.scope, null, o.request.arg, false);
15347 result = o.reader.read(response);
15349 this.fireEvent("loadexception", this, o, response, e);
15350 o.request.callback.call(o.request.scope, null, o.request.arg, false);
15354 this.fireEvent("load", this, o, o.request.arg);
15355 o.request.callback.call(o.request.scope, result, o.request.arg, true);
15359 update : function(dataSet){
15364 updateResponse : function(dataSet){
15369 * Ext JS Library 1.1.1
15370 * Copyright(c) 2006-2007, Ext JS, LLC.
15372 * Originally Released Under LGPL - original licence link has changed is not relivant.
15375 * <script type="text/javascript">
15379 * @class Roo.data.ScriptTagProxy
15380 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
15381 * other than the originating domain of the running page.<br><br>
15383 * <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
15384 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
15386 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
15387 * source code that is used as the source inside a <script> tag.<br><br>
15389 * In order for the browser to process the returned data, the server must wrap the data object
15390 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
15391 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
15392 * depending on whether the callback name was passed:
15395 boolean scriptTag = false;
15396 String cb = request.getParameter("callback");
15399 response.setContentType("text/javascript");
15401 response.setContentType("application/x-json");
15403 Writer out = response.getWriter();
15405 out.write(cb + "(");
15407 out.print(dataBlock.toJsonString());
15414 * @param {Object} config A configuration object.
15416 Roo.data.ScriptTagProxy = function(config){
15417 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
15418 Roo.apply(this, config);
15419 this.head = document.getElementsByTagName("head")[0];
15422 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
15424 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
15426 * @cfg {String} url The URL from which to request the data object.
15429 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
15433 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
15434 * the server the name of the callback function set up by the load call to process the returned data object.
15435 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
15436 * javascript output which calls this named function passing the data object as its only parameter.
15438 callbackParam : "callback",
15440 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
15441 * name to the request.
15446 * Load data from the configured URL, read the data object into
15447 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15448 * process that block using the passed callback.
15449 * @param {Object} params An object containing properties which are to be used as HTTP parameters
15450 * for the request to the remote server.
15451 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15452 * object into a block of Roo.data.Records.
15453 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15454 * The function must be passed <ul>
15455 * <li>The Record block object</li>
15456 * <li>The "arg" argument from the load function</li>
15457 * <li>A boolean success indicator</li>
15459 * @param {Object} scope The scope in which to call the callback
15460 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15462 load : function(params, reader, callback, scope, arg){
15463 if(this.fireEvent("beforeload", this, params) !== false){
15465 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
15467 var url = this.url;
15468 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
15470 url += "&_dc=" + (new Date().getTime());
15472 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
15475 cb : "stcCallback"+transId,
15476 scriptId : "stcScript"+transId,
15480 callback : callback,
15486 window[trans.cb] = function(o){
15487 conn.handleResponse(o, trans);
15490 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
15492 if(this.autoAbort !== false){
15496 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
15498 var script = document.createElement("script");
15499 script.setAttribute("src", url);
15500 script.setAttribute("type", "text/javascript");
15501 script.setAttribute("id", trans.scriptId);
15502 this.head.appendChild(script);
15504 this.trans = trans;
15506 callback.call(scope||this, null, arg, false);
15511 isLoading : function(){
15512 return this.trans ? true : false;
15516 * Abort the current server request.
15518 abort : function(){
15519 if(this.isLoading()){
15520 this.destroyTrans(this.trans);
15525 destroyTrans : function(trans, isLoaded){
15526 this.head.removeChild(document.getElementById(trans.scriptId));
15527 clearTimeout(trans.timeoutId);
15529 window[trans.cb] = undefined;
15531 delete window[trans.cb];
15534 // if hasn't been loaded, wait for load to remove it to prevent script error
15535 window[trans.cb] = function(){
15536 window[trans.cb] = undefined;
15538 delete window[trans.cb];
15545 handleResponse : function(o, trans){
15546 this.trans = false;
15547 this.destroyTrans(trans, true);
15550 result = trans.reader.readRecords(o);
15552 this.fireEvent("loadexception", this, o, trans.arg, e);
15553 trans.callback.call(trans.scope||window, null, trans.arg, false);
15556 this.fireEvent("load", this, o, trans.arg);
15557 trans.callback.call(trans.scope||window, result, trans.arg, true);
15561 handleFailure : function(trans){
15562 this.trans = false;
15563 this.destroyTrans(trans, false);
15564 this.fireEvent("loadexception", this, null, trans.arg);
15565 trans.callback.call(trans.scope||window, null, trans.arg, false);
15569 * Ext JS Library 1.1.1
15570 * Copyright(c) 2006-2007, Ext JS, LLC.
15572 * Originally Released Under LGPL - original licence link has changed is not relivant.
15575 * <script type="text/javascript">
15579 * @class Roo.data.JsonReader
15580 * @extends Roo.data.DataReader
15581 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
15582 * based on mappings in a provided Roo.data.Record constructor.
15584 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
15585 * in the reply previously.
15590 var RecordDef = Roo.data.Record.create([
15591 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
15592 {name: 'occupation'} // This field will use "occupation" as the mapping.
15594 var myReader = new Roo.data.JsonReader({
15595 totalProperty: "results", // The property which contains the total dataset size (optional)
15596 root: "rows", // The property which contains an Array of row objects
15597 id: "id" // The property within each row object that provides an ID for the record (optional)
15601 * This would consume a JSON file like this:
15603 { 'results': 2, 'rows': [
15604 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
15605 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
15608 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
15609 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
15610 * paged from the remote server.
15611 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
15612 * @cfg {String} root name of the property which contains the Array of row objects.
15613 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15614 * @cfg {Array} fields Array of field definition objects
15616 * Create a new JsonReader
15617 * @param {Object} meta Metadata configuration options
15618 * @param {Object} recordType Either an Array of field definition objects,
15619 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
15621 Roo.data.JsonReader = function(meta, recordType){
15624 // set some defaults:
15625 Roo.applyIf(meta, {
15626 totalProperty: 'total',
15627 successProperty : 'success',
15632 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15634 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
15636 readerType : 'Json',
15639 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
15640 * Used by Store query builder to append _requestMeta to params.
15643 metaFromRemote : false,
15645 * This method is only used by a DataProxy which has retrieved data from a remote server.
15646 * @param {Object} response The XHR object which contains the JSON data in its responseText.
15647 * @return {Object} data A data block which is used by an Roo.data.Store object as
15648 * a cache of Roo.data.Records.
15650 read : function(response){
15651 var json = response.responseText;
15653 var o = /* eval:var:o */ eval("("+json+")");
15655 throw {message: "JsonReader.read: Json object not found"};
15661 this.metaFromRemote = true;
15662 this.meta = o.metaData;
15663 this.recordType = Roo.data.Record.create(o.metaData.fields);
15664 this.onMetaChange(this.meta, this.recordType, o);
15666 return this.readRecords(o);
15669 // private function a store will implement
15670 onMetaChange : function(meta, recordType, o){
15677 simpleAccess: function(obj, subsc) {
15684 getJsonAccessor: function(){
15686 return function(expr) {
15688 return(re.test(expr))
15689 ? new Function("obj", "return obj." + expr)
15694 return Roo.emptyFn;
15699 * Create a data block containing Roo.data.Records from an XML document.
15700 * @param {Object} o An object which contains an Array of row objects in the property specified
15701 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
15702 * which contains the total size of the dataset.
15703 * @return {Object} data A data block which is used by an Roo.data.Store object as
15704 * a cache of Roo.data.Records.
15706 readRecords : function(o){
15708 * After any data loads, the raw JSON data is available for further custom processing.
15712 var s = this.meta, Record = this.recordType,
15713 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
15715 // Generate extraction functions for the totalProperty, the root, the id, and for each field
15717 if(s.totalProperty) {
15718 this.getTotal = this.getJsonAccessor(s.totalProperty);
15720 if(s.successProperty) {
15721 this.getSuccess = this.getJsonAccessor(s.successProperty);
15723 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
15725 var g = this.getJsonAccessor(s.id);
15726 this.getId = function(rec) {
15728 return (r === undefined || r === "") ? null : r;
15731 this.getId = function(){return null;};
15734 for(var jj = 0; jj < fl; jj++){
15736 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
15737 this.ef[jj] = this.getJsonAccessor(map);
15741 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
15742 if(s.totalProperty){
15743 var vt = parseInt(this.getTotal(o), 10);
15748 if(s.successProperty){
15749 var vs = this.getSuccess(o);
15750 if(vs === false || vs === 'false'){
15755 for(var i = 0; i < c; i++){
15758 var id = this.getId(n);
15759 for(var j = 0; j < fl; j++){
15761 var v = this.ef[j](n);
15763 Roo.log('missing convert for ' + f.name);
15767 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
15769 var record = new Record(values, id);
15771 records[i] = record;
15777 totalRecords : totalRecords
15780 // used when loading children.. @see loadDataFromChildren
15781 toLoadData: function(rec)
15783 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15784 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15785 return { data : data, total : data.length };
15790 * Ext JS Library 1.1.1
15791 * Copyright(c) 2006-2007, Ext JS, LLC.
15793 * Originally Released Under LGPL - original licence link has changed is not relivant.
15796 * <script type="text/javascript">
15800 * @class Roo.data.ArrayReader
15801 * @extends Roo.data.DataReader
15802 * Data reader class to create an Array of Roo.data.Record objects from an Array.
15803 * Each element of that Array represents a row of data fields. The
15804 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
15805 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
15809 var RecordDef = Roo.data.Record.create([
15810 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
15811 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
15813 var myReader = new Roo.data.ArrayReader({
15814 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
15818 * This would consume an Array like this:
15820 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
15824 * Create a new JsonReader
15825 * @param {Object} meta Metadata configuration options.
15826 * @param {Object|Array} recordType Either an Array of field definition objects
15828 * @cfg {Array} fields Array of field definition objects
15829 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15830 * as specified to {@link Roo.data.Record#create},
15831 * or an {@link Roo.data.Record} object
15834 * created using {@link Roo.data.Record#create}.
15836 Roo.data.ArrayReader = function(meta, recordType)
15838 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15841 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
15844 * Create a data block containing Roo.data.Records from an XML document.
15845 * @param {Object} o An Array of row objects which represents the dataset.
15846 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
15847 * a cache of Roo.data.Records.
15849 readRecords : function(o)
15851 var sid = this.meta ? this.meta.id : null;
15852 var recordType = this.recordType, fields = recordType.prototype.fields;
15855 for(var i = 0; i < root.length; i++){
15858 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
15859 for(var j = 0, jlen = fields.length; j < jlen; j++){
15860 var f = fields.items[j];
15861 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
15862 var v = n[k] !== undefined ? n[k] : f.defaultValue;
15864 values[f.name] = v;
15866 var record = new recordType(values, id);
15868 records[records.length] = record;
15872 totalRecords : records.length
15875 // used when loading children.. @see loadDataFromChildren
15876 toLoadData: function(rec)
15878 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15879 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15890 * @class Roo.bootstrap.ComboBox
15891 * @extends Roo.bootstrap.TriggerField
15892 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
15893 * @cfg {Boolean} append (true|false) default false
15894 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
15895 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
15896 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
15897 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
15898 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
15899 * @cfg {Boolean} animate default true
15900 * @cfg {Boolean} emptyResultText only for touch device
15901 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15902 * @cfg {String} emptyTitle default ''
15903 * @cfg {Number} width fixed with? experimental
15905 * Create a new ComboBox.
15906 * @param {Object} config Configuration options
15908 Roo.bootstrap.ComboBox = function(config){
15909 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15913 * Fires when the dropdown list is expanded
15914 * @param {Roo.bootstrap.ComboBox} combo This combo box
15919 * Fires when the dropdown list is collapsed
15920 * @param {Roo.bootstrap.ComboBox} combo This combo box
15924 * @event beforeselect
15925 * Fires before a list item is selected. Return false to cancel the selection.
15926 * @param {Roo.bootstrap.ComboBox} combo This combo box
15927 * @param {Roo.data.Record} record The data record returned from the underlying store
15928 * @param {Number} index The index of the selected item in the dropdown list
15930 'beforeselect' : true,
15933 * Fires when a list item is selected
15934 * @param {Roo.bootstrap.ComboBox} combo This combo box
15935 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15936 * @param {Number} index The index of the selected item in the dropdown list
15940 * @event beforequery
15941 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15942 * The event object passed has these properties:
15943 * @param {Roo.bootstrap.ComboBox} combo This combo box
15944 * @param {String} query The query
15945 * @param {Boolean} forceAll true to force "all" query
15946 * @param {Boolean} cancel true to cancel the query
15947 * @param {Object} e The query event object
15949 'beforequery': true,
15952 * Fires when the 'add' icon is pressed (add a listener to enable add button)
15953 * @param {Roo.bootstrap.ComboBox} combo This combo box
15958 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15959 * @param {Roo.bootstrap.ComboBox} combo This combo box
15960 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15965 * Fires when the remove value from the combobox array
15966 * @param {Roo.bootstrap.ComboBox} combo This combo box
15970 * @event afterremove
15971 * Fires when the remove value from the combobox array
15972 * @param {Roo.bootstrap.ComboBox} combo This combo box
15974 'afterremove' : true,
15976 * @event specialfilter
15977 * Fires when specialfilter
15978 * @param {Roo.bootstrap.ComboBox} combo This combo box
15980 'specialfilter' : true,
15983 * Fires when tick the element
15984 * @param {Roo.bootstrap.ComboBox} combo This combo box
15988 * @event touchviewdisplay
15989 * Fires when touch view require special display (default is using displayField)
15990 * @param {Roo.bootstrap.ComboBox} combo This combo box
15991 * @param {Object} cfg set html .
15993 'touchviewdisplay' : true
15998 this.tickItems = [];
16000 this.selectedIndex = -1;
16001 if(this.mode == 'local'){
16002 if(config.queryDelay === undefined){
16003 this.queryDelay = 10;
16005 if(config.minChars === undefined){
16011 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
16014 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16015 * rendering into an Roo.Editor, defaults to false)
16018 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16019 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16022 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16025 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16026 * the dropdown list (defaults to undefined, with no header element)
16030 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
16034 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16036 listWidth: undefined,
16038 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16039 * mode = 'remote' or 'text' if mode = 'local')
16041 displayField: undefined,
16044 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16045 * mode = 'remote' or 'value' if mode = 'local').
16046 * Note: use of a valueField requires the user make a selection
16047 * in order for a value to be mapped.
16049 valueField: undefined,
16051 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16056 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16057 * field's data value (defaults to the underlying DOM element's name)
16059 hiddenName: undefined,
16061 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16065 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16067 selectedClass: 'active',
16070 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16074 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16075 * anchor positions (defaults to 'tl-bl')
16077 listAlign: 'tl-bl?',
16079 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16083 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
16084 * query specified by the allQuery config option (defaults to 'query')
16086 triggerAction: 'query',
16088 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16089 * (defaults to 4, does not apply if editable = false)
16093 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16094 * delay (typeAheadDelay) if it matches a known value (defaults to false)
16098 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
16099 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
16103 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
16104 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
16108 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
16109 * when editable = true (defaults to false)
16111 selectOnFocus:false,
16113 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
16115 queryParam: 'query',
16117 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
16118 * when mode = 'remote' (defaults to 'Loading...')
16120 loadingText: 'Loading...',
16122 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
16126 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
16130 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
16131 * traditional select (defaults to true)
16135 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
16139 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
16143 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
16144 * listWidth has a higher value)
16148 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
16149 * allow the user to set arbitrary text into the field (defaults to false)
16151 forceSelection:false,
16153 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
16154 * if typeAhead = true (defaults to 250)
16156 typeAheadDelay : 250,
16158 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
16159 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
16161 valueNotFoundText : undefined,
16163 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
16165 blockFocus : false,
16168 * @cfg {Boolean} disableClear Disable showing of clear button.
16170 disableClear : false,
16172 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
16174 alwaysQuery : false,
16177 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
16182 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
16184 invalidClass : "has-warning",
16187 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
16189 validClass : "has-success",
16192 * @cfg {Boolean} specialFilter (true|false) special filter default false
16194 specialFilter : false,
16197 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
16199 mobileTouchView : true,
16202 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
16204 useNativeIOS : false,
16207 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
16209 mobile_restrict_height : false,
16211 ios_options : false,
16223 btnPosition : 'right',
16224 triggerList : true,
16225 showToggleBtn : true,
16227 emptyResultText: 'Empty',
16228 triggerText : 'Select',
16232 // element that contains real text value.. (when hidden is used..)
16234 getAutoCreate : function()
16239 * Render classic select for iso
16242 if(Roo.isIOS && this.useNativeIOS){
16243 cfg = this.getAutoCreateNativeIOS();
16251 if(Roo.isTouch && this.mobileTouchView){
16252 cfg = this.getAutoCreateTouchView();
16259 if(!this.tickable){
16260 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
16265 * ComboBox with tickable selections
16268 var align = this.labelAlign || this.parentLabelAlign();
16271 cls : 'form-group roo-combobox-tickable' //input-group
16274 var btn_text_select = '';
16275 var btn_text_done = '';
16276 var btn_text_cancel = '';
16278 if (this.btn_text_show) {
16279 btn_text_select = 'Select';
16280 btn_text_done = 'Done';
16281 btn_text_cancel = 'Cancel';
16286 cls : 'tickable-buttons',
16291 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
16292 //html : this.triggerText
16293 html: btn_text_select
16299 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
16301 html: btn_text_done
16307 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
16309 html: btn_text_cancel
16315 buttons.cn.unshift({
16317 cls: 'roo-select2-search-field-input'
16323 Roo.each(buttons.cn, function(c){
16325 c.cls += ' btn-' + _this.size;
16328 if (_this.disabled) {
16335 style : 'display: contents',
16340 cls: 'form-hidden-field'
16344 cls: 'roo-select2-choices',
16348 cls: 'roo-select2-search-field',
16359 cls: 'roo-select2-container input-group roo-select2-container-multi',
16365 // cls: 'typeahead typeahead-long dropdown-menu',
16366 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
16371 if(this.hasFeedback && !this.allowBlank){
16375 cls: 'glyphicon form-control-feedback'
16378 combobox.cn.push(feedback);
16385 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
16386 tooltip : 'This field is required'
16388 if (Roo.bootstrap.version == 4) {
16391 style : 'display:none'
16394 if (align ==='left' && this.fieldLabel.length) {
16396 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
16403 cls : 'control-label col-form-label',
16404 html : this.fieldLabel
16416 var labelCfg = cfg.cn[1];
16417 var contentCfg = cfg.cn[2];
16420 if(this.indicatorpos == 'right'){
16426 cls : 'control-label col-form-label',
16430 html : this.fieldLabel
16446 labelCfg = cfg.cn[0];
16447 contentCfg = cfg.cn[1];
16451 if(this.labelWidth > 12){
16452 labelCfg.style = "width: " + this.labelWidth + 'px';
16454 if(this.width * 1 > 0){
16455 contentCfg.style = "width: " + this.width + 'px';
16457 if(this.labelWidth < 13 && this.labelmd == 0){
16458 this.labelmd = this.labelWidth;
16461 if(this.labellg > 0){
16462 labelCfg.cls += ' col-lg-' + this.labellg;
16463 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
16466 if(this.labelmd > 0){
16467 labelCfg.cls += ' col-md-' + this.labelmd;
16468 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
16471 if(this.labelsm > 0){
16472 labelCfg.cls += ' col-sm-' + this.labelsm;
16473 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
16476 if(this.labelxs > 0){
16477 labelCfg.cls += ' col-xs-' + this.labelxs;
16478 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
16482 } else if ( this.fieldLabel.length) {
16483 // Roo.log(" label");
16488 //cls : 'input-group-addon',
16489 html : this.fieldLabel
16494 if(this.indicatorpos == 'right'){
16498 //cls : 'input-group-addon',
16499 html : this.fieldLabel
16509 // Roo.log(" no label && no align");
16516 ['xs','sm','md','lg'].map(function(size){
16517 if (settings[size]) {
16518 cfg.cls += ' col-' + size + '-' + settings[size];
16526 _initEventsCalled : false,
16529 initEvents: function()
16531 if (this._initEventsCalled) { // as we call render... prevent looping...
16534 this._initEventsCalled = true;
16537 throw "can not find store for combo";
16540 this.indicator = this.indicatorEl();
16542 this.store = Roo.factory(this.store, Roo.data);
16543 this.store.parent = this;
16545 // if we are building from html. then this element is so complex, that we can not really
16546 // use the rendered HTML.
16547 // so we have to trash and replace the previous code.
16548 if (Roo.XComponent.build_from_html) {
16549 // remove this element....
16550 var e = this.el.dom, k=0;
16551 while (e ) { e = e.previousSibling; ++k;}
16556 this.rendered = false;
16558 this.render(this.parent().getChildContainer(true), k);
16561 if(Roo.isIOS && this.useNativeIOS){
16562 this.initIOSView();
16570 if(Roo.isTouch && this.mobileTouchView){
16571 this.initTouchView();
16576 this.initTickableEvents();
16580 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
16582 if(this.hiddenName){
16584 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16586 this.hiddenField.dom.value =
16587 this.hiddenValue !== undefined ? this.hiddenValue :
16588 this.value !== undefined ? this.value : '';
16590 // prevent input submission
16591 this.el.dom.removeAttribute('name');
16592 this.hiddenField.dom.setAttribute('name', this.hiddenName);
16597 // this.el.dom.setAttribute('autocomplete', 'off');
16600 var cls = 'x-combo-list';
16602 //this.list = new Roo.Layer({
16603 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
16609 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16610 _this.list.setWidth(lw);
16613 this.list.on('mouseover', this.onViewOver, this);
16614 this.list.on('mousemove', this.onViewMove, this);
16615 this.list.on('scroll', this.onViewScroll, this);
16618 this.list.swallowEvent('mousewheel');
16619 this.assetHeight = 0;
16622 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
16623 this.assetHeight += this.header.getHeight();
16626 this.innerList = this.list.createChild({cls:cls+'-inner'});
16627 this.innerList.on('mouseover', this.onViewOver, this);
16628 this.innerList.on('mousemove', this.onViewMove, this);
16629 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16631 if(this.allowBlank && !this.pageSize && !this.disableClear){
16632 this.footer = this.list.createChild({cls:cls+'-ft'});
16633 this.pageTb = new Roo.Toolbar(this.footer);
16637 this.footer = this.list.createChild({cls:cls+'-ft'});
16638 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
16639 {pageSize: this.pageSize});
16643 if (this.pageTb && this.allowBlank && !this.disableClear) {
16645 this.pageTb.add(new Roo.Toolbar.Fill(), {
16646 cls: 'x-btn-icon x-btn-clear',
16648 handler: function()
16651 _this.clearValue();
16652 _this.onSelect(false, -1);
16657 this.assetHeight += this.footer.getHeight();
16662 this.tpl = Roo.bootstrap.version == 4 ?
16663 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
16664 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
16667 this.view = new Roo.View(this.list, this.tpl, {
16668 singleSelect:true, store: this.store, selectedClass: this.selectedClass
16670 //this.view.wrapEl.setDisplayed(false);
16671 this.view.on('click', this.onViewClick, this);
16674 this.store.on('beforeload', this.onBeforeLoad, this);
16675 this.store.on('load', this.onLoad, this);
16676 this.store.on('loadexception', this.onLoadException, this);
16678 if(this.resizable){
16679 this.resizer = new Roo.Resizable(this.list, {
16680 pinned:true, handles:'se'
16682 this.resizer.on('resize', function(r, w, h){
16683 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
16684 this.listWidth = w;
16685 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
16686 this.restrictHeight();
16688 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
16691 if(!this.editable){
16692 this.editable = true;
16693 this.setEditable(false);
16698 if (typeof(this.events.add.listeners) != 'undefined') {
16700 this.addicon = this.wrap.createChild(
16701 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
16703 this.addicon.on('click', function(e) {
16704 this.fireEvent('add', this);
16707 if (typeof(this.events.edit.listeners) != 'undefined') {
16709 this.editicon = this.wrap.createChild(
16710 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
16711 if (this.addicon) {
16712 this.editicon.setStyle('margin-left', '40px');
16714 this.editicon.on('click', function(e) {
16716 // we fire even if inothing is selected..
16717 this.fireEvent('edit', this, this.lastData );
16723 this.keyNav = new Roo.KeyNav(this.inputEl(), {
16724 "up" : function(e){
16725 this.inKeyMode = true;
16729 "down" : function(e){
16730 if(!this.isExpanded()){
16731 this.onTriggerClick();
16733 this.inKeyMode = true;
16738 "enter" : function(e){
16739 // this.onViewClick();
16743 if(this.fireEvent("specialkey", this, e)){
16744 this.onViewClick(false);
16750 "esc" : function(e){
16754 "tab" : function(e){
16757 if(this.fireEvent("specialkey", this, e)){
16758 this.onViewClick(false);
16766 doRelay : function(foo, bar, hname){
16767 if(hname == 'down' || this.scope.isExpanded()){
16768 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16777 this.queryDelay = Math.max(this.queryDelay || 10,
16778 this.mode == 'local' ? 10 : 250);
16781 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16783 if(this.typeAhead){
16784 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16786 if(this.editable !== false){
16787 this.inputEl().on("keyup", this.onKeyUp, this);
16789 if(this.forceSelection){
16790 this.inputEl().on('blur', this.doForce, this);
16794 this.choices = this.el.select('ul.roo-select2-choices', true).first();
16795 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16799 initTickableEvents: function()
16803 if(this.hiddenName){
16805 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16807 this.hiddenField.dom.value =
16808 this.hiddenValue !== undefined ? this.hiddenValue :
16809 this.value !== undefined ? this.value : '';
16811 // prevent input submission
16812 this.el.dom.removeAttribute('name');
16813 this.hiddenField.dom.setAttribute('name', this.hiddenName);
16818 // this.list = this.el.select('ul.dropdown-menu',true).first();
16820 this.choices = this.el.select('ul.roo-select2-choices', true).first();
16821 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16822 if(this.triggerList){
16823 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
16826 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
16827 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
16829 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
16830 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
16832 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
16833 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
16835 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
16836 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
16837 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
16840 this.cancelBtn.hide();
16845 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16846 _this.list.setWidth(lw);
16849 this.list.on('mouseover', this.onViewOver, this);
16850 this.list.on('mousemove', this.onViewMove, this);
16852 this.list.on('scroll', this.onViewScroll, this);
16855 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
16856 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
16859 this.view = new Roo.View(this.list, this.tpl, {
16864 selectedClass: this.selectedClass
16867 //this.view.wrapEl.setDisplayed(false);
16868 this.view.on('click', this.onViewClick, this);
16872 this.store.on('beforeload', this.onBeforeLoad, this);
16873 this.store.on('load', this.onLoad, this);
16874 this.store.on('loadexception', this.onLoadException, this);
16877 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
16878 "up" : function(e){
16879 this.inKeyMode = true;
16883 "down" : function(e){
16884 this.inKeyMode = true;
16888 "enter" : function(e){
16889 if(this.fireEvent("specialkey", this, e)){
16890 this.onViewClick(false);
16896 "esc" : function(e){
16897 this.onTickableFooterButtonClick(e, false, false);
16900 "tab" : function(e){
16901 this.fireEvent("specialkey", this, e);
16903 this.onTickableFooterButtonClick(e, false, false);
16910 doRelay : function(e, fn, key){
16911 if(this.scope.isExpanded()){
16912 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16921 this.queryDelay = Math.max(this.queryDelay || 10,
16922 this.mode == 'local' ? 10 : 250);
16925 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16927 if(this.typeAhead){
16928 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16931 if(this.editable !== false){
16932 this.tickableInputEl().on("keyup", this.onKeyUp, this);
16935 this.indicator = this.indicatorEl();
16937 if(this.indicator){
16938 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16939 this.indicator.hide();
16944 onDestroy : function(){
16946 this.view.setStore(null);
16947 this.view.el.removeAllListeners();
16948 this.view.el.remove();
16949 this.view.purgeListeners();
16952 this.list.dom.innerHTML = '';
16956 this.store.un('beforeload', this.onBeforeLoad, this);
16957 this.store.un('load', this.onLoad, this);
16958 this.store.un('loadexception', this.onLoadException, this);
16960 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16964 fireKey : function(e){
16965 if(e.isNavKeyPress() && !this.list.isVisible()){
16966 this.fireEvent("specialkey", this, e);
16971 onResize: function(w, h)
16975 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16977 // if(typeof w != 'number'){
16978 // // we do not handle it!?!?
16981 // var tw = this.trigger.getWidth();
16982 // // tw += this.addicon ? this.addicon.getWidth() : 0;
16983 // // tw += this.editicon ? this.editicon.getWidth() : 0;
16985 // this.inputEl().setWidth( this.adjustWidth('input', x));
16987 // //this.trigger.setStyle('left', x+'px');
16989 // if(this.list && this.listWidth === undefined){
16990 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16991 // this.list.setWidth(lw);
16992 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17000 * Allow or prevent the user from directly editing the field text. If false is passed,
17001 * the user will only be able to select from the items defined in the dropdown list. This method
17002 * is the runtime equivalent of setting the 'editable' config option at config time.
17003 * @param {Boolean} value True to allow the user to directly edit the field text
17005 setEditable : function(value){
17006 if(value == this.editable){
17009 this.editable = value;
17011 this.inputEl().dom.setAttribute('readOnly', true);
17012 this.inputEl().on('mousedown', this.onTriggerClick, this);
17013 this.inputEl().addClass('x-combo-noedit');
17015 this.inputEl().dom.removeAttribute('readOnly');
17016 this.inputEl().un('mousedown', this.onTriggerClick, this);
17017 this.inputEl().removeClass('x-combo-noedit');
17023 onBeforeLoad : function(combo,opts){
17024 if(!this.hasFocus){
17028 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17030 this.restrictHeight();
17031 this.selectedIndex = -1;
17035 onLoad : function(){
17037 this.hasQuery = false;
17039 if(!this.hasFocus){
17043 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17044 this.loading.hide();
17047 if(this.store.getCount() > 0){
17050 this.restrictHeight();
17051 if(this.lastQuery == this.allQuery){
17052 if(this.editable && !this.tickable){
17053 this.inputEl().dom.select();
17057 !this.selectByValue(this.value, true) &&
17060 !this.store.lastOptions ||
17061 typeof(this.store.lastOptions.add) == 'undefined' ||
17062 this.store.lastOptions.add != true
17065 this.select(0, true);
17068 if(this.autoFocus){
17071 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17072 this.taTask.delay(this.typeAheadDelay);
17076 this.onEmptyResults();
17082 onLoadException : function()
17084 this.hasQuery = false;
17086 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17087 this.loading.hide();
17090 if(this.tickable && this.editable){
17095 // only causes errors at present
17096 //Roo.log(this.store.reader.jsonData);
17097 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
17099 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
17105 onTypeAhead : function(){
17106 if(this.store.getCount() > 0){
17107 var r = this.store.getAt(0);
17108 var newValue = r.data[this.displayField];
17109 var len = newValue.length;
17110 var selStart = this.getRawValue().length;
17112 if(selStart != len){
17113 this.setRawValue(newValue);
17114 this.selectText(selStart, newValue.length);
17120 onSelect : function(record, index){
17122 if(this.fireEvent('beforeselect', this, record, index) !== false){
17124 this.setFromData(index > -1 ? record.data : false);
17127 this.fireEvent('select', this, record, index);
17132 * Returns the currently selected field value or empty string if no value is set.
17133 * @return {String} value The selected value
17135 getValue : function()
17137 if(Roo.isIOS && this.useNativeIOS){
17138 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
17142 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
17145 if(this.valueField){
17146 return typeof this.value != 'undefined' ? this.value : '';
17148 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
17152 getRawValue : function()
17154 if(Roo.isIOS && this.useNativeIOS){
17155 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
17158 var v = this.inputEl().getValue();
17164 * Clears any text/value currently set in the field
17166 clearValue : function(){
17168 if(this.hiddenField){
17169 this.hiddenField.dom.value = '';
17172 this.setRawValue('');
17173 this.lastSelectionText = '';
17174 this.lastData = false;
17176 var close = this.closeTriggerEl();
17187 * Sets the specified value into the field. If the value finds a match, the corresponding record text
17188 * will be displayed in the field. If the value does not match the data value of an existing item,
17189 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
17190 * Otherwise the field will be blank (although the value will still be set).
17191 * @param {String} value The value to match
17193 setValue : function(v)
17195 if(Roo.isIOS && this.useNativeIOS){
17196 this.setIOSValue(v);
17206 if(this.valueField){
17207 var r = this.findRecord(this.valueField, v);
17209 text = r.data[this.displayField];
17210 }else if(this.valueNotFoundText !== undefined){
17211 text = this.valueNotFoundText;
17214 this.lastSelectionText = text;
17215 if(this.hiddenField){
17216 this.hiddenField.dom.value = v;
17218 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
17221 var close = this.closeTriggerEl();
17224 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
17230 * @property {Object} the last set data for the element
17235 * Sets the value of the field based on a object which is related to the record format for the store.
17236 * @param {Object} value the value to set as. or false on reset?
17238 setFromData : function(o){
17245 var dv = ''; // display value
17246 var vv = ''; // value value..
17248 if (this.displayField) {
17249 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17251 // this is an error condition!!!
17252 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
17255 if(this.valueField){
17256 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
17259 var close = this.closeTriggerEl();
17262 if(dv.length || vv * 1 > 0){
17264 this.blockFocus=true;
17270 if(this.hiddenField){
17271 this.hiddenField.dom.value = vv;
17273 this.lastSelectionText = dv;
17274 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17278 // no hidden field.. - we store the value in 'value', but still display
17279 // display field!!!!
17280 this.lastSelectionText = dv;
17281 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17288 reset : function(){
17289 // overridden so that last data is reset..
17296 this.setValue(this.originalValue);
17297 //this.clearInvalid();
17298 this.lastData = false;
17300 this.view.clearSelections();
17306 findRecord : function(prop, value){
17308 if(this.store.getCount() > 0){
17309 this.store.each(function(r){
17310 if(r.data[prop] == value){
17320 getName: function()
17322 // returns hidden if it's set..
17323 if (!this.rendered) {return ''};
17324 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
17328 onViewMove : function(e, t){
17329 this.inKeyMode = false;
17333 onViewOver : function(e, t){
17334 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
17337 var item = this.view.findItemFromChild(t);
17340 var index = this.view.indexOf(item);
17341 this.select(index, false);
17346 onViewClick : function(view, doFocus, el, e)
17348 var index = this.view.getSelectedIndexes()[0];
17350 var r = this.store.getAt(index);
17354 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
17361 Roo.each(this.tickItems, function(v,k){
17363 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
17365 _this.tickItems.splice(k, 1);
17367 if(typeof(e) == 'undefined' && view == false){
17368 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
17380 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
17381 this.tickItems.push(r.data);
17384 if(typeof(e) == 'undefined' && view == false){
17385 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
17392 this.onSelect(r, index);
17394 if(doFocus !== false && !this.blockFocus){
17395 this.inputEl().focus();
17400 restrictHeight : function(){
17401 //this.innerList.dom.style.height = '';
17402 //var inner = this.innerList.dom;
17403 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
17404 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
17405 //this.list.beginUpdate();
17406 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
17407 this.list.alignTo(this.inputEl(), this.listAlign);
17408 this.list.alignTo(this.inputEl(), this.listAlign);
17409 //this.list.endUpdate();
17413 onEmptyResults : function(){
17415 if(this.tickable && this.editable){
17416 this.hasFocus = false;
17417 this.restrictHeight();
17425 * Returns true if the dropdown list is expanded, else false.
17427 isExpanded : function(){
17428 return this.list.isVisible();
17432 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
17433 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17434 * @param {String} value The data value of the item to select
17435 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17436 * selected item if it is not currently in view (defaults to true)
17437 * @return {Boolean} True if the value matched an item in the list, else false
17439 selectByValue : function(v, scrollIntoView){
17440 if(v !== undefined && v !== null){
17441 var r = this.findRecord(this.valueField || this.displayField, v);
17443 this.select(this.store.indexOf(r), scrollIntoView);
17451 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
17452 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17453 * @param {Number} index The zero-based index of the list item to select
17454 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17455 * selected item if it is not currently in view (defaults to true)
17457 select : function(index, scrollIntoView){
17458 this.selectedIndex = index;
17459 this.view.select(index);
17460 if(scrollIntoView !== false){
17461 var el = this.view.getNode(index);
17463 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
17466 this.list.scrollChildIntoView(el, false);
17472 selectNext : function(){
17473 var ct = this.store.getCount();
17475 if(this.selectedIndex == -1){
17477 }else if(this.selectedIndex < ct-1){
17478 this.select(this.selectedIndex+1);
17484 selectPrev : function(){
17485 var ct = this.store.getCount();
17487 if(this.selectedIndex == -1){
17489 }else if(this.selectedIndex != 0){
17490 this.select(this.selectedIndex-1);
17496 onKeyUp : function(e){
17497 if(this.editable !== false && !e.isSpecialKey()){
17498 this.lastKey = e.getKey();
17499 this.dqTask.delay(this.queryDelay);
17504 validateBlur : function(){
17505 return !this.list || !this.list.isVisible();
17509 initQuery : function(){
17511 var v = this.getRawValue();
17513 if(this.tickable && this.editable){
17514 v = this.tickableInputEl().getValue();
17521 doForce : function(){
17522 if(this.inputEl().dom.value.length > 0){
17523 this.inputEl().dom.value =
17524 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
17530 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
17531 * query allowing the query action to be canceled if needed.
17532 * @param {String} query The SQL query to execute
17533 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
17534 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
17535 * saved in the current store (defaults to false)
17537 doQuery : function(q, forceAll){
17539 if(q === undefined || q === null){
17544 forceAll: forceAll,
17548 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
17553 forceAll = qe.forceAll;
17554 if(forceAll === true || (q.length >= this.minChars)){
17556 this.hasQuery = true;
17558 if(this.lastQuery != q || this.alwaysQuery){
17559 this.lastQuery = q;
17560 if(this.mode == 'local'){
17561 this.selectedIndex = -1;
17563 this.store.clearFilter();
17566 if(this.specialFilter){
17567 this.fireEvent('specialfilter', this);
17572 this.store.filter(this.displayField, q);
17575 this.store.fireEvent("datachanged", this.store);
17582 this.store.baseParams[this.queryParam] = q;
17584 var options = {params : this.getParams(q)};
17587 options.add = true;
17588 options.params.start = this.page * this.pageSize;
17591 this.store.load(options);
17594 * this code will make the page width larger, at the beginning, the list not align correctly,
17595 * we should expand the list on onLoad
17596 * so command out it
17601 this.selectedIndex = -1;
17606 this.loadNext = false;
17610 getParams : function(q){
17612 //p[this.queryParam] = q;
17616 p.limit = this.pageSize;
17622 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
17624 collapse : function(){
17625 if(!this.isExpanded()){
17631 this.hasFocus = false;
17635 this.cancelBtn.hide();
17636 this.trigger.show();
17639 this.tickableInputEl().dom.value = '';
17640 this.tickableInputEl().blur();
17645 Roo.get(document).un('mousedown', this.collapseIf, this);
17646 Roo.get(document).un('mousewheel', this.collapseIf, this);
17647 if (!this.editable) {
17648 Roo.get(document).un('keydown', this.listKeyPress, this);
17650 this.fireEvent('collapse', this);
17656 collapseIf : function(e){
17657 var in_combo = e.within(this.el);
17658 var in_list = e.within(this.list);
17659 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
17661 if (in_combo || in_list || is_list) {
17662 //e.stopPropagation();
17667 this.onTickableFooterButtonClick(e, false, false);
17675 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
17677 expand : function(){
17679 if(this.isExpanded() || !this.hasFocus){
17683 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
17684 this.list.setWidth(lw);
17690 this.restrictHeight();
17694 this.tickItems = Roo.apply([], this.item);
17697 this.cancelBtn.show();
17698 this.trigger.hide();
17701 this.tickableInputEl().focus();
17706 Roo.get(document).on('mousedown', this.collapseIf, this);
17707 Roo.get(document).on('mousewheel', this.collapseIf, this);
17708 if (!this.editable) {
17709 Roo.get(document).on('keydown', this.listKeyPress, this);
17712 this.fireEvent('expand', this);
17716 // Implements the default empty TriggerField.onTriggerClick function
17717 onTriggerClick : function(e)
17719 Roo.log('trigger click');
17721 if(this.disabled || !this.triggerList){
17726 this.loadNext = false;
17728 if(this.isExpanded()){
17730 if (!this.blockFocus) {
17731 this.inputEl().focus();
17735 this.hasFocus = true;
17736 if(this.triggerAction == 'all') {
17737 this.doQuery(this.allQuery, true);
17739 this.doQuery(this.getRawValue());
17741 if (!this.blockFocus) {
17742 this.inputEl().focus();
17747 onTickableTriggerClick : function(e)
17754 this.loadNext = false;
17755 this.hasFocus = true;
17757 if(this.triggerAction == 'all') {
17758 this.doQuery(this.allQuery, true);
17760 this.doQuery(this.getRawValue());
17764 onSearchFieldClick : function(e)
17766 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
17767 this.onTickableFooterButtonClick(e, false, false);
17771 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
17776 this.loadNext = false;
17777 this.hasFocus = true;
17779 if(this.triggerAction == 'all') {
17780 this.doQuery(this.allQuery, true);
17782 this.doQuery(this.getRawValue());
17786 listKeyPress : function(e)
17788 //Roo.log('listkeypress');
17789 // scroll to first matching element based on key pres..
17790 if (e.isSpecialKey()) {
17793 var k = String.fromCharCode(e.getKey()).toUpperCase();
17796 var csel = this.view.getSelectedNodes();
17797 var cselitem = false;
17799 var ix = this.view.indexOf(csel[0]);
17800 cselitem = this.store.getAt(ix);
17801 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
17807 this.store.each(function(v) {
17809 // start at existing selection.
17810 if (cselitem.id == v.id) {
17816 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
17817 match = this.store.indexOf(v);
17823 if (match === false) {
17824 return true; // no more action?
17827 this.view.select(match);
17828 var sn = Roo.get(this.view.getSelectedNodes()[0]);
17829 sn.scrollIntoView(sn.dom.parentNode, false);
17832 onViewScroll : function(e, t){
17834 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){
17838 this.hasQuery = true;
17840 this.loading = this.list.select('.loading', true).first();
17842 if(this.loading === null){
17843 this.list.createChild({
17845 cls: 'loading roo-select2-more-results roo-select2-active',
17846 html: 'Loading more results...'
17849 this.loading = this.list.select('.loading', true).first();
17851 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
17853 this.loading.hide();
17856 this.loading.show();
17861 this.loadNext = true;
17863 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
17868 addItem : function(o)
17870 var dv = ''; // display value
17872 if (this.displayField) {
17873 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17875 // this is an error condition!!!
17876 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
17883 var choice = this.choices.createChild({
17885 cls: 'roo-select2-search-choice',
17894 cls: 'roo-select2-search-choice-close fa fa-times',
17899 }, this.searchField);
17901 var close = choice.select('a.roo-select2-search-choice-close', true).first();
17903 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17911 this.inputEl().dom.value = '';
17916 onRemoveItem : function(e, _self, o)
17918 e.preventDefault();
17920 this.lastItem = Roo.apply([], this.item);
17922 var index = this.item.indexOf(o.data) * 1;
17925 Roo.log('not this item?!');
17929 this.item.splice(index, 1);
17934 this.fireEvent('remove', this, e);
17940 syncValue : function()
17942 if(!this.item.length){
17949 Roo.each(this.item, function(i){
17950 if(_this.valueField){
17951 value.push(i[_this.valueField]);
17958 this.value = value.join(',');
17960 if(this.hiddenField){
17961 this.hiddenField.dom.value = this.value;
17964 this.store.fireEvent("datachanged", this.store);
17969 clearItem : function()
17971 if(!this.multiple){
17977 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17985 if(this.tickable && !Roo.isTouch){
17986 this.view.refresh();
17990 inputEl: function ()
17992 if(Roo.isIOS && this.useNativeIOS){
17993 return this.el.select('select.roo-ios-select', true).first();
17996 if(Roo.isTouch && this.mobileTouchView){
17997 return this.el.select('input.form-control',true).first();
18001 return this.searchField;
18004 return this.el.select('input.form-control',true).first();
18007 onTickableFooterButtonClick : function(e, btn, el)
18009 e.preventDefault();
18011 this.lastItem = Roo.apply([], this.item);
18013 if(btn && btn.name == 'cancel'){
18014 this.tickItems = Roo.apply([], this.item);
18023 Roo.each(this.tickItems, function(o){
18031 validate : function()
18033 if(this.getVisibilityEl().hasClass('hidden')){
18037 var v = this.getRawValue();
18040 v = this.getValue();
18043 if(this.disabled || this.allowBlank || v.length){
18048 this.markInvalid();
18052 tickableInputEl : function()
18054 if(!this.tickable || !this.editable){
18055 return this.inputEl();
18058 return this.inputEl().select('.roo-select2-search-field-input', true).first();
18062 getAutoCreateTouchView : function()
18067 cls: 'form-group' //input-group
18073 type : this.inputType,
18074 cls : 'form-control x-combo-noedit',
18075 autocomplete: 'new-password',
18076 placeholder : this.placeholder || '',
18081 input.name = this.name;
18085 input.cls += ' input-' + this.size;
18088 if (this.disabled) {
18089 input.disabled = true;
18093 cls : 'roo-combobox-wrap',
18100 inputblock.cls += ' input-group';
18102 inputblock.cn.unshift({
18104 cls : 'input-group-addon input-group-prepend input-group-text',
18109 if(this.removable && !this.multiple){
18110 inputblock.cls += ' roo-removable';
18112 inputblock.cn.push({
18115 cls : 'roo-combo-removable-btn close'
18119 if(this.hasFeedback && !this.allowBlank){
18121 inputblock.cls += ' has-feedback';
18123 inputblock.cn.push({
18125 cls: 'glyphicon form-control-feedback'
18132 inputblock.cls += (this.before) ? '' : ' input-group';
18134 inputblock.cn.push({
18136 cls : 'input-group-addon input-group-append input-group-text',
18142 var ibwrap = inputblock;
18147 cls: 'roo-select2-choices',
18151 cls: 'roo-select2-search-field',
18164 cls: 'roo-select2-container input-group roo-touchview-combobox ',
18169 cls: 'form-hidden-field'
18175 if(!this.multiple && this.showToggleBtn){
18181 if (this.caret != false) {
18184 cls: 'fa fa-' + this.caret
18191 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
18193 Roo.bootstrap.version == 3 ? caret : '',
18196 cls: 'combobox-clear',
18210 combobox.cls += ' roo-select2-container-multi';
18213 var required = this.allowBlank ? {
18215 style: 'display: none'
18218 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
18219 tooltip : 'This field is required'
18222 var align = this.labelAlign || this.parentLabelAlign();
18224 if (align ==='left' && this.fieldLabel.length) {
18230 cls : 'control-label col-form-label',
18231 html : this.fieldLabel
18235 cls : 'roo-combobox-wrap ',
18242 var labelCfg = cfg.cn[1];
18243 var contentCfg = cfg.cn[2];
18246 if(this.indicatorpos == 'right'){
18251 cls : 'control-label col-form-label',
18255 html : this.fieldLabel
18261 cls : "roo-combobox-wrap ",
18269 labelCfg = cfg.cn[0];
18270 contentCfg = cfg.cn[1];
18275 if(this.labelWidth > 12){
18276 labelCfg.style = "width: " + this.labelWidth + 'px';
18279 if(this.labelWidth < 13 && this.labelmd == 0){
18280 this.labelmd = this.labelWidth;
18283 if(this.labellg > 0){
18284 labelCfg.cls += ' col-lg-' + this.labellg;
18285 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
18288 if(this.labelmd > 0){
18289 labelCfg.cls += ' col-md-' + this.labelmd;
18290 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
18293 if(this.labelsm > 0){
18294 labelCfg.cls += ' col-sm-' + this.labelsm;
18295 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
18298 if(this.labelxs > 0){
18299 labelCfg.cls += ' col-xs-' + this.labelxs;
18300 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
18304 } else if ( this.fieldLabel.length) {
18309 cls : 'control-label',
18310 html : this.fieldLabel
18321 if(this.indicatorpos == 'right'){
18325 cls : 'control-label',
18326 html : this.fieldLabel,
18344 var settings = this;
18346 ['xs','sm','md','lg'].map(function(size){
18347 if (settings[size]) {
18348 cfg.cls += ' col-' + size + '-' + settings[size];
18355 initTouchView : function()
18357 this.renderTouchView();
18359 this.touchViewEl.on('scroll', function(){
18360 this.el.dom.scrollTop = 0;
18363 this.originalValue = this.getValue();
18365 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
18367 this.inputEl().on("click", this.showTouchView, this);
18368 if (this.triggerEl) {
18369 this.triggerEl.on("click", this.showTouchView, this);
18373 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
18374 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
18376 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
18378 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
18379 this.store.on('load', this.onTouchViewLoad, this);
18380 this.store.on('loadexception', this.onTouchViewLoadException, this);
18382 if(this.hiddenName){
18384 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
18386 this.hiddenField.dom.value =
18387 this.hiddenValue !== undefined ? this.hiddenValue :
18388 this.value !== undefined ? this.value : '';
18390 this.el.dom.removeAttribute('name');
18391 this.hiddenField.dom.setAttribute('name', this.hiddenName);
18395 this.choices = this.el.select('ul.roo-select2-choices', true).first();
18396 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
18399 if(this.removable && !this.multiple){
18400 var close = this.closeTriggerEl();
18402 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
18403 close.on('click', this.removeBtnClick, this, close);
18407 * fix the bug in Safari iOS8
18409 this.inputEl().on("focus", function(e){
18410 document.activeElement.blur();
18413 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
18420 renderTouchView : function()
18422 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
18423 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18425 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
18426 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18428 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
18429 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18430 this.touchViewBodyEl.setStyle('overflow', 'auto');
18432 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
18433 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18435 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
18436 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18440 showTouchView : function()
18446 this.touchViewHeaderEl.hide();
18448 if(this.modalTitle.length){
18449 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
18450 this.touchViewHeaderEl.show();
18453 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
18454 this.touchViewEl.show();
18456 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
18458 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
18459 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18461 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18463 if(this.modalTitle.length){
18464 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18467 this.touchViewBodyEl.setHeight(bodyHeight);
18471 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
18473 this.touchViewEl.addClass(['in','show']);
18476 if(this._touchViewMask){
18477 Roo.get(document.body).addClass("x-body-masked");
18478 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18479 this._touchViewMask.setStyle('z-index', 10000);
18480 this._touchViewMask.addClass('show');
18483 this.doTouchViewQuery();
18487 hideTouchView : function()
18489 this.touchViewEl.removeClass(['in','show']);
18493 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
18495 this.touchViewEl.setStyle('display', 'none');
18498 if(this._touchViewMask){
18499 this._touchViewMask.removeClass('show');
18500 Roo.get(document.body).removeClass("x-body-masked");
18504 setTouchViewValue : function()
18511 Roo.each(this.tickItems, function(o){
18516 this.hideTouchView();
18519 doTouchViewQuery : function()
18528 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
18532 if(!this.alwaysQuery || this.mode == 'local'){
18533 this.onTouchViewLoad();
18540 onTouchViewBeforeLoad : function(combo,opts)
18546 onTouchViewLoad : function()
18548 if(this.store.getCount() < 1){
18549 this.onTouchViewEmptyResults();
18553 this.clearTouchView();
18555 var rawValue = this.getRawValue();
18557 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
18559 this.tickItems = [];
18561 this.store.data.each(function(d, rowIndex){
18562 var row = this.touchViewListGroup.createChild(template);
18564 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
18565 row.addClass(d.data.cls);
18568 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
18571 html : d.data[this.displayField]
18574 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
18575 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
18578 row.removeClass('selected');
18579 if(!this.multiple && this.valueField &&
18580 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
18583 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18584 row.addClass('selected');
18587 if(this.multiple && this.valueField &&
18588 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
18592 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18593 this.tickItems.push(d.data);
18596 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
18600 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
18602 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18604 if(this.modalTitle.length){
18605 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18608 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
18610 if(this.mobile_restrict_height && listHeight < bodyHeight){
18611 this.touchViewBodyEl.setHeight(listHeight);
18616 if(firstChecked && listHeight > bodyHeight){
18617 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
18622 onTouchViewLoadException : function()
18624 this.hideTouchView();
18627 onTouchViewEmptyResults : function()
18629 this.clearTouchView();
18631 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
18633 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
18637 clearTouchView : function()
18639 this.touchViewListGroup.dom.innerHTML = '';
18642 onTouchViewClick : function(e, el, o)
18644 e.preventDefault();
18647 var rowIndex = o.rowIndex;
18649 var r = this.store.getAt(rowIndex);
18651 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
18653 if(!this.multiple){
18654 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
18655 c.dom.removeAttribute('checked');
18658 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18660 this.setFromData(r.data);
18662 var close = this.closeTriggerEl();
18668 this.hideTouchView();
18670 this.fireEvent('select', this, r, rowIndex);
18675 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
18676 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
18677 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
18681 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18682 this.addItem(r.data);
18683 this.tickItems.push(r.data);
18687 getAutoCreateNativeIOS : function()
18690 cls: 'form-group' //input-group,
18695 cls : 'roo-ios-select'
18699 combobox.name = this.name;
18702 if (this.disabled) {
18703 combobox.disabled = true;
18706 var settings = this;
18708 ['xs','sm','md','lg'].map(function(size){
18709 if (settings[size]) {
18710 cfg.cls += ' col-' + size + '-' + settings[size];
18720 initIOSView : function()
18722 this.store.on('load', this.onIOSViewLoad, this);
18727 onIOSViewLoad : function()
18729 if(this.store.getCount() < 1){
18733 this.clearIOSView();
18735 if(this.allowBlank) {
18737 var default_text = '-- SELECT --';
18739 if(this.placeholder.length){
18740 default_text = this.placeholder;
18743 if(this.emptyTitle.length){
18744 default_text += ' - ' + this.emptyTitle + ' -';
18747 var opt = this.inputEl().createChild({
18750 html : default_text
18754 o[this.valueField] = 0;
18755 o[this.displayField] = default_text;
18757 this.ios_options.push({
18764 this.store.data.each(function(d, rowIndex){
18768 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
18769 html = d.data[this.displayField];
18774 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
18775 value = d.data[this.valueField];
18784 if(this.value == d.data[this.valueField]){
18785 option['selected'] = true;
18788 var opt = this.inputEl().createChild(option);
18790 this.ios_options.push({
18797 this.inputEl().on('change', function(){
18798 this.fireEvent('select', this);
18803 clearIOSView: function()
18805 this.inputEl().dom.innerHTML = '';
18807 this.ios_options = [];
18810 setIOSValue: function(v)
18814 if(!this.ios_options){
18818 Roo.each(this.ios_options, function(opts){
18820 opts.el.dom.removeAttribute('selected');
18822 if(opts.data[this.valueField] != v){
18826 opts.el.dom.setAttribute('selected', true);
18832 * @cfg {Boolean} grow
18836 * @cfg {Number} growMin
18840 * @cfg {Number} growMax
18849 Roo.apply(Roo.bootstrap.ComboBox, {
18853 cls: 'modal-header',
18875 cls: 'list-group-item',
18879 cls: 'roo-combobox-list-group-item-value'
18883 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
18897 listItemCheckbox : {
18899 cls: 'list-group-item',
18903 cls: 'roo-combobox-list-group-item-value'
18907 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18923 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18928 cls: 'modal-footer',
18936 cls: 'col-xs-6 text-left',
18939 cls: 'btn btn-danger roo-touch-view-cancel',
18945 cls: 'col-xs-6 text-right',
18948 cls: 'btn btn-success roo-touch-view-ok',
18959 Roo.apply(Roo.bootstrap.ComboBox, {
18961 touchViewTemplate : {
18963 cls: 'modal fade roo-combobox-touch-view',
18967 cls: 'modal-dialog',
18968 style : 'position:fixed', // we have to fix position....
18972 cls: 'modal-content',
18974 Roo.bootstrap.ComboBox.header,
18975 Roo.bootstrap.ComboBox.body,
18976 Roo.bootstrap.ComboBox.footer
18985 * Ext JS Library 1.1.1
18986 * Copyright(c) 2006-2007, Ext JS, LLC.
18988 * Originally Released Under LGPL - original licence link has changed is not relivant.
18991 * <script type="text/javascript">
18996 * @extends Roo.util.Observable
18997 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
18998 * This class also supports single and multi selection modes. <br>
18999 * Create a data model bound view:
19001 var store = new Roo.data.Store(...);
19003 var view = new Roo.View({
19005 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
19007 singleSelect: true,
19008 selectedClass: "ydataview-selected",
19012 // listen for node click?
19013 view.on("click", function(vw, index, node, e){
19014 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19018 dataModel.load("foobar.xml");
19020 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19022 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19023 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19025 * Note: old style constructor is still suported (container, template, config)
19028 * Create a new View
19029 * @param {Object} config The config object
19032 Roo.View = function(config, depreciated_tpl, depreciated_config){
19034 this.parent = false;
19036 if (typeof(depreciated_tpl) == 'undefined') {
19037 // new way.. - universal constructor.
19038 Roo.apply(this, config);
19039 this.el = Roo.get(this.el);
19042 this.el = Roo.get(config);
19043 this.tpl = depreciated_tpl;
19044 Roo.apply(this, depreciated_config);
19046 this.wrapEl = this.el.wrap().wrap();
19047 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19050 if(typeof(this.tpl) == "string"){
19051 this.tpl = new Roo.Template(this.tpl);
19053 // support xtype ctors..
19054 this.tpl = new Roo.factory(this.tpl, Roo);
19058 this.tpl.compile();
19063 * @event beforeclick
19064 * Fires before a click is processed. Returns false to cancel the default action.
19065 * @param {Roo.View} this
19066 * @param {Number} index The index of the target node
19067 * @param {HTMLElement} node The target node
19068 * @param {Roo.EventObject} e The raw event object
19070 "beforeclick" : true,
19073 * Fires when a template node is clicked.
19074 * @param {Roo.View} this
19075 * @param {Number} index The index of the target node
19076 * @param {HTMLElement} node The target node
19077 * @param {Roo.EventObject} e The raw event object
19082 * Fires when a template node is double clicked.
19083 * @param {Roo.View} this
19084 * @param {Number} index The index of the target node
19085 * @param {HTMLElement} node The target node
19086 * @param {Roo.EventObject} e The raw event object
19090 * @event contextmenu
19091 * Fires when a template node is right clicked.
19092 * @param {Roo.View} this
19093 * @param {Number} index The index of the target node
19094 * @param {HTMLElement} node The target node
19095 * @param {Roo.EventObject} e The raw event object
19097 "contextmenu" : true,
19099 * @event selectionchange
19100 * Fires when the selected nodes change.
19101 * @param {Roo.View} this
19102 * @param {Array} selections Array of the selected nodes
19104 "selectionchange" : true,
19107 * @event beforeselect
19108 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
19109 * @param {Roo.View} this
19110 * @param {HTMLElement} node The node to be selected
19111 * @param {Array} selections Array of currently selected nodes
19113 "beforeselect" : true,
19115 * @event preparedata
19116 * Fires on every row to render, to allow you to change the data.
19117 * @param {Roo.View} this
19118 * @param {Object} data to be rendered (change this)
19120 "preparedata" : true
19128 "click": this.onClick,
19129 "dblclick": this.onDblClick,
19130 "contextmenu": this.onContextMenu,
19134 this.selections = [];
19136 this.cmp = new Roo.CompositeElementLite([]);
19138 this.store = Roo.factory(this.store, Roo.data);
19139 this.setStore(this.store, true);
19142 if ( this.footer && this.footer.xtype) {
19144 var fctr = this.wrapEl.appendChild(document.createElement("div"));
19146 this.footer.dataSource = this.store;
19147 this.footer.container = fctr;
19148 this.footer = Roo.factory(this.footer, Roo);
19149 fctr.insertFirst(this.el);
19151 // this is a bit insane - as the paging toolbar seems to detach the el..
19152 // dom.parentNode.parentNode.parentNode
19153 // they get detached?
19157 Roo.View.superclass.constructor.call(this);
19162 Roo.extend(Roo.View, Roo.util.Observable, {
19165 * @cfg {Roo.data.Store} store Data store to load data from.
19170 * @cfg {String|Roo.Element} el The container element.
19175 * @cfg {String|Roo.Template} tpl The template used by this View
19179 * @cfg {String} dataName the named area of the template to use as the data area
19180 * Works with domtemplates roo-name="name"
19184 * @cfg {String} selectedClass The css class to add to selected nodes
19186 selectedClass : "x-view-selected",
19188 * @cfg {String} emptyText The empty text to show when nothing is loaded.
19193 * @cfg {String} text to display on mask (default Loading)
19197 * @cfg {Boolean} multiSelect Allow multiple selection
19199 multiSelect : false,
19201 * @cfg {Boolean} singleSelect Allow single selection
19203 singleSelect: false,
19206 * @cfg {Boolean} toggleSelect - selecting
19208 toggleSelect : false,
19211 * @cfg {Boolean} tickable - selecting
19216 * Returns the element this view is bound to.
19217 * @return {Roo.Element}
19219 getEl : function(){
19220 return this.wrapEl;
19226 * Refreshes the view. - called by datachanged on the store. - do not call directly.
19228 refresh : function(){
19229 //Roo.log('refresh');
19232 // if we are using something like 'domtemplate', then
19233 // the what gets used is:
19234 // t.applySubtemplate(NAME, data, wrapping data..)
19235 // the outer template then get' applied with
19236 // the store 'extra data'
19237 // and the body get's added to the
19238 // roo-name="data" node?
19239 // <span class='roo-tpl-{name}'></span> ?????
19243 this.clearSelections();
19244 this.el.update("");
19246 var records = this.store.getRange();
19247 if(records.length < 1) {
19249 // is this valid?? = should it render a template??
19251 this.el.update(this.emptyText);
19255 if (this.dataName) {
19256 this.el.update(t.apply(this.store.meta)); //????
19257 el = this.el.child('.roo-tpl-' + this.dataName);
19260 for(var i = 0, len = records.length; i < len; i++){
19261 var data = this.prepareData(records[i].data, i, records[i]);
19262 this.fireEvent("preparedata", this, data, i, records[i]);
19264 var d = Roo.apply({}, data);
19267 Roo.apply(d, {'roo-id' : Roo.id()});
19271 Roo.each(this.parent.item, function(item){
19272 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
19275 Roo.apply(d, {'roo-data-checked' : 'checked'});
19279 html[html.length] = Roo.util.Format.trim(
19281 t.applySubtemplate(this.dataName, d, this.store.meta) :
19288 el.update(html.join(""));
19289 this.nodes = el.dom.childNodes;
19290 this.updateIndexes(0);
19295 * Function to override to reformat the data that is sent to
19296 * the template for each node.
19297 * DEPRICATED - use the preparedata event handler.
19298 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
19299 * a JSON object for an UpdateManager bound view).
19301 prepareData : function(data, index, record)
19303 this.fireEvent("preparedata", this, data, index, record);
19307 onUpdate : function(ds, record){
19308 // Roo.log('on update');
19309 this.clearSelections();
19310 var index = this.store.indexOf(record);
19311 var n = this.nodes[index];
19312 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
19313 n.parentNode.removeChild(n);
19314 this.updateIndexes(index, index);
19320 onAdd : function(ds, records, index)
19322 //Roo.log(['on Add', ds, records, index] );
19323 this.clearSelections();
19324 if(this.nodes.length == 0){
19328 var n = this.nodes[index];
19329 for(var i = 0, len = records.length; i < len; i++){
19330 var d = this.prepareData(records[i].data, i, records[i]);
19332 this.tpl.insertBefore(n, d);
19335 this.tpl.append(this.el, d);
19338 this.updateIndexes(index);
19341 onRemove : function(ds, record, index){
19342 // Roo.log('onRemove');
19343 this.clearSelections();
19344 var el = this.dataName ?
19345 this.el.child('.roo-tpl-' + this.dataName) :
19348 el.dom.removeChild(this.nodes[index]);
19349 this.updateIndexes(index);
19353 * Refresh an individual node.
19354 * @param {Number} index
19356 refreshNode : function(index){
19357 this.onUpdate(this.store, this.store.getAt(index));
19360 updateIndexes : function(startIndex, endIndex){
19361 var ns = this.nodes;
19362 startIndex = startIndex || 0;
19363 endIndex = endIndex || ns.length - 1;
19364 for(var i = startIndex; i <= endIndex; i++){
19365 ns[i].nodeIndex = i;
19370 * Changes the data store this view uses and refresh the view.
19371 * @param {Store} store
19373 setStore : function(store, initial){
19374 if(!initial && this.store){
19375 this.store.un("datachanged", this.refresh);
19376 this.store.un("add", this.onAdd);
19377 this.store.un("remove", this.onRemove);
19378 this.store.un("update", this.onUpdate);
19379 this.store.un("clear", this.refresh);
19380 this.store.un("beforeload", this.onBeforeLoad);
19381 this.store.un("load", this.onLoad);
19382 this.store.un("loadexception", this.onLoad);
19386 store.on("datachanged", this.refresh, this);
19387 store.on("add", this.onAdd, this);
19388 store.on("remove", this.onRemove, this);
19389 store.on("update", this.onUpdate, this);
19390 store.on("clear", this.refresh, this);
19391 store.on("beforeload", this.onBeforeLoad, this);
19392 store.on("load", this.onLoad, this);
19393 store.on("loadexception", this.onLoad, this);
19401 * onbeforeLoad - masks the loading area.
19404 onBeforeLoad : function(store,opts)
19406 //Roo.log('onBeforeLoad');
19408 this.el.update("");
19410 this.el.mask(this.mask ? this.mask : "Loading" );
19412 onLoad : function ()
19419 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
19420 * @param {HTMLElement} node
19421 * @return {HTMLElement} The template node
19423 findItemFromChild : function(node){
19424 var el = this.dataName ?
19425 this.el.child('.roo-tpl-' + this.dataName,true) :
19428 if(!node || node.parentNode == el){
19431 var p = node.parentNode;
19432 while(p && p != el){
19433 if(p.parentNode == el){
19442 onClick : function(e){
19443 var item = this.findItemFromChild(e.getTarget());
19445 var index = this.indexOf(item);
19446 if(this.onItemClick(item, index, e) !== false){
19447 this.fireEvent("click", this, index, item, e);
19450 this.clearSelections();
19455 onContextMenu : function(e){
19456 var item = this.findItemFromChild(e.getTarget());
19458 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
19463 onDblClick : function(e){
19464 var item = this.findItemFromChild(e.getTarget());
19466 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
19470 onItemClick : function(item, index, e)
19472 if(this.fireEvent("beforeclick", this, index, item, e) === false){
19475 if (this.toggleSelect) {
19476 var m = this.isSelected(item) ? 'unselect' : 'select';
19479 _t[m](item, true, false);
19482 if(this.multiSelect || this.singleSelect){
19483 if(this.multiSelect && e.shiftKey && this.lastSelection){
19484 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
19486 this.select(item, this.multiSelect && e.ctrlKey);
19487 this.lastSelection = item;
19490 if(!this.tickable){
19491 e.preventDefault();
19499 * Get the number of selected nodes.
19502 getSelectionCount : function(){
19503 return this.selections.length;
19507 * Get the currently selected nodes.
19508 * @return {Array} An array of HTMLElements
19510 getSelectedNodes : function(){
19511 return this.selections;
19515 * Get the indexes of the selected nodes.
19518 getSelectedIndexes : function(){
19519 var indexes = [], s = this.selections;
19520 for(var i = 0, len = s.length; i < len; i++){
19521 indexes.push(s[i].nodeIndex);
19527 * Clear all selections
19528 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
19530 clearSelections : function(suppressEvent){
19531 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
19532 this.cmp.elements = this.selections;
19533 this.cmp.removeClass(this.selectedClass);
19534 this.selections = [];
19535 if(!suppressEvent){
19536 this.fireEvent("selectionchange", this, this.selections);
19542 * Returns true if the passed node is selected
19543 * @param {HTMLElement/Number} node The node or node index
19544 * @return {Boolean}
19546 isSelected : function(node){
19547 var s = this.selections;
19551 node = this.getNode(node);
19552 return s.indexOf(node) !== -1;
19557 * @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
19558 * @param {Boolean} keepExisting (optional) true to keep existing selections
19559 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
19561 select : function(nodeInfo, keepExisting, suppressEvent){
19562 if(nodeInfo instanceof Array){
19564 this.clearSelections(true);
19566 for(var i = 0, len = nodeInfo.length; i < len; i++){
19567 this.select(nodeInfo[i], true, true);
19571 var node = this.getNode(nodeInfo);
19572 if(!node || this.isSelected(node)){
19573 return; // already selected.
19576 this.clearSelections(true);
19579 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
19580 Roo.fly(node).addClass(this.selectedClass);
19581 this.selections.push(node);
19582 if(!suppressEvent){
19583 this.fireEvent("selectionchange", this, this.selections);
19591 * @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
19592 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
19593 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
19595 unselect : function(nodeInfo, keepExisting, suppressEvent)
19597 if(nodeInfo instanceof Array){
19598 Roo.each(this.selections, function(s) {
19599 this.unselect(s, nodeInfo);
19603 var node = this.getNode(nodeInfo);
19604 if(!node || !this.isSelected(node)){
19605 //Roo.log("not selected");
19606 return; // not selected.
19610 Roo.each(this.selections, function(s) {
19612 Roo.fly(node).removeClass(this.selectedClass);
19619 this.selections= ns;
19620 this.fireEvent("selectionchange", this, this.selections);
19624 * Gets a template node.
19625 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19626 * @return {HTMLElement} The node or null if it wasn't found
19628 getNode : function(nodeInfo){
19629 if(typeof nodeInfo == "string"){
19630 return document.getElementById(nodeInfo);
19631 }else if(typeof nodeInfo == "number"){
19632 return this.nodes[nodeInfo];
19638 * Gets a range template nodes.
19639 * @param {Number} startIndex
19640 * @param {Number} endIndex
19641 * @return {Array} An array of nodes
19643 getNodes : function(start, end){
19644 var ns = this.nodes;
19645 start = start || 0;
19646 end = typeof end == "undefined" ? ns.length - 1 : end;
19649 for(var i = start; i <= end; i++){
19653 for(var i = start; i >= end; i--){
19661 * Finds the index of the passed node
19662 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19663 * @return {Number} The index of the node or -1
19665 indexOf : function(node){
19666 node = this.getNode(node);
19667 if(typeof node.nodeIndex == "number"){
19668 return node.nodeIndex;
19670 var ns = this.nodes;
19671 for(var i = 0, len = ns.length; i < len; i++){
19682 * based on jquery fullcalendar
19686 Roo.bootstrap = Roo.bootstrap || {};
19688 * @class Roo.bootstrap.Calendar
19689 * @extends Roo.bootstrap.Component
19690 * Bootstrap Calendar class
19691 * @cfg {Boolean} loadMask (true|false) default false
19692 * @cfg {Object} header generate the user specific header of the calendar, default false
19695 * Create a new Container
19696 * @param {Object} config The config object
19701 Roo.bootstrap.Calendar = function(config){
19702 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
19706 * Fires when a date is selected
19707 * @param {DatePicker} this
19708 * @param {Date} date The selected date
19712 * @event monthchange
19713 * Fires when the displayed month changes
19714 * @param {DatePicker} this
19715 * @param {Date} date The selected month
19717 'monthchange': true,
19719 * @event evententer
19720 * Fires when mouse over an event
19721 * @param {Calendar} this
19722 * @param {event} Event
19724 'evententer': true,
19726 * @event eventleave
19727 * Fires when the mouse leaves an
19728 * @param {Calendar} this
19731 'eventleave': true,
19733 * @event eventclick
19734 * Fires when the mouse click an
19735 * @param {Calendar} this
19744 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
19747 * @cfg {Number} startDay
19748 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
19756 getAutoCreate : function(){
19759 var fc_button = function(name, corner, style, content ) {
19760 return Roo.apply({},{
19762 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
19764 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
19767 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
19778 style : 'width:100%',
19785 cls : 'fc-header-left',
19787 fc_button('prev', 'left', 'arrow', '‹' ),
19788 fc_button('next', 'right', 'arrow', '›' ),
19789 { tag: 'span', cls: 'fc-header-space' },
19790 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
19798 cls : 'fc-header-center',
19802 cls: 'fc-header-title',
19805 html : 'month / year'
19813 cls : 'fc-header-right',
19815 /* fc_button('month', 'left', '', 'month' ),
19816 fc_button('week', '', '', 'week' ),
19817 fc_button('day', 'right', '', 'day' )
19829 header = this.header;
19832 var cal_heads = function() {
19834 // fixme - handle this.
19836 for (var i =0; i < Date.dayNames.length; i++) {
19837 var d = Date.dayNames[i];
19840 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
19841 html : d.substring(0,3)
19845 ret[0].cls += ' fc-first';
19846 ret[6].cls += ' fc-last';
19849 var cal_cell = function(n) {
19852 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
19857 cls: 'fc-day-number',
19861 cls: 'fc-day-content',
19865 style: 'position: relative;' // height: 17px;
19877 var cal_rows = function() {
19880 for (var r = 0; r < 6; r++) {
19887 for (var i =0; i < Date.dayNames.length; i++) {
19888 var d = Date.dayNames[i];
19889 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
19892 row.cn[0].cls+=' fc-first';
19893 row.cn[0].cn[0].style = 'min-height:90px';
19894 row.cn[6].cls+=' fc-last';
19898 ret[0].cls += ' fc-first';
19899 ret[4].cls += ' fc-prev-last';
19900 ret[5].cls += ' fc-last';
19907 cls: 'fc-border-separate',
19908 style : 'width:100%',
19916 cls : 'fc-first fc-last',
19934 cls : 'fc-content',
19935 style : "position: relative;",
19938 cls : 'fc-view fc-view-month fc-grid',
19939 style : 'position: relative',
19940 unselectable : 'on',
19943 cls : 'fc-event-container',
19944 style : 'position:absolute;z-index:8;top:0;left:0;'
19962 initEvents : function()
19965 throw "can not find store for calendar";
19971 style: "text-align:center",
19975 style: "background-color:white;width:50%;margin:250 auto",
19979 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
19990 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19992 var size = this.el.select('.fc-content', true).first().getSize();
19993 this.maskEl.setSize(size.width, size.height);
19994 this.maskEl.enableDisplayMode("block");
19995 if(!this.loadMask){
19996 this.maskEl.hide();
19999 this.store = Roo.factory(this.store, Roo.data);
20000 this.store.on('load', this.onLoad, this);
20001 this.store.on('beforeload', this.onBeforeLoad, this);
20005 this.cells = this.el.select('.fc-day',true);
20006 //Roo.log(this.cells);
20007 this.textNodes = this.el.query('.fc-day-number');
20008 this.cells.addClassOnOver('fc-state-hover');
20010 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20011 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20012 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20013 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20015 this.on('monthchange', this.onMonthChange, this);
20017 this.update(new Date().clearTime());
20020 resize : function() {
20021 var sz = this.el.getSize();
20023 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20024 this.el.select('.fc-day-content div',true).setHeight(34);
20029 showPrevMonth : function(e){
20030 this.update(this.activeDate.add("mo", -1));
20032 showToday : function(e){
20033 this.update(new Date().clearTime());
20036 showNextMonth : function(e){
20037 this.update(this.activeDate.add("mo", 1));
20041 showPrevYear : function(){
20042 this.update(this.activeDate.add("y", -1));
20046 showNextYear : function(){
20047 this.update(this.activeDate.add("y", 1));
20052 update : function(date)
20054 var vd = this.activeDate;
20055 this.activeDate = date;
20056 // if(vd && this.el){
20057 // var t = date.getTime();
20058 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20059 // Roo.log('using add remove');
20061 // this.fireEvent('monthchange', this, date);
20063 // this.cells.removeClass("fc-state-highlight");
20064 // this.cells.each(function(c){
20065 // if(c.dateValue == t){
20066 // c.addClass("fc-state-highlight");
20067 // setTimeout(function(){
20068 // try{c.dom.firstChild.focus();}catch(e){}
20078 var days = date.getDaysInMonth();
20080 var firstOfMonth = date.getFirstDateOfMonth();
20081 var startingPos = firstOfMonth.getDay()-this.startDay;
20083 if(startingPos < this.startDay){
20087 var pm = date.add(Date.MONTH, -1);
20088 var prevStart = pm.getDaysInMonth()-startingPos;
20090 this.cells = this.el.select('.fc-day',true);
20091 this.textNodes = this.el.query('.fc-day-number');
20092 this.cells.addClassOnOver('fc-state-hover');
20094 var cells = this.cells.elements;
20095 var textEls = this.textNodes;
20097 Roo.each(cells, function(cell){
20098 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
20101 days += startingPos;
20103 // convert everything to numbers so it's fast
20104 var day = 86400000;
20105 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
20108 //Roo.log(prevStart);
20110 var today = new Date().clearTime().getTime();
20111 var sel = date.clearTime().getTime();
20112 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
20113 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
20114 var ddMatch = this.disabledDatesRE;
20115 var ddText = this.disabledDatesText;
20116 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
20117 var ddaysText = this.disabledDaysText;
20118 var format = this.format;
20120 var setCellClass = function(cal, cell){
20124 //Roo.log('set Cell Class');
20126 var t = d.getTime();
20130 cell.dateValue = t;
20132 cell.className += " fc-today";
20133 cell.className += " fc-state-highlight";
20134 cell.title = cal.todayText;
20137 // disable highlight in other month..
20138 //cell.className += " fc-state-highlight";
20143 cell.className = " fc-state-disabled";
20144 cell.title = cal.minText;
20148 cell.className = " fc-state-disabled";
20149 cell.title = cal.maxText;
20153 if(ddays.indexOf(d.getDay()) != -1){
20154 cell.title = ddaysText;
20155 cell.className = " fc-state-disabled";
20158 if(ddMatch && format){
20159 var fvalue = d.dateFormat(format);
20160 if(ddMatch.test(fvalue)){
20161 cell.title = ddText.replace("%0", fvalue);
20162 cell.className = " fc-state-disabled";
20166 if (!cell.initialClassName) {
20167 cell.initialClassName = cell.dom.className;
20170 cell.dom.className = cell.initialClassName + ' ' + cell.className;
20175 for(; i < startingPos; i++) {
20176 textEls[i].innerHTML = (++prevStart);
20177 d.setDate(d.getDate()+1);
20179 cells[i].className = "fc-past fc-other-month";
20180 setCellClass(this, cells[i]);
20185 for(; i < days; i++){
20186 intDay = i - startingPos + 1;
20187 textEls[i].innerHTML = (intDay);
20188 d.setDate(d.getDate()+1);
20190 cells[i].className = ''; // "x-date-active";
20191 setCellClass(this, cells[i]);
20195 for(; i < 42; i++) {
20196 textEls[i].innerHTML = (++extraDays);
20197 d.setDate(d.getDate()+1);
20199 cells[i].className = "fc-future fc-other-month";
20200 setCellClass(this, cells[i]);
20203 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
20205 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
20207 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
20208 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
20210 if(totalRows != 6){
20211 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
20212 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
20215 this.fireEvent('monthchange', this, date);
20219 if(!this.internalRender){
20220 var main = this.el.dom.firstChild;
20221 var w = main.offsetWidth;
20222 this.el.setWidth(w + this.el.getBorderWidth("lr"));
20223 Roo.fly(main).setWidth(w);
20224 this.internalRender = true;
20225 // opera does not respect the auto grow header center column
20226 // then, after it gets a width opera refuses to recalculate
20227 // without a second pass
20228 if(Roo.isOpera && !this.secondPass){
20229 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
20230 this.secondPass = true;
20231 this.update.defer(10, this, [date]);
20238 findCell : function(dt) {
20239 dt = dt.clearTime().getTime();
20241 this.cells.each(function(c){
20242 //Roo.log("check " +c.dateValue + '?=' + dt);
20243 if(c.dateValue == dt){
20253 findCells : function(ev) {
20254 var s = ev.start.clone().clearTime().getTime();
20256 var e= ev.end.clone().clearTime().getTime();
20259 this.cells.each(function(c){
20260 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
20262 if(c.dateValue > e){
20265 if(c.dateValue < s){
20274 // findBestRow: function(cells)
20278 // for (var i =0 ; i < cells.length;i++) {
20279 // ret = Math.max(cells[i].rows || 0,ret);
20286 addItem : function(ev)
20288 // look for vertical location slot in
20289 var cells = this.findCells(ev);
20291 // ev.row = this.findBestRow(cells);
20293 // work out the location.
20297 for(var i =0; i < cells.length; i++) {
20299 cells[i].row = cells[0].row;
20302 cells[i].row = cells[i].row + 1;
20312 if (crow.start.getY() == cells[i].getY()) {
20314 crow.end = cells[i];
20331 cells[0].events.push(ev);
20333 this.calevents.push(ev);
20336 clearEvents: function() {
20338 if(!this.calevents){
20342 Roo.each(this.cells.elements, function(c){
20348 Roo.each(this.calevents, function(e) {
20349 Roo.each(e.els, function(el) {
20350 el.un('mouseenter' ,this.onEventEnter, this);
20351 el.un('mouseleave' ,this.onEventLeave, this);
20356 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
20362 renderEvents: function()
20366 this.cells.each(function(c) {
20375 if(c.row != c.events.length){
20376 r = 4 - (4 - (c.row - c.events.length));
20379 c.events = ev.slice(0, r);
20380 c.more = ev.slice(r);
20382 if(c.more.length && c.more.length == 1){
20383 c.events.push(c.more.pop());
20386 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
20390 this.cells.each(function(c) {
20392 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
20395 for (var e = 0; e < c.events.length; e++){
20396 var ev = c.events[e];
20397 var rows = ev.rows;
20399 for(var i = 0; i < rows.length; i++) {
20401 // how many rows should it span..
20404 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
20405 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
20407 unselectable : "on",
20410 cls: 'fc-event-inner',
20414 // cls: 'fc-event-time',
20415 // html : cells.length > 1 ? '' : ev.time
20419 cls: 'fc-event-title',
20420 html : String.format('{0}', ev.title)
20427 cls: 'ui-resizable-handle ui-resizable-e',
20428 html : '  '
20435 cfg.cls += ' fc-event-start';
20437 if ((i+1) == rows.length) {
20438 cfg.cls += ' fc-event-end';
20441 var ctr = _this.el.select('.fc-event-container',true).first();
20442 var cg = ctr.createChild(cfg);
20444 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
20445 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
20447 var r = (c.more.length) ? 1 : 0;
20448 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
20449 cg.setWidth(ebox.right - sbox.x -2);
20451 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
20452 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
20453 cg.on('click', _this.onEventClick, _this, ev);
20464 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
20465 style : 'position: absolute',
20466 unselectable : "on",
20469 cls: 'fc-event-inner',
20473 cls: 'fc-event-title',
20481 cls: 'ui-resizable-handle ui-resizable-e',
20482 html : '  '
20488 var ctr = _this.el.select('.fc-event-container',true).first();
20489 var cg = ctr.createChild(cfg);
20491 var sbox = c.select('.fc-day-content',true).first().getBox();
20492 var ebox = c.select('.fc-day-content',true).first().getBox();
20494 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
20495 cg.setWidth(ebox.right - sbox.x -2);
20497 cg.on('click', _this.onMoreEventClick, _this, c.more);
20507 onEventEnter: function (e, el,event,d) {
20508 this.fireEvent('evententer', this, el, event);
20511 onEventLeave: function (e, el,event,d) {
20512 this.fireEvent('eventleave', this, el, event);
20515 onEventClick: function (e, el,event,d) {
20516 this.fireEvent('eventclick', this, el, event);
20519 onMonthChange: function () {
20523 onMoreEventClick: function(e, el, more)
20527 this.calpopover.placement = 'right';
20528 this.calpopover.setTitle('More');
20530 this.calpopover.setContent('');
20532 var ctr = this.calpopover.el.select('.popover-content', true).first();
20534 Roo.each(more, function(m){
20536 cls : 'fc-event-hori fc-event-draggable',
20539 var cg = ctr.createChild(cfg);
20541 cg.on('click', _this.onEventClick, _this, m);
20544 this.calpopover.show(el);
20549 onLoad: function ()
20551 this.calevents = [];
20554 if(this.store.getCount() > 0){
20555 this.store.data.each(function(d){
20558 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
20559 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
20560 time : d.data.start_time,
20561 title : d.data.title,
20562 description : d.data.description,
20563 venue : d.data.venue
20568 this.renderEvents();
20570 if(this.calevents.length && this.loadMask){
20571 this.maskEl.hide();
20575 onBeforeLoad: function()
20577 this.clearEvents();
20579 this.maskEl.show();
20593 * @class Roo.bootstrap.Popover
20594 * @extends Roo.bootstrap.Component
20595 * Bootstrap Popover class
20596 * @cfg {String} html contents of the popover (or false to use children..)
20597 * @cfg {String} title of popover (or false to hide)
20598 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
20599 * @cfg {String} trigger click || hover (or false to trigger manually)
20600 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
20601 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
20602 * - if false and it has a 'parent' then it will be automatically added to that element
20603 * - if string - Roo.get will be called
20604 * @cfg {Number} delay - delay before showing
20607 * Create a new Popover
20608 * @param {Object} config The config object
20611 Roo.bootstrap.Popover = function(config){
20612 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
20618 * After the popover show
20620 * @param {Roo.bootstrap.Popover} this
20625 * After the popover hide
20627 * @param {Roo.bootstrap.Popover} this
20633 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
20638 placement : 'right',
20639 trigger : 'hover', // hover
20645 can_build_overlaid : false,
20647 maskEl : false, // the mask element
20650 alignEl : false, // when show is called with an element - this get's stored.
20652 getChildContainer : function()
20654 return this.contentEl;
20657 getPopoverHeader : function()
20659 this.title = true; // flag not to hide it..
20660 this.headerEl.addClass('p-0');
20661 return this.headerEl
20665 getAutoCreate : function(){
20668 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
20669 style: 'display:block',
20675 cls : 'popover-inner ',
20679 cls: 'popover-title popover-header',
20680 html : this.title === false ? '' : this.title
20683 cls : 'popover-content popover-body ' + (this.cls || ''),
20684 html : this.html || ''
20695 * @param {string} the title
20697 setTitle: function(str)
20701 this.headerEl.dom.innerHTML = str;
20706 * @param {string} the body content
20708 setContent: function(str)
20711 if (this.contentEl) {
20712 this.contentEl.dom.innerHTML = str;
20716 // as it get's added to the bottom of the page.
20717 onRender : function(ct, position)
20719 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
20724 var cfg = Roo.apply({}, this.getAutoCreate());
20728 cfg.cls += ' ' + this.cls;
20731 cfg.style = this.style;
20733 //Roo.log("adding to ");
20734 this.el = Roo.get(document.body).createChild(cfg, position);
20735 // Roo.log(this.el);
20738 this.contentEl = this.el.select('.popover-content',true).first();
20739 this.headerEl = this.el.select('.popover-title',true).first();
20742 if(typeof(this.items) != 'undefined'){
20743 var items = this.items;
20746 for(var i =0;i < items.length;i++) {
20747 nitems.push(this.addxtype(Roo.apply({}, items[i])));
20751 this.items = nitems;
20753 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
20754 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
20761 resizeMask : function()
20763 this.maskEl.setSize(
20764 Roo.lib.Dom.getViewWidth(true),
20765 Roo.lib.Dom.getViewHeight(true)
20769 initEvents : function()
20773 Roo.bootstrap.Popover.register(this);
20776 this.arrowEl = this.el.select('.arrow',true).first();
20777 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
20778 this.el.enableDisplayMode('block');
20782 if (this.over === false && !this.parent()) {
20785 if (this.triggers === false) {
20790 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
20791 var triggers = this.trigger ? this.trigger.split(' ') : [];
20792 Roo.each(triggers, function(trigger) {
20794 if (trigger == 'click') {
20795 on_el.on('click', this.toggle, this);
20796 } else if (trigger != 'manual') {
20797 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
20798 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
20800 on_el.on(eventIn ,this.enter, this);
20801 on_el.on(eventOut, this.leave, this);
20811 toggle : function () {
20812 this.hoverState == 'in' ? this.leave() : this.enter();
20815 enter : function () {
20817 clearTimeout(this.timeout);
20819 this.hoverState = 'in';
20821 if (!this.delay || !this.delay.show) {
20826 this.timeout = setTimeout(function () {
20827 if (_t.hoverState == 'in') {
20830 }, this.delay.show)
20833 leave : function() {
20834 clearTimeout(this.timeout);
20836 this.hoverState = 'out';
20838 if (!this.delay || !this.delay.hide) {
20843 this.timeout = setTimeout(function () {
20844 if (_t.hoverState == 'out') {
20847 }, this.delay.hide)
20851 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
20852 * @param {string} (left|right|top|bottom) position
20854 show : function (on_el, placement)
20856 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
20857 on_el = on_el || false; // default to false
20860 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
20861 on_el = this.parent().el;
20862 } else if (this.over) {
20863 on_el = Roo.get(this.over);
20868 this.alignEl = Roo.get( on_el );
20871 this.render(document.body);
20877 if (this.title === false) {
20878 this.headerEl.hide();
20883 this.el.dom.style.display = 'block';
20886 if (this.alignEl) {
20887 this.updatePosition(this.placement, true);
20890 // this is usually just done by the builder = to show the popoup in the middle of the scren.
20891 var es = this.el.getSize();
20892 var x = Roo.lib.Dom.getViewWidth()/2;
20893 var y = Roo.lib.Dom.getViewHeight()/2;
20894 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
20899 //var arrow = this.el.select('.arrow',true).first();
20900 //arrow.set(align[2],
20902 this.el.addClass('in');
20906 this.hoverState = 'in';
20909 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
20910 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20911 this.maskEl.dom.style.display = 'block';
20912 this.maskEl.addClass('show');
20914 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20916 this.fireEvent('show', this);
20920 * fire this manually after loading a grid in the table for example
20921 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20922 * @param {Boolean} try and move it if we cant get right position.
20924 updatePosition : function(placement, try_move)
20926 // allow for calling with no parameters
20927 placement = placement ? placement : this.placement;
20928 try_move = typeof(try_move) == 'undefined' ? true : try_move;
20930 this.el.removeClass([
20931 'fade','top','bottom', 'left', 'right','in',
20932 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20934 this.el.addClass(placement + ' bs-popover-' + placement);
20936 if (!this.alignEl ) {
20940 switch (placement) {
20942 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20943 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20944 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20945 //normal display... or moved up/down.
20946 this.el.setXY(offset);
20947 var xy = this.alignEl.getAnchorXY('tr', false);
20949 this.arrowEl.setXY(xy);
20952 // continue through...
20953 return this.updatePosition('left', false);
20957 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20958 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20959 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20960 //normal display... or moved up/down.
20961 this.el.setXY(offset);
20962 var xy = this.alignEl.getAnchorXY('tl', false);
20963 xy[0]-=10;xy[1]+=5; // << fix me
20964 this.arrowEl.setXY(xy);
20968 return this.updatePosition('right', false);
20971 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20972 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20973 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20974 //normal display... or moved up/down.
20975 this.el.setXY(offset);
20976 var xy = this.alignEl.getAnchorXY('t', false);
20977 xy[1]-=10; // << fix me
20978 this.arrowEl.setXY(xy);
20982 return this.updatePosition('bottom', false);
20985 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20986 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20987 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20988 //normal display... or moved up/down.
20989 this.el.setXY(offset);
20990 var xy = this.alignEl.getAnchorXY('b', false);
20991 xy[1]+=2; // << fix me
20992 this.arrowEl.setXY(xy);
20996 return this.updatePosition('top', false);
21007 this.el.setXY([0,0]);
21008 this.el.removeClass('in');
21010 this.hoverState = null;
21011 this.maskEl.hide(); // always..
21012 this.fireEvent('hide', this);
21018 Roo.apply(Roo.bootstrap.Popover, {
21021 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21022 'right' : ['l-br', [10,0], 'right bs-popover-right'],
21023 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21024 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21029 clickHander : false,
21033 onMouseDown : function(e)
21035 if (this.popups.length && !e.getTarget(".roo-popover")) {
21036 /// what is nothing is showing..
21045 register : function(popup)
21047 if (!Roo.bootstrap.Popover.clickHandler) {
21048 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21050 // hide other popups.
21051 popup.on('show', Roo.bootstrap.Popover.onShow, popup);
21052 popup.on('hide', Roo.bootstrap.Popover.onHide, popup);
21053 this.hideAll(); //<< why?
21054 //this.popups.push(popup);
21056 hideAll : function()
21058 this.popups.forEach(function(p) {
21062 onShow : function() {
21063 Roo.bootstrap.Popover.popups.push(this);
21065 onHide : function() {
21066 Roo.bootstrap.Popover.popups.remove(this);
21072 * Card header - holder for the card header elements.
21077 * @class Roo.bootstrap.PopoverNav
21078 * @extends Roo.bootstrap.NavGroup
21079 * Bootstrap Popover header navigation class
21081 * Create a new Popover Header Navigation
21082 * @param {Object} config The config object
21085 Roo.bootstrap.PopoverNav = function(config){
21086 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
21089 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
21092 container_method : 'getPopoverHeader'
21110 * @class Roo.bootstrap.Progress
21111 * @extends Roo.bootstrap.Component
21112 * Bootstrap Progress class
21113 * @cfg {Boolean} striped striped of the progress bar
21114 * @cfg {Boolean} active animated of the progress bar
21118 * Create a new Progress
21119 * @param {Object} config The config object
21122 Roo.bootstrap.Progress = function(config){
21123 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
21126 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
21131 getAutoCreate : function(){
21139 cfg.cls += ' progress-striped';
21143 cfg.cls += ' active';
21162 * @class Roo.bootstrap.ProgressBar
21163 * @extends Roo.bootstrap.Component
21164 * Bootstrap ProgressBar class
21165 * @cfg {Number} aria_valuenow aria-value now
21166 * @cfg {Number} aria_valuemin aria-value min
21167 * @cfg {Number} aria_valuemax aria-value max
21168 * @cfg {String} label label for the progress bar
21169 * @cfg {String} panel (success | info | warning | danger )
21170 * @cfg {String} role role of the progress bar
21171 * @cfg {String} sr_only text
21175 * Create a new ProgressBar
21176 * @param {Object} config The config object
21179 Roo.bootstrap.ProgressBar = function(config){
21180 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
21183 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
21187 aria_valuemax : 100,
21193 getAutoCreate : function()
21198 cls: 'progress-bar',
21199 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
21211 cfg.role = this.role;
21214 if(this.aria_valuenow){
21215 cfg['aria-valuenow'] = this.aria_valuenow;
21218 if(this.aria_valuemin){
21219 cfg['aria-valuemin'] = this.aria_valuemin;
21222 if(this.aria_valuemax){
21223 cfg['aria-valuemax'] = this.aria_valuemax;
21226 if(this.label && !this.sr_only){
21227 cfg.html = this.label;
21231 cfg.cls += ' progress-bar-' + this.panel;
21237 update : function(aria_valuenow)
21239 this.aria_valuenow = aria_valuenow;
21241 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
21256 * @class Roo.bootstrap.TabGroup
21257 * @extends Roo.bootstrap.Column
21258 * Bootstrap Column class
21259 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
21260 * @cfg {Boolean} carousel true to make the group behave like a carousel
21261 * @cfg {Boolean} bullets show bullets for the panels
21262 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
21263 * @cfg {Number} timer auto slide timer .. default 0 millisecond
21264 * @cfg {Boolean} showarrow (true|false) show arrow default true
21267 * Create a new TabGroup
21268 * @param {Object} config The config object
21271 Roo.bootstrap.TabGroup = function(config){
21272 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
21274 this.navId = Roo.id();
21277 Roo.bootstrap.TabGroup.register(this);
21281 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
21284 transition : false,
21289 slideOnTouch : false,
21292 getAutoCreate : function()
21294 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
21296 cfg.cls += ' tab-content';
21298 if (this.carousel) {
21299 cfg.cls += ' carousel slide';
21302 cls : 'carousel-inner',
21306 if(this.bullets && !Roo.isTouch){
21309 cls : 'carousel-bullets',
21313 if(this.bullets_cls){
21314 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
21321 cfg.cn[0].cn.push(bullets);
21324 if(this.showarrow){
21325 cfg.cn[0].cn.push({
21327 class : 'carousel-arrow',
21331 class : 'carousel-prev',
21335 class : 'fa fa-chevron-left'
21341 class : 'carousel-next',
21345 class : 'fa fa-chevron-right'
21358 initEvents: function()
21360 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
21361 // this.el.on("touchstart", this.onTouchStart, this);
21364 if(this.autoslide){
21367 this.slideFn = window.setInterval(function() {
21368 _this.showPanelNext();
21372 if(this.showarrow){
21373 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
21374 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
21380 // onTouchStart : function(e, el, o)
21382 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
21386 // this.showPanelNext();
21390 getChildContainer : function()
21392 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
21396 * register a Navigation item
21397 * @param {Roo.bootstrap.NavItem} the navitem to add
21399 register : function(item)
21401 this.tabs.push( item);
21402 item.navId = this.navId; // not really needed..
21407 getActivePanel : function()
21410 Roo.each(this.tabs, function(t) {
21420 getPanelByName : function(n)
21423 Roo.each(this.tabs, function(t) {
21424 if (t.tabId == n) {
21432 indexOfPanel : function(p)
21435 Roo.each(this.tabs, function(t,i) {
21436 if (t.tabId == p.tabId) {
21445 * show a specific panel
21446 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
21447 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
21449 showPanel : function (pan)
21451 if(this.transition || typeof(pan) == 'undefined'){
21452 Roo.log("waiting for the transitionend");
21456 if (typeof(pan) == 'number') {
21457 pan = this.tabs[pan];
21460 if (typeof(pan) == 'string') {
21461 pan = this.getPanelByName(pan);
21464 var cur = this.getActivePanel();
21467 Roo.log('pan or acitve pan is undefined');
21471 if (pan.tabId == this.getActivePanel().tabId) {
21475 if (false === cur.fireEvent('beforedeactivate')) {
21479 if(this.bullets > 0 && !Roo.isTouch){
21480 this.setActiveBullet(this.indexOfPanel(pan));
21483 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
21485 //class="carousel-item carousel-item-next carousel-item-left"
21487 this.transition = true;
21488 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
21489 var lr = dir == 'next' ? 'left' : 'right';
21490 pan.el.addClass(dir); // or prev
21491 pan.el.addClass('carousel-item-' + dir); // or prev
21492 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
21493 cur.el.addClass(lr); // or right
21494 pan.el.addClass(lr);
21495 cur.el.addClass('carousel-item-' +lr); // or right
21496 pan.el.addClass('carousel-item-' +lr);
21500 cur.el.on('transitionend', function() {
21501 Roo.log("trans end?");
21503 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
21504 pan.setActive(true);
21506 cur.el.removeClass([lr, 'carousel-item-' + lr]);
21507 cur.setActive(false);
21509 _this.transition = false;
21511 }, this, { single: true } );
21516 cur.setActive(false);
21517 pan.setActive(true);
21522 showPanelNext : function()
21524 var i = this.indexOfPanel(this.getActivePanel());
21526 if (i >= this.tabs.length - 1 && !this.autoslide) {
21530 if (i >= this.tabs.length - 1 && this.autoslide) {
21534 this.showPanel(this.tabs[i+1]);
21537 showPanelPrev : function()
21539 var i = this.indexOfPanel(this.getActivePanel());
21541 if (i < 1 && !this.autoslide) {
21545 if (i < 1 && this.autoslide) {
21546 i = this.tabs.length;
21549 this.showPanel(this.tabs[i-1]);
21553 addBullet: function()
21555 if(!this.bullets || Roo.isTouch){
21558 var ctr = this.el.select('.carousel-bullets',true).first();
21559 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
21560 var bullet = ctr.createChild({
21561 cls : 'bullet bullet-' + i
21562 },ctr.dom.lastChild);
21567 bullet.on('click', (function(e, el, o, ii, t){
21569 e.preventDefault();
21571 this.showPanel(ii);
21573 if(this.autoslide && this.slideFn){
21574 clearInterval(this.slideFn);
21575 this.slideFn = window.setInterval(function() {
21576 _this.showPanelNext();
21580 }).createDelegate(this, [i, bullet], true));
21585 setActiveBullet : function(i)
21591 Roo.each(this.el.select('.bullet', true).elements, function(el){
21592 el.removeClass('selected');
21595 var bullet = this.el.select('.bullet-' + i, true).first();
21601 bullet.addClass('selected');
21612 Roo.apply(Roo.bootstrap.TabGroup, {
21616 * register a Navigation Group
21617 * @param {Roo.bootstrap.NavGroup} the navgroup to add
21619 register : function(navgrp)
21621 this.groups[navgrp.navId] = navgrp;
21625 * fetch a Navigation Group based on the navigation ID
21626 * if one does not exist , it will get created.
21627 * @param {string} the navgroup to add
21628 * @returns {Roo.bootstrap.NavGroup} the navgroup
21630 get: function(navId) {
21631 if (typeof(this.groups[navId]) == 'undefined') {
21632 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
21634 return this.groups[navId] ;
21649 * @class Roo.bootstrap.TabPanel
21650 * @extends Roo.bootstrap.Component
21651 * Bootstrap TabPanel class
21652 * @cfg {Boolean} active panel active
21653 * @cfg {String} html panel content
21654 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
21655 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
21656 * @cfg {String} href click to link..
21657 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
21661 * Create a new TabPanel
21662 * @param {Object} config The config object
21665 Roo.bootstrap.TabPanel = function(config){
21666 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
21670 * Fires when the active status changes
21671 * @param {Roo.bootstrap.TabPanel} this
21672 * @param {Boolean} state the new state
21677 * @event beforedeactivate
21678 * Fires before a tab is de-activated - can be used to do validation on a form.
21679 * @param {Roo.bootstrap.TabPanel} this
21680 * @return {Boolean} false if there is an error
21683 'beforedeactivate': true
21686 this.tabId = this.tabId || Roo.id();
21690 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
21697 touchSlide : false,
21698 getAutoCreate : function(){
21703 // item is needed for carousel - not sure if it has any effect otherwise
21704 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
21705 html: this.html || ''
21709 cfg.cls += ' active';
21713 cfg.tabId = this.tabId;
21721 initEvents: function()
21723 var p = this.parent();
21725 this.navId = this.navId || p.navId;
21727 if (typeof(this.navId) != 'undefined') {
21728 // not really needed.. but just in case.. parent should be a NavGroup.
21729 var tg = Roo.bootstrap.TabGroup.get(this.navId);
21733 var i = tg.tabs.length - 1;
21735 if(this.active && tg.bullets > 0 && i < tg.bullets){
21736 tg.setActiveBullet(i);
21740 this.el.on('click', this.onClick, this);
21742 if(Roo.isTouch && this.touchSlide){
21743 this.el.on("touchstart", this.onTouchStart, this);
21744 this.el.on("touchmove", this.onTouchMove, this);
21745 this.el.on("touchend", this.onTouchEnd, this);
21750 onRender : function(ct, position)
21752 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
21755 setActive : function(state)
21757 Roo.log("panel - set active " + this.tabId + "=" + state);
21759 this.active = state;
21761 this.el.removeClass('active');
21763 } else if (!this.el.hasClass('active')) {
21764 this.el.addClass('active');
21767 this.fireEvent('changed', this, state);
21770 onClick : function(e)
21772 e.preventDefault();
21774 if(!this.href.length){
21778 window.location.href = this.href;
21787 onTouchStart : function(e)
21789 this.swiping = false;
21791 this.startX = e.browserEvent.touches[0].clientX;
21792 this.startY = e.browserEvent.touches[0].clientY;
21795 onTouchMove : function(e)
21797 this.swiping = true;
21799 this.endX = e.browserEvent.touches[0].clientX;
21800 this.endY = e.browserEvent.touches[0].clientY;
21803 onTouchEnd : function(e)
21810 var tabGroup = this.parent();
21812 if(this.endX > this.startX){ // swiping right
21813 tabGroup.showPanelPrev();
21817 if(this.startX > this.endX){ // swiping left
21818 tabGroup.showPanelNext();
21837 * @class Roo.bootstrap.DateField
21838 * @extends Roo.bootstrap.Input
21839 * Bootstrap DateField class
21840 * @cfg {Number} weekStart default 0
21841 * @cfg {String} viewMode default empty, (months|years)
21842 * @cfg {String} minViewMode default empty, (months|years)
21843 * @cfg {Number} startDate default -Infinity
21844 * @cfg {Number} endDate default Infinity
21845 * @cfg {Boolean} todayHighlight default false
21846 * @cfg {Boolean} todayBtn default false
21847 * @cfg {Boolean} calendarWeeks default false
21848 * @cfg {Object} daysOfWeekDisabled default empty
21849 * @cfg {Boolean} singleMode default false (true | false)
21851 * @cfg {Boolean} keyboardNavigation default true
21852 * @cfg {String} language default en
21855 * Create a new DateField
21856 * @param {Object} config The config object
21859 Roo.bootstrap.DateField = function(config){
21860 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
21864 * Fires when this field show.
21865 * @param {Roo.bootstrap.DateField} this
21866 * @param {Mixed} date The date value
21871 * Fires when this field hide.
21872 * @param {Roo.bootstrap.DateField} this
21873 * @param {Mixed} date The date value
21878 * Fires when select a date.
21879 * @param {Roo.bootstrap.DateField} this
21880 * @param {Mixed} date The date value
21884 * @event beforeselect
21885 * Fires when before select a date.
21886 * @param {Roo.bootstrap.DateField} this
21887 * @param {Mixed} date The date value
21889 beforeselect : true
21893 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
21896 * @cfg {String} format
21897 * The default date format string which can be overriden for localization support. The format must be
21898 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
21902 * @cfg {String} altFormats
21903 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
21904 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21906 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21914 todayHighlight : false,
21920 keyboardNavigation: true,
21922 calendarWeeks: false,
21924 startDate: -Infinity,
21928 daysOfWeekDisabled: [],
21932 singleMode : false,
21934 UTCDate: function()
21936 return new Date(Date.UTC.apply(Date, arguments));
21939 UTCToday: function()
21941 var today = new Date();
21942 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21945 getDate: function() {
21946 var d = this.getUTCDate();
21947 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21950 getUTCDate: function() {
21954 setDate: function(d) {
21955 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21958 setUTCDate: function(d) {
21960 this.setValue(this.formatDate(this.date));
21963 onRender: function(ct, position)
21966 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21968 this.language = this.language || 'en';
21969 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21970 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21972 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21973 this.format = this.format || 'm/d/y';
21974 this.isInline = false;
21975 this.isInput = true;
21976 this.component = this.el.select('.add-on', true).first() || false;
21977 this.component = (this.component && this.component.length === 0) ? false : this.component;
21978 this.hasInput = this.component && this.inputEl().length;
21980 if (typeof(this.minViewMode === 'string')) {
21981 switch (this.minViewMode) {
21983 this.minViewMode = 1;
21986 this.minViewMode = 2;
21989 this.minViewMode = 0;
21994 if (typeof(this.viewMode === 'string')) {
21995 switch (this.viewMode) {
22008 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
22010 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
22012 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22014 this.picker().on('mousedown', this.onMousedown, this);
22015 this.picker().on('click', this.onClick, this);
22017 this.picker().addClass('datepicker-dropdown');
22019 this.startViewMode = this.viewMode;
22021 if(this.singleMode){
22022 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22023 v.setVisibilityMode(Roo.Element.DISPLAY);
22027 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22028 v.setStyle('width', '189px');
22032 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22033 if(!this.calendarWeeks){
22038 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22039 v.attr('colspan', function(i, val){
22040 return parseInt(val) + 1;
22045 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22047 this.setStartDate(this.startDate);
22048 this.setEndDate(this.endDate);
22050 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22057 if(this.isInline) {
22062 picker : function()
22064 return this.pickerEl;
22065 // return this.el.select('.datepicker', true).first();
22068 fillDow: function()
22070 var dowCnt = this.weekStart;
22079 if(this.calendarWeeks){
22087 while (dowCnt < this.weekStart + 7) {
22091 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
22095 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
22098 fillMonths: function()
22101 var months = this.picker().select('>.datepicker-months td', true).first();
22103 months.dom.innerHTML = '';
22109 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
22112 months.createChild(month);
22119 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;
22121 if (this.date < this.startDate) {
22122 this.viewDate = new Date(this.startDate);
22123 } else if (this.date > this.endDate) {
22124 this.viewDate = new Date(this.endDate);
22126 this.viewDate = new Date(this.date);
22134 var d = new Date(this.viewDate),
22135 year = d.getUTCFullYear(),
22136 month = d.getUTCMonth(),
22137 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
22138 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
22139 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
22140 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
22141 currentDate = this.date && this.date.valueOf(),
22142 today = this.UTCToday();
22144 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
22146 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22148 // this.picker.select('>tfoot th.today').
22149 // .text(dates[this.language].today)
22150 // .toggle(this.todayBtn !== false);
22152 this.updateNavArrows();
22155 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
22157 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
22159 prevMonth.setUTCDate(day);
22161 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
22163 var nextMonth = new Date(prevMonth);
22165 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
22167 nextMonth = nextMonth.valueOf();
22169 var fillMonths = false;
22171 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
22173 while(prevMonth.valueOf() <= nextMonth) {
22176 if (prevMonth.getUTCDay() === this.weekStart) {
22178 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
22186 if(this.calendarWeeks){
22187 // ISO 8601: First week contains first thursday.
22188 // ISO also states week starts on Monday, but we can be more abstract here.
22190 // Start of current week: based on weekstart/current date
22191 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
22192 // Thursday of this week
22193 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
22194 // First Thursday of year, year from thursday
22195 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
22196 // Calendar week: ms between thursdays, div ms per day, div 7 days
22197 calWeek = (th - yth) / 864e5 / 7 + 1;
22199 fillMonths.cn.push({
22207 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
22209 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
22212 if (this.todayHighlight &&
22213 prevMonth.getUTCFullYear() == today.getFullYear() &&
22214 prevMonth.getUTCMonth() == today.getMonth() &&
22215 prevMonth.getUTCDate() == today.getDate()) {
22216 clsName += ' today';
22219 if (currentDate && prevMonth.valueOf() === currentDate) {
22220 clsName += ' active';
22223 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
22224 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
22225 clsName += ' disabled';
22228 fillMonths.cn.push({
22230 cls: 'day ' + clsName,
22231 html: prevMonth.getDate()
22234 prevMonth.setDate(prevMonth.getDate()+1);
22237 var currentYear = this.date && this.date.getUTCFullYear();
22238 var currentMonth = this.date && this.date.getUTCMonth();
22240 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
22242 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
22243 v.removeClass('active');
22245 if(currentYear === year && k === currentMonth){
22246 v.addClass('active');
22249 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
22250 v.addClass('disabled');
22256 year = parseInt(year/10, 10) * 10;
22258 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
22260 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
22263 for (var i = -1; i < 11; i++) {
22264 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
22266 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
22274 showMode: function(dir)
22277 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
22280 Roo.each(this.picker().select('>div',true).elements, function(v){
22281 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22284 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
22289 if(this.isInline) {
22293 this.picker().removeClass(['bottom', 'top']);
22295 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22297 * place to the top of element!
22301 this.picker().addClass('top');
22302 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22307 this.picker().addClass('bottom');
22309 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22312 parseDate : function(value)
22314 if(!value || value instanceof Date){
22317 var v = Date.parseDate(value, this.format);
22318 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
22319 v = Date.parseDate(value, 'Y-m-d');
22321 if(!v && this.altFormats){
22322 if(!this.altFormatsArray){
22323 this.altFormatsArray = this.altFormats.split("|");
22325 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22326 v = Date.parseDate(value, this.altFormatsArray[i]);
22332 formatDate : function(date, fmt)
22334 return (!date || !(date instanceof Date)) ?
22335 date : date.dateFormat(fmt || this.format);
22338 onFocus : function()
22340 Roo.bootstrap.DateField.superclass.onFocus.call(this);
22344 onBlur : function()
22346 Roo.bootstrap.DateField.superclass.onBlur.call(this);
22348 var d = this.inputEl().getValue();
22355 showPopup : function()
22357 this.picker().show();
22361 this.fireEvent('showpopup', this, this.date);
22364 hidePopup : function()
22366 if(this.isInline) {
22369 this.picker().hide();
22370 this.viewMode = this.startViewMode;
22373 this.fireEvent('hidepopup', this, this.date);
22377 onMousedown: function(e)
22379 e.stopPropagation();
22380 e.preventDefault();
22385 Roo.bootstrap.DateField.superclass.keyup.call(this);
22389 setValue: function(v)
22391 if(this.fireEvent('beforeselect', this, v) !== false){
22392 var d = new Date(this.parseDate(v) ).clearTime();
22394 if(isNaN(d.getTime())){
22395 this.date = this.viewDate = '';
22396 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22400 v = this.formatDate(d);
22402 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
22404 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
22408 this.fireEvent('select', this, this.date);
22412 getValue: function()
22414 return this.formatDate(this.date);
22417 fireKey: function(e)
22419 if (!this.picker().isVisible()){
22420 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22426 var dateChanged = false,
22428 newDate, newViewDate;
22433 e.preventDefault();
22437 if (!this.keyboardNavigation) {
22440 dir = e.keyCode == 37 ? -1 : 1;
22443 newDate = this.moveYear(this.date, dir);
22444 newViewDate = this.moveYear(this.viewDate, dir);
22445 } else if (e.shiftKey){
22446 newDate = this.moveMonth(this.date, dir);
22447 newViewDate = this.moveMonth(this.viewDate, dir);
22449 newDate = new Date(this.date);
22450 newDate.setUTCDate(this.date.getUTCDate() + dir);
22451 newViewDate = new Date(this.viewDate);
22452 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
22454 if (this.dateWithinRange(newDate)){
22455 this.date = newDate;
22456 this.viewDate = newViewDate;
22457 this.setValue(this.formatDate(this.date));
22459 e.preventDefault();
22460 dateChanged = true;
22465 if (!this.keyboardNavigation) {
22468 dir = e.keyCode == 38 ? -1 : 1;
22470 newDate = this.moveYear(this.date, dir);
22471 newViewDate = this.moveYear(this.viewDate, dir);
22472 } else if (e.shiftKey){
22473 newDate = this.moveMonth(this.date, dir);
22474 newViewDate = this.moveMonth(this.viewDate, dir);
22476 newDate = new Date(this.date);
22477 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
22478 newViewDate = new Date(this.viewDate);
22479 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
22481 if (this.dateWithinRange(newDate)){
22482 this.date = newDate;
22483 this.viewDate = newViewDate;
22484 this.setValue(this.formatDate(this.date));
22486 e.preventDefault();
22487 dateChanged = true;
22491 this.setValue(this.formatDate(this.date));
22493 e.preventDefault();
22496 this.setValue(this.formatDate(this.date));
22510 onClick: function(e)
22512 e.stopPropagation();
22513 e.preventDefault();
22515 var target = e.getTarget();
22517 if(target.nodeName.toLowerCase() === 'i'){
22518 target = Roo.get(target).dom.parentNode;
22521 var nodeName = target.nodeName;
22522 var className = target.className;
22523 var html = target.innerHTML;
22524 //Roo.log(nodeName);
22526 switch(nodeName.toLowerCase()) {
22528 switch(className) {
22534 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
22535 switch(this.viewMode){
22537 this.viewDate = this.moveMonth(this.viewDate, dir);
22541 this.viewDate = this.moveYear(this.viewDate, dir);
22547 var date = new Date();
22548 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
22550 this.setValue(this.formatDate(this.date));
22557 if (className.indexOf('disabled') < 0) {
22558 if (!this.viewDate) {
22559 this.viewDate = new Date();
22561 this.viewDate.setUTCDate(1);
22562 if (className.indexOf('month') > -1) {
22563 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
22565 var year = parseInt(html, 10) || 0;
22566 this.viewDate.setUTCFullYear(year);
22570 if(this.singleMode){
22571 this.setValue(this.formatDate(this.viewDate));
22582 //Roo.log(className);
22583 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
22584 var day = parseInt(html, 10) || 1;
22585 var year = (this.viewDate || new Date()).getUTCFullYear(),
22586 month = (this.viewDate || new Date()).getUTCMonth();
22588 if (className.indexOf('old') > -1) {
22595 } else if (className.indexOf('new') > -1) {
22603 //Roo.log([year,month,day]);
22604 this.date = this.UTCDate(year, month, day,0,0,0,0);
22605 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
22607 //Roo.log(this.formatDate(this.date));
22608 this.setValue(this.formatDate(this.date));
22615 setStartDate: function(startDate)
22617 this.startDate = startDate || -Infinity;
22618 if (this.startDate !== -Infinity) {
22619 this.startDate = this.parseDate(this.startDate);
22622 this.updateNavArrows();
22625 setEndDate: function(endDate)
22627 this.endDate = endDate || Infinity;
22628 if (this.endDate !== Infinity) {
22629 this.endDate = this.parseDate(this.endDate);
22632 this.updateNavArrows();
22635 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
22637 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
22638 if (typeof(this.daysOfWeekDisabled) !== 'object') {
22639 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
22641 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
22642 return parseInt(d, 10);
22645 this.updateNavArrows();
22648 updateNavArrows: function()
22650 if(this.singleMode){
22654 var d = new Date(this.viewDate),
22655 year = d.getUTCFullYear(),
22656 month = d.getUTCMonth();
22658 Roo.each(this.picker().select('.prev', true).elements, function(v){
22660 switch (this.viewMode) {
22663 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
22669 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
22676 Roo.each(this.picker().select('.next', true).elements, function(v){
22678 switch (this.viewMode) {
22681 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
22687 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
22695 moveMonth: function(date, dir)
22700 var new_date = new Date(date.valueOf()),
22701 day = new_date.getUTCDate(),
22702 month = new_date.getUTCMonth(),
22703 mag = Math.abs(dir),
22705 dir = dir > 0 ? 1 : -1;
22708 // If going back one month, make sure month is not current month
22709 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
22711 return new_date.getUTCMonth() == month;
22713 // If going forward one month, make sure month is as expected
22714 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
22716 return new_date.getUTCMonth() != new_month;
22718 new_month = month + dir;
22719 new_date.setUTCMonth(new_month);
22720 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
22721 if (new_month < 0 || new_month > 11) {
22722 new_month = (new_month + 12) % 12;
22725 // For magnitudes >1, move one month at a time...
22726 for (var i=0; i<mag; i++) {
22727 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
22728 new_date = this.moveMonth(new_date, dir);
22730 // ...then reset the day, keeping it in the new month
22731 new_month = new_date.getUTCMonth();
22732 new_date.setUTCDate(day);
22734 return new_month != new_date.getUTCMonth();
22737 // Common date-resetting loop -- if date is beyond end of month, make it
22740 new_date.setUTCDate(--day);
22741 new_date.setUTCMonth(new_month);
22746 moveYear: function(date, dir)
22748 return this.moveMonth(date, dir*12);
22751 dateWithinRange: function(date)
22753 return date >= this.startDate && date <= this.endDate;
22759 this.picker().remove();
22762 validateValue : function(value)
22764 if(this.getVisibilityEl().hasClass('hidden')){
22768 if(value.length < 1) {
22769 if(this.allowBlank){
22775 if(value.length < this.minLength){
22778 if(value.length > this.maxLength){
22782 var vt = Roo.form.VTypes;
22783 if(!vt[this.vtype](value, this)){
22787 if(typeof this.validator == "function"){
22788 var msg = this.validator(value);
22794 if(this.regex && !this.regex.test(value)){
22798 if(typeof(this.parseDate(value)) == 'undefined'){
22802 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
22806 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
22816 this.date = this.viewDate = '';
22818 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22823 Roo.apply(Roo.bootstrap.DateField, {
22834 html: '<i class="fa fa-arrow-left"/>'
22844 html: '<i class="fa fa-arrow-right"/>'
22886 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
22887 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
22888 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
22889 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22890 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
22903 navFnc: 'FullYear',
22908 navFnc: 'FullYear',
22913 Roo.apply(Roo.bootstrap.DateField, {
22917 cls: 'datepicker dropdown-menu roo-dynamic shadow',
22921 cls: 'datepicker-days',
22925 cls: 'table-condensed',
22927 Roo.bootstrap.DateField.head,
22931 Roo.bootstrap.DateField.footer
22938 cls: 'datepicker-months',
22942 cls: 'table-condensed',
22944 Roo.bootstrap.DateField.head,
22945 Roo.bootstrap.DateField.content,
22946 Roo.bootstrap.DateField.footer
22953 cls: 'datepicker-years',
22957 cls: 'table-condensed',
22959 Roo.bootstrap.DateField.head,
22960 Roo.bootstrap.DateField.content,
22961 Roo.bootstrap.DateField.footer
22980 * @class Roo.bootstrap.TimeField
22981 * @extends Roo.bootstrap.Input
22982 * Bootstrap DateField class
22986 * Create a new TimeField
22987 * @param {Object} config The config object
22990 Roo.bootstrap.TimeField = function(config){
22991 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22995 * Fires when this field show.
22996 * @param {Roo.bootstrap.DateField} thisthis
22997 * @param {Mixed} date The date value
23002 * Fires when this field hide.
23003 * @param {Roo.bootstrap.DateField} this
23004 * @param {Mixed} date The date value
23009 * Fires when select a date.
23010 * @param {Roo.bootstrap.DateField} this
23011 * @param {Mixed} date The date value
23017 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
23020 * @cfg {String} format
23021 * The default time format string which can be overriden for localization support. The format must be
23022 * valid according to {@link Date#parseDate} (defaults to 'H:i').
23026 getAutoCreate : function()
23028 this.after = '<i class="fa far fa-clock"></i>';
23029 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
23033 onRender: function(ct, position)
23036 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
23038 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
23040 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23042 this.pop = this.picker().select('>.datepicker-time',true).first();
23043 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23045 this.picker().on('mousedown', this.onMousedown, this);
23046 this.picker().on('click', this.onClick, this);
23048 this.picker().addClass('datepicker-dropdown');
23053 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23054 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23055 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23056 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23057 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23058 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23062 fireKey: function(e){
23063 if (!this.picker().isVisible()){
23064 if (e.keyCode == 27) { // allow escape to hide and re-show picker
23070 e.preventDefault();
23078 this.onTogglePeriod();
23081 this.onIncrementMinutes();
23084 this.onDecrementMinutes();
23093 onClick: function(e) {
23094 e.stopPropagation();
23095 e.preventDefault();
23098 picker : function()
23100 return this.pickerEl;
23103 fillTime: function()
23105 var time = this.pop.select('tbody', true).first();
23107 time.dom.innerHTML = '';
23122 cls: 'hours-up fa fas fa-chevron-up'
23142 cls: 'minutes-up fa fas fa-chevron-up'
23163 cls: 'timepicker-hour',
23178 cls: 'timepicker-minute',
23193 cls: 'btn btn-primary period',
23215 cls: 'hours-down fa fas fa-chevron-down'
23235 cls: 'minutes-down fa fas fa-chevron-down'
23253 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
23260 var hours = this.time.getHours();
23261 var minutes = this.time.getMinutes();
23274 hours = hours - 12;
23278 hours = '0' + hours;
23282 minutes = '0' + minutes;
23285 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
23286 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
23287 this.pop.select('button', true).first().dom.innerHTML = period;
23293 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
23295 var cls = ['bottom'];
23297 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
23304 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
23308 //this.picker().setXY(20000,20000);
23309 this.picker().addClass(cls.join('-'));
23313 Roo.each(cls, function(c){
23318 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
23319 //_this.picker().setTop(_this.inputEl().getHeight());
23323 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
23325 //_this.picker().setTop(0 - _this.picker().getHeight());
23330 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
23334 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
23342 onFocus : function()
23344 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
23348 onBlur : function()
23350 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
23356 this.picker().show();
23361 this.fireEvent('show', this, this.date);
23366 this.picker().hide();
23369 this.fireEvent('hide', this, this.date);
23372 setTime : function()
23375 this.setValue(this.time.format(this.format));
23377 this.fireEvent('select', this, this.date);
23382 onMousedown: function(e){
23383 e.stopPropagation();
23384 e.preventDefault();
23387 onIncrementHours: function()
23389 Roo.log('onIncrementHours');
23390 this.time = this.time.add(Date.HOUR, 1);
23395 onDecrementHours: function()
23397 Roo.log('onDecrementHours');
23398 this.time = this.time.add(Date.HOUR, -1);
23402 onIncrementMinutes: function()
23404 Roo.log('onIncrementMinutes');
23405 this.time = this.time.add(Date.MINUTE, 1);
23409 onDecrementMinutes: function()
23411 Roo.log('onDecrementMinutes');
23412 this.time = this.time.add(Date.MINUTE, -1);
23416 onTogglePeriod: function()
23418 Roo.log('onTogglePeriod');
23419 this.time = this.time.add(Date.HOUR, 12);
23427 Roo.apply(Roo.bootstrap.TimeField, {
23431 cls: 'datepicker dropdown-menu',
23435 cls: 'datepicker-time',
23439 cls: 'table-condensed',
23468 cls: 'btn btn-info ok',
23496 * @class Roo.bootstrap.MonthField
23497 * @extends Roo.bootstrap.Input
23498 * Bootstrap MonthField class
23500 * @cfg {String} language default en
23503 * Create a new MonthField
23504 * @param {Object} config The config object
23507 Roo.bootstrap.MonthField = function(config){
23508 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
23513 * Fires when this field show.
23514 * @param {Roo.bootstrap.MonthField} this
23515 * @param {Mixed} date The date value
23520 * Fires when this field hide.
23521 * @param {Roo.bootstrap.MonthField} this
23522 * @param {Mixed} date The date value
23527 * Fires when select a date.
23528 * @param {Roo.bootstrap.MonthField} this
23529 * @param {String} oldvalue The old value
23530 * @param {String} newvalue The new value
23536 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
23538 onRender: function(ct, position)
23541 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
23543 this.language = this.language || 'en';
23544 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
23545 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
23547 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
23548 this.isInline = false;
23549 this.isInput = true;
23550 this.component = this.el.select('.add-on', true).first() || false;
23551 this.component = (this.component && this.component.length === 0) ? false : this.component;
23552 this.hasInput = this.component && this.inputEL().length;
23554 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
23556 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23558 this.picker().on('mousedown', this.onMousedown, this);
23559 this.picker().on('click', this.onClick, this);
23561 this.picker().addClass('datepicker-dropdown');
23563 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
23564 v.setStyle('width', '189px');
23571 if(this.isInline) {
23577 setValue: function(v, suppressEvent)
23579 var o = this.getValue();
23581 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
23585 if(suppressEvent !== true){
23586 this.fireEvent('select', this, o, v);
23591 getValue: function()
23596 onClick: function(e)
23598 e.stopPropagation();
23599 e.preventDefault();
23601 var target = e.getTarget();
23603 if(target.nodeName.toLowerCase() === 'i'){
23604 target = Roo.get(target).dom.parentNode;
23607 var nodeName = target.nodeName;
23608 var className = target.className;
23609 var html = target.innerHTML;
23611 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
23615 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
23617 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23623 picker : function()
23625 return this.pickerEl;
23628 fillMonths: function()
23631 var months = this.picker().select('>.datepicker-months td', true).first();
23633 months.dom.innerHTML = '';
23639 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
23642 months.createChild(month);
23651 if(typeof(this.vIndex) == 'undefined' && this.value.length){
23652 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
23655 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
23656 e.removeClass('active');
23658 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
23659 e.addClass('active');
23666 if(this.isInline) {
23670 this.picker().removeClass(['bottom', 'top']);
23672 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23674 * place to the top of element!
23678 this.picker().addClass('top');
23679 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23684 this.picker().addClass('bottom');
23686 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23689 onFocus : function()
23691 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
23695 onBlur : function()
23697 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
23699 var d = this.inputEl().getValue();
23708 this.picker().show();
23709 this.picker().select('>.datepicker-months', true).first().show();
23713 this.fireEvent('show', this, this.date);
23718 if(this.isInline) {
23721 this.picker().hide();
23722 this.fireEvent('hide', this, this.date);
23726 onMousedown: function(e)
23728 e.stopPropagation();
23729 e.preventDefault();
23734 Roo.bootstrap.MonthField.superclass.keyup.call(this);
23738 fireKey: function(e)
23740 if (!this.picker().isVisible()){
23741 if (e.keyCode == 27) {// allow escape to hide and re-show picker
23752 e.preventDefault();
23756 dir = e.keyCode == 37 ? -1 : 1;
23758 this.vIndex = this.vIndex + dir;
23760 if(this.vIndex < 0){
23764 if(this.vIndex > 11){
23768 if(isNaN(this.vIndex)){
23772 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23778 dir = e.keyCode == 38 ? -1 : 1;
23780 this.vIndex = this.vIndex + dir * 4;
23782 if(this.vIndex < 0){
23786 if(this.vIndex > 11){
23790 if(isNaN(this.vIndex)){
23794 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23799 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23800 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23804 e.preventDefault();
23807 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23808 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23824 this.picker().remove();
23829 Roo.apply(Roo.bootstrap.MonthField, {
23848 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23849 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
23854 Roo.apply(Roo.bootstrap.MonthField, {
23858 cls: 'datepicker dropdown-menu roo-dynamic',
23862 cls: 'datepicker-months',
23866 cls: 'table-condensed',
23868 Roo.bootstrap.DateField.content
23888 * @class Roo.bootstrap.CheckBox
23889 * @extends Roo.bootstrap.Input
23890 * Bootstrap CheckBox class
23892 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23893 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
23894 * @cfg {String} boxLabel The text that appears beside the checkbox
23895 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
23896 * @cfg {Boolean} checked initnal the element
23897 * @cfg {Boolean} inline inline the element (default false)
23898 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
23899 * @cfg {String} tooltip label tooltip
23902 * Create a new CheckBox
23903 * @param {Object} config The config object
23906 Roo.bootstrap.CheckBox = function(config){
23907 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23912 * Fires when the element is checked or unchecked.
23913 * @param {Roo.bootstrap.CheckBox} this This input
23914 * @param {Boolean} checked The new checked value
23919 * Fires when the element is click.
23920 * @param {Roo.bootstrap.CheckBox} this This input
23927 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
23929 inputType: 'checkbox',
23938 // checkbox success does not make any sense really..
23943 getAutoCreate : function()
23945 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23951 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23954 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
23960 type : this.inputType,
23961 value : this.inputValue,
23962 cls : 'roo-' + this.inputType, //'form-box',
23963 placeholder : this.placeholder || ''
23967 if(this.inputType != 'radio'){
23971 cls : 'roo-hidden-value',
23972 value : this.checked ? this.inputValue : this.valueOff
23977 if (this.weight) { // Validity check?
23978 cfg.cls += " " + this.inputType + "-" + this.weight;
23981 if (this.disabled) {
23982 input.disabled=true;
23986 input.checked = this.checked;
23991 input.name = this.name;
23993 if(this.inputType != 'radio'){
23994 hidden.name = this.name;
23995 input.name = '_hidden_' + this.name;
24000 input.cls += ' input-' + this.size;
24005 ['xs','sm','md','lg'].map(function(size){
24006 if (settings[size]) {
24007 cfg.cls += ' col-' + size + '-' + settings[size];
24011 var inputblock = input;
24013 if (this.before || this.after) {
24016 cls : 'input-group',
24021 inputblock.cn.push({
24023 cls : 'input-group-addon',
24028 inputblock.cn.push(input);
24030 if(this.inputType != 'radio'){
24031 inputblock.cn.push(hidden);
24035 inputblock.cn.push({
24037 cls : 'input-group-addon',
24043 var boxLabelCfg = false;
24049 //'for': id, // box label is handled by onclick - so no for...
24051 html: this.boxLabel
24054 boxLabelCfg.tooltip = this.tooltip;
24060 if (align ==='left' && this.fieldLabel.length) {
24061 // Roo.log("left and has label");
24066 cls : 'control-label',
24067 html : this.fieldLabel
24078 cfg.cn[1].cn.push(boxLabelCfg);
24081 if(this.labelWidth > 12){
24082 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
24085 if(this.labelWidth < 13 && this.labelmd == 0){
24086 this.labelmd = this.labelWidth;
24089 if(this.labellg > 0){
24090 cfg.cn[0].cls += ' col-lg-' + this.labellg;
24091 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
24094 if(this.labelmd > 0){
24095 cfg.cn[0].cls += ' col-md-' + this.labelmd;
24096 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
24099 if(this.labelsm > 0){
24100 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
24101 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
24104 if(this.labelxs > 0){
24105 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
24106 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
24109 } else if ( this.fieldLabel.length) {
24110 // Roo.log(" label");
24114 tag: this.boxLabel ? 'span' : 'label',
24116 cls: 'control-label box-input-label',
24117 //cls : 'input-group-addon',
24118 html : this.fieldLabel
24125 cfg.cn.push(boxLabelCfg);
24130 // Roo.log(" no label && no align");
24131 cfg.cn = [ inputblock ] ;
24133 cfg.cn.push(boxLabelCfg);
24141 if(this.inputType != 'radio'){
24142 cfg.cn.push(hidden);
24150 * return the real input element.
24152 inputEl: function ()
24154 return this.el.select('input.roo-' + this.inputType,true).first();
24156 hiddenEl: function ()
24158 return this.el.select('input.roo-hidden-value',true).first();
24161 labelEl: function()
24163 return this.el.select('label.control-label',true).first();
24165 /* depricated... */
24169 return this.labelEl();
24172 boxLabelEl: function()
24174 return this.el.select('label.box-label',true).first();
24177 initEvents : function()
24179 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
24181 this.inputEl().on('click', this.onClick, this);
24183 if (this.boxLabel) {
24184 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
24187 this.startValue = this.getValue();
24190 Roo.bootstrap.CheckBox.register(this);
24194 onClick : function(e)
24196 if(this.fireEvent('click', this, e) !== false){
24197 this.setChecked(!this.checked);
24202 setChecked : function(state,suppressEvent)
24204 this.startValue = this.getValue();
24206 if(this.inputType == 'radio'){
24208 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24209 e.dom.checked = false;
24212 this.inputEl().dom.checked = true;
24214 this.inputEl().dom.value = this.inputValue;
24216 if(suppressEvent !== true){
24217 this.fireEvent('check', this, true);
24225 this.checked = state;
24227 this.inputEl().dom.checked = state;
24230 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
24232 if(suppressEvent !== true){
24233 this.fireEvent('check', this, state);
24239 getValue : function()
24241 if(this.inputType == 'radio'){
24242 return this.getGroupValue();
24245 return this.hiddenEl().dom.value;
24249 getGroupValue : function()
24251 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
24255 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
24258 setValue : function(v,suppressEvent)
24260 if(this.inputType == 'radio'){
24261 this.setGroupValue(v, suppressEvent);
24265 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
24270 setGroupValue : function(v, suppressEvent)
24272 this.startValue = this.getValue();
24274 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24275 e.dom.checked = false;
24277 if(e.dom.value == v){
24278 e.dom.checked = true;
24282 if(suppressEvent !== true){
24283 this.fireEvent('check', this, true);
24291 validate : function()
24293 if(this.getVisibilityEl().hasClass('hidden')){
24299 (this.inputType == 'radio' && this.validateRadio()) ||
24300 (this.inputType == 'checkbox' && this.validateCheckbox())
24306 this.markInvalid();
24310 validateRadio : function()
24312 if(this.getVisibilityEl().hasClass('hidden')){
24316 if(this.allowBlank){
24322 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24323 if(!e.dom.checked){
24335 validateCheckbox : function()
24338 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
24339 //return (this.getValue() == this.inputValue) ? true : false;
24342 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24350 for(var i in group){
24351 if(group[i].el.isVisible(true)){
24359 for(var i in group){
24364 r = (group[i].getValue() == group[i].inputValue) ? true : false;
24371 * Mark this field as valid
24373 markValid : function()
24377 this.fireEvent('valid', this);
24379 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24382 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24389 if(this.inputType == 'radio'){
24390 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24391 var fg = e.findParent('.form-group', false, true);
24392 if (Roo.bootstrap.version == 3) {
24393 fg.removeClass([_this.invalidClass, _this.validClass]);
24394 fg.addClass(_this.validClass);
24396 fg.removeClass(['is-valid', 'is-invalid']);
24397 fg.addClass('is-valid');
24405 var fg = this.el.findParent('.form-group', false, true);
24406 if (Roo.bootstrap.version == 3) {
24407 fg.removeClass([this.invalidClass, this.validClass]);
24408 fg.addClass(this.validClass);
24410 fg.removeClass(['is-valid', 'is-invalid']);
24411 fg.addClass('is-valid');
24416 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24422 for(var i in group){
24423 var fg = group[i].el.findParent('.form-group', false, true);
24424 if (Roo.bootstrap.version == 3) {
24425 fg.removeClass([this.invalidClass, this.validClass]);
24426 fg.addClass(this.validClass);
24428 fg.removeClass(['is-valid', 'is-invalid']);
24429 fg.addClass('is-valid');
24435 * Mark this field as invalid
24436 * @param {String} msg The validation message
24438 markInvalid : function(msg)
24440 if(this.allowBlank){
24446 this.fireEvent('invalid', this, msg);
24448 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24451 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24455 label.markInvalid();
24458 if(this.inputType == 'radio'){
24460 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24461 var fg = e.findParent('.form-group', false, true);
24462 if (Roo.bootstrap.version == 3) {
24463 fg.removeClass([_this.invalidClass, _this.validClass]);
24464 fg.addClass(_this.invalidClass);
24466 fg.removeClass(['is-invalid', 'is-valid']);
24467 fg.addClass('is-invalid');
24475 var fg = this.el.findParent('.form-group', false, true);
24476 if (Roo.bootstrap.version == 3) {
24477 fg.removeClass([_this.invalidClass, _this.validClass]);
24478 fg.addClass(_this.invalidClass);
24480 fg.removeClass(['is-invalid', 'is-valid']);
24481 fg.addClass('is-invalid');
24486 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24492 for(var i in group){
24493 var fg = group[i].el.findParent('.form-group', false, true);
24494 if (Roo.bootstrap.version == 3) {
24495 fg.removeClass([_this.invalidClass, _this.validClass]);
24496 fg.addClass(_this.invalidClass);
24498 fg.removeClass(['is-invalid', 'is-valid']);
24499 fg.addClass('is-invalid');
24505 clearInvalid : function()
24507 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
24509 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
24511 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24513 if (label && label.iconEl) {
24514 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
24515 label.iconEl.removeClass(['is-invalid', 'is-valid']);
24519 disable : function()
24521 if(this.inputType != 'radio'){
24522 Roo.bootstrap.CheckBox.superclass.disable.call(this);
24529 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24530 _this.getActionEl().addClass(this.disabledClass);
24531 e.dom.disabled = true;
24535 this.disabled = true;
24536 this.fireEvent("disable", this);
24540 enable : function()
24542 if(this.inputType != 'radio'){
24543 Roo.bootstrap.CheckBox.superclass.enable.call(this);
24550 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24551 _this.getActionEl().removeClass(this.disabledClass);
24552 e.dom.disabled = false;
24556 this.disabled = false;
24557 this.fireEvent("enable", this);
24561 setBoxLabel : function(v)
24566 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24572 Roo.apply(Roo.bootstrap.CheckBox, {
24577 * register a CheckBox Group
24578 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
24580 register : function(checkbox)
24582 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
24583 this.groups[checkbox.groupId] = {};
24586 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
24590 this.groups[checkbox.groupId][checkbox.name] = checkbox;
24594 * fetch a CheckBox Group based on the group ID
24595 * @param {string} the group ID
24596 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
24598 get: function(groupId) {
24599 if (typeof(this.groups[groupId]) == 'undefined') {
24603 return this.groups[groupId] ;
24616 * @class Roo.bootstrap.Radio
24617 * @extends Roo.bootstrap.Component
24618 * Bootstrap Radio class
24619 * @cfg {String} boxLabel - the label associated
24620 * @cfg {String} value - the value of radio
24623 * Create a new Radio
24624 * @param {Object} config The config object
24626 Roo.bootstrap.Radio = function(config){
24627 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
24631 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
24637 getAutoCreate : function()
24641 cls : 'form-group radio',
24646 html : this.boxLabel
24654 initEvents : function()
24656 this.parent().register(this);
24658 this.el.on('click', this.onClick, this);
24662 onClick : function(e)
24664 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
24665 this.setChecked(true);
24669 setChecked : function(state, suppressEvent)
24671 this.parent().setValue(this.value, suppressEvent);
24675 setBoxLabel : function(v)
24680 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24695 * @class Roo.bootstrap.SecurePass
24696 * @extends Roo.bootstrap.Input
24697 * Bootstrap SecurePass class
24701 * Create a new SecurePass
24702 * @param {Object} config The config object
24705 Roo.bootstrap.SecurePass = function (config) {
24706 // these go here, so the translation tool can replace them..
24708 PwdEmpty: "Please type a password, and then retype it to confirm.",
24709 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24710 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24711 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24712 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24713 FNInPwd: "Your password can't contain your first name. Please type a different password.",
24714 LNInPwd: "Your password can't contain your last name. Please type a different password.",
24715 TooWeak: "Your password is Too Weak."
24717 this.meterLabel = "Password strength:";
24718 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
24719 this.meterClass = [
24720 "roo-password-meter-tooweak",
24721 "roo-password-meter-weak",
24722 "roo-password-meter-medium",
24723 "roo-password-meter-strong",
24724 "roo-password-meter-grey"
24729 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
24732 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
24734 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
24736 * PwdEmpty: "Please type a password, and then retype it to confirm.",
24737 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24738 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24739 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24740 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24741 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
24742 * LNInPwd: "Your password can't contain your last name. Please type a different password."
24752 * @cfg {String/Object} Label for the strength meter (defaults to
24753 * 'Password strength:')
24758 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
24759 * ['Weak', 'Medium', 'Strong'])
24762 pwdStrengths: false,
24775 initEvents: function ()
24777 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
24779 if (this.el.is('input[type=password]') && Roo.isSafari) {
24780 this.el.on('keydown', this.SafariOnKeyDown, this);
24783 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
24786 onRender: function (ct, position)
24788 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
24789 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
24790 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
24792 this.trigger.createChild({
24797 cls: 'roo-password-meter-grey col-xs-12',
24800 //width: this.meterWidth + 'px'
24804 cls: 'roo-password-meter-text'
24810 if (this.hideTrigger) {
24811 this.trigger.setDisplayed(false);
24813 this.setSize(this.width || '', this.height || '');
24816 onDestroy: function ()
24818 if (this.trigger) {
24819 this.trigger.removeAllListeners();
24820 this.trigger.remove();
24823 this.wrap.remove();
24825 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
24828 checkStrength: function ()
24830 var pwd = this.inputEl().getValue();
24831 if (pwd == this._lastPwd) {
24836 if (this.ClientSideStrongPassword(pwd)) {
24838 } else if (this.ClientSideMediumPassword(pwd)) {
24840 } else if (this.ClientSideWeakPassword(pwd)) {
24846 Roo.log('strength1: ' + strength);
24848 //var pm = this.trigger.child('div/div/div').dom;
24849 var pm = this.trigger.child('div/div');
24850 pm.removeClass(this.meterClass);
24851 pm.addClass(this.meterClass[strength]);
24854 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24856 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24858 this._lastPwd = pwd;
24862 Roo.bootstrap.SecurePass.superclass.reset.call(this);
24864 this._lastPwd = '';
24866 var pm = this.trigger.child('div/div');
24867 pm.removeClass(this.meterClass);
24868 pm.addClass('roo-password-meter-grey');
24871 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24874 this.inputEl().dom.type='password';
24877 validateValue: function (value)
24879 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
24882 if (value.length == 0) {
24883 if (this.allowBlank) {
24884 this.clearInvalid();
24888 this.markInvalid(this.errors.PwdEmpty);
24889 this.errorMsg = this.errors.PwdEmpty;
24897 if (!value.match(/[\x21-\x7e]+/)) {
24898 this.markInvalid(this.errors.PwdBadChar);
24899 this.errorMsg = this.errors.PwdBadChar;
24902 if (value.length < 6) {
24903 this.markInvalid(this.errors.PwdShort);
24904 this.errorMsg = this.errors.PwdShort;
24907 if (value.length > 16) {
24908 this.markInvalid(this.errors.PwdLong);
24909 this.errorMsg = this.errors.PwdLong;
24913 if (this.ClientSideStrongPassword(value)) {
24915 } else if (this.ClientSideMediumPassword(value)) {
24917 } else if (this.ClientSideWeakPassword(value)) {
24924 if (strength < 2) {
24925 //this.markInvalid(this.errors.TooWeak);
24926 this.errorMsg = this.errors.TooWeak;
24931 console.log('strength2: ' + strength);
24933 //var pm = this.trigger.child('div/div/div').dom;
24935 var pm = this.trigger.child('div/div');
24936 pm.removeClass(this.meterClass);
24937 pm.addClass(this.meterClass[strength]);
24939 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24941 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24943 this.errorMsg = '';
24947 CharacterSetChecks: function (type)
24950 this.fResult = false;
24953 isctype: function (character, type)
24956 case this.kCapitalLetter:
24957 if (character >= 'A' && character <= 'Z') {
24962 case this.kSmallLetter:
24963 if (character >= 'a' && character <= 'z') {
24969 if (character >= '0' && character <= '9') {
24974 case this.kPunctuation:
24975 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24986 IsLongEnough: function (pwd, size)
24988 return !(pwd == null || isNaN(size) || pwd.length < size);
24991 SpansEnoughCharacterSets: function (word, nb)
24993 if (!this.IsLongEnough(word, nb))
24998 var characterSetChecks = new Array(
24999 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25000 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25003 for (var index = 0; index < word.length; ++index) {
25004 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25005 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25006 characterSetChecks[nCharSet].fResult = true;
25013 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25014 if (characterSetChecks[nCharSet].fResult) {
25019 if (nCharSets < nb) {
25025 ClientSideStrongPassword: function (pwd)
25027 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25030 ClientSideMediumPassword: function (pwd)
25032 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25035 ClientSideWeakPassword: function (pwd)
25037 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25040 })//<script type="text/javascript">
25043 * Based Ext JS Library 1.1.1
25044 * Copyright(c) 2006-2007, Ext JS, LLC.
25050 * @class Roo.HtmlEditorCore
25051 * @extends Roo.Component
25052 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25054 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25057 Roo.HtmlEditorCore = function(config){
25060 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25065 * @event initialize
25066 * Fires when the editor is fully initialized (including the iframe)
25067 * @param {Roo.HtmlEditorCore} this
25072 * Fires when the editor is first receives the focus. Any insertion must wait
25073 * until after this event.
25074 * @param {Roo.HtmlEditorCore} this
25078 * @event beforesync
25079 * Fires before the textarea is updated with content from the editor iframe. Return false
25080 * to cancel the sync.
25081 * @param {Roo.HtmlEditorCore} this
25082 * @param {String} html
25086 * @event beforepush
25087 * Fires before the iframe editor is updated with content from the textarea. Return false
25088 * to cancel the push.
25089 * @param {Roo.HtmlEditorCore} this
25090 * @param {String} html
25095 * Fires when the textarea is updated with content from the editor iframe.
25096 * @param {Roo.HtmlEditorCore} this
25097 * @param {String} html
25102 * Fires when the iframe editor is updated with content from the textarea.
25103 * @param {Roo.HtmlEditorCore} this
25104 * @param {String} html
25109 * @event editorevent
25110 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25111 * @param {Roo.HtmlEditorCore} this
25117 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
25119 // defaults : white / black...
25120 this.applyBlacklists();
25127 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
25131 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
25137 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
25142 * @cfg {Number} height (in pixels)
25146 * @cfg {Number} width (in pixels)
25151 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25154 stylesheets: false,
25159 // private properties
25160 validationEvent : false,
25162 initialized : false,
25164 sourceEditMode : false,
25165 onFocus : Roo.emptyFn,
25167 hideMode:'offsets',
25171 // blacklist + whitelisted elements..
25178 * Protected method that will not generally be called directly. It
25179 * is called when the editor initializes the iframe with HTML contents. Override this method if you
25180 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25182 getDocMarkup : function(){
25186 // inherit styels from page...??
25187 if (this.stylesheets === false) {
25189 Roo.get(document.head).select('style').each(function(node) {
25190 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25193 Roo.get(document.head).select('link').each(function(node) {
25194 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25197 } else if (!this.stylesheets.length) {
25199 st = '<style type="text/css">' +
25200 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25203 for (var i in this.stylesheets) {
25204 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
25209 st += '<style type="text/css">' +
25210 'IMG { cursor: pointer } ' +
25213 var cls = 'roo-htmleditor-body';
25215 if(this.bodyCls.length){
25216 cls += ' ' + this.bodyCls;
25219 return '<html><head>' + st +
25220 //<style type="text/css">' +
25221 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25223 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
25227 onRender : function(ct, position)
25230 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
25231 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
25234 this.el.dom.style.border = '0 none';
25235 this.el.dom.setAttribute('tabIndex', -1);
25236 this.el.addClass('x-hidden hide');
25240 if(Roo.isIE){ // fix IE 1px bogus margin
25241 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25245 this.frameId = Roo.id();
25249 var iframe = this.owner.wrap.createChild({
25251 cls: 'form-control', // bootstrap..
25253 name: this.frameId,
25254 frameBorder : 'no',
25255 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
25260 this.iframe = iframe.dom;
25262 this.assignDocWin();
25264 this.doc.designMode = 'on';
25267 this.doc.write(this.getDocMarkup());
25271 var task = { // must defer to wait for browser to be ready
25273 //console.log("run task?" + this.doc.readyState);
25274 this.assignDocWin();
25275 if(this.doc.body || this.doc.readyState == 'complete'){
25277 this.doc.designMode="on";
25281 Roo.TaskMgr.stop(task);
25282 this.initEditor.defer(10, this);
25289 Roo.TaskMgr.start(task);
25294 onResize : function(w, h)
25296 Roo.log('resize: ' +w + ',' + h );
25297 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
25301 if(typeof w == 'number'){
25303 this.iframe.style.width = w + 'px';
25305 if(typeof h == 'number'){
25307 this.iframe.style.height = h + 'px';
25309 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
25316 * Toggles the editor between standard and source edit mode.
25317 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25319 toggleSourceEdit : function(sourceEditMode){
25321 this.sourceEditMode = sourceEditMode === true;
25323 if(this.sourceEditMode){
25325 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
25328 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
25329 //this.iframe.className = '';
25332 //this.setSize(this.owner.wrap.getSize());
25333 //this.fireEvent('editmodechange', this, this.sourceEditMode);
25340 * Protected method that will not generally be called directly. If you need/want
25341 * custom HTML cleanup, this is the method you should override.
25342 * @param {String} html The HTML to be cleaned
25343 * return {String} The cleaned HTML
25345 cleanHtml : function(html){
25346 html = String(html);
25347 if(html.length > 5){
25348 if(Roo.isSafari){ // strip safari nonsense
25349 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25352 if(html == ' '){
25359 * HTML Editor -> Textarea
25360 * Protected method that will not generally be called directly. Syncs the contents
25361 * of the editor iframe with the textarea.
25363 syncValue : function(){
25364 if(this.initialized){
25365 var bd = (this.doc.body || this.doc.documentElement);
25366 //this.cleanUpPaste(); -- this is done else where and causes havoc..
25367 var html = bd.innerHTML;
25369 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25370 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25372 html = '<div style="'+m[0]+'">' + html + '</div>';
25375 html = this.cleanHtml(html);
25376 // fix up the special chars.. normaly like back quotes in word...
25377 // however we do not want to do this with chinese..
25378 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
25380 var cc = match.charCodeAt();
25382 // Get the character value, handling surrogate pairs
25383 if (match.length == 2) {
25384 // It's a surrogate pair, calculate the Unicode code point
25385 var high = match.charCodeAt(0) - 0xD800;
25386 var low = match.charCodeAt(1) - 0xDC00;
25387 cc = (high * 0x400) + low + 0x10000;
25389 (cc >= 0x4E00 && cc < 0xA000 ) ||
25390 (cc >= 0x3400 && cc < 0x4E00 ) ||
25391 (cc >= 0xf900 && cc < 0xfb00 )
25396 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
25397 return "&#" + cc + ";";
25404 if(this.owner.fireEvent('beforesync', this, html) !== false){
25405 this.el.dom.value = html;
25406 this.owner.fireEvent('sync', this, html);
25412 * Protected method that will not generally be called directly. Pushes the value of the textarea
25413 * into the iframe editor.
25415 pushValue : function(){
25416 if(this.initialized){
25417 var v = this.el.dom.value.trim();
25419 // if(v.length < 1){
25423 if(this.owner.fireEvent('beforepush', this, v) !== false){
25424 var d = (this.doc.body || this.doc.documentElement);
25426 this.cleanUpPaste();
25427 this.el.dom.value = d.innerHTML;
25428 this.owner.fireEvent('push', this, v);
25434 deferFocus : function(){
25435 this.focus.defer(10, this);
25439 focus : function(){
25440 if(this.win && !this.sourceEditMode){
25447 assignDocWin: function()
25449 var iframe = this.iframe;
25452 this.doc = iframe.contentWindow.document;
25453 this.win = iframe.contentWindow;
25455 // if (!Roo.get(this.frameId)) {
25458 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25459 // this.win = Roo.get(this.frameId).dom.contentWindow;
25461 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25465 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25466 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25471 initEditor : function(){
25472 //console.log("INIT EDITOR");
25473 this.assignDocWin();
25477 this.doc.designMode="on";
25479 this.doc.write(this.getDocMarkup());
25482 var dbody = (this.doc.body || this.doc.documentElement);
25483 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25484 // this copies styles from the containing element into thsi one..
25485 // not sure why we need all of this..
25486 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25488 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25489 //ss['background-attachment'] = 'fixed'; // w3c
25490 dbody.bgProperties = 'fixed'; // ie
25491 //Roo.DomHelper.applyStyles(dbody, ss);
25492 Roo.EventManager.on(this.doc, {
25493 //'mousedown': this.onEditorEvent,
25494 'mouseup': this.onEditorEvent,
25495 'dblclick': this.onEditorEvent,
25496 'click': this.onEditorEvent,
25497 'keyup': this.onEditorEvent,
25502 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25504 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25505 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25507 this.initialized = true;
25509 this.owner.fireEvent('initialize', this);
25514 onDestroy : function(){
25520 //for (var i =0; i < this.toolbars.length;i++) {
25521 // // fixme - ask toolbars for heights?
25522 // this.toolbars[i].onDestroy();
25525 //this.wrap.dom.innerHTML = '';
25526 //this.wrap.remove();
25531 onFirstFocus : function(){
25533 this.assignDocWin();
25536 this.activated = true;
25539 if(Roo.isGecko){ // prevent silly gecko errors
25541 var s = this.win.getSelection();
25542 if(!s.focusNode || s.focusNode.nodeType != 3){
25543 var r = s.getRangeAt(0);
25544 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25549 this.execCmd('useCSS', true);
25550 this.execCmd('styleWithCSS', false);
25553 this.owner.fireEvent('activate', this);
25557 adjustFont: function(btn){
25558 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25559 //if(Roo.isSafari){ // safari
25562 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25563 if(Roo.isSafari){ // safari
25564 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25565 v = (v < 10) ? 10 : v;
25566 v = (v > 48) ? 48 : v;
25567 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25572 v = Math.max(1, v+adjust);
25574 this.execCmd('FontSize', v );
25577 onEditorEvent : function(e)
25579 this.owner.fireEvent('editorevent', this, e);
25580 // this.updateToolbar();
25581 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25584 insertTag : function(tg)
25586 // could be a bit smarter... -> wrap the current selected tRoo..
25587 if (tg.toLowerCase() == 'span' ||
25588 tg.toLowerCase() == 'code' ||
25589 tg.toLowerCase() == 'sup' ||
25590 tg.toLowerCase() == 'sub'
25593 range = this.createRange(this.getSelection());
25594 var wrappingNode = this.doc.createElement(tg.toLowerCase());
25595 wrappingNode.appendChild(range.extractContents());
25596 range.insertNode(wrappingNode);
25603 this.execCmd("formatblock", tg);
25607 insertText : function(txt)
25611 var range = this.createRange();
25612 range.deleteContents();
25613 //alert(Sender.getAttribute('label'));
25615 range.insertNode(this.doc.createTextNode(txt));
25621 * Executes a Midas editor command on the editor document and performs necessary focus and
25622 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25623 * @param {String} cmd The Midas command
25624 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25626 relayCmd : function(cmd, value){
25628 this.execCmd(cmd, value);
25629 this.owner.fireEvent('editorevent', this);
25630 //this.updateToolbar();
25631 this.owner.deferFocus();
25635 * Executes a Midas editor command directly on the editor document.
25636 * For visual commands, you should use {@link #relayCmd} instead.
25637 * <b>This should only be called after the editor is initialized.</b>
25638 * @param {String} cmd The Midas command
25639 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25641 execCmd : function(cmd, value){
25642 this.doc.execCommand(cmd, false, value === undefined ? null : value);
25649 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25651 * @param {String} text | dom node..
25653 insertAtCursor : function(text)
25656 if(!this.activated){
25662 var r = this.doc.selection.createRange();
25673 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25677 // from jquery ui (MIT licenced)
25679 var win = this.win;
25681 if (win.getSelection && win.getSelection().getRangeAt) {
25682 range = win.getSelection().getRangeAt(0);
25683 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25684 range.insertNode(node);
25685 } else if (win.document.selection && win.document.selection.createRange) {
25686 // no firefox support
25687 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25688 win.document.selection.createRange().pasteHTML(txt);
25690 // no firefox support
25691 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25692 this.execCmd('InsertHTML', txt);
25701 mozKeyPress : function(e){
25703 var c = e.getCharCode(), cmd;
25706 c = String.fromCharCode(c).toLowerCase();
25720 this.cleanUpPaste.defer(100, this);
25728 e.preventDefault();
25736 fixKeys : function(){ // load time branching for fastest keydown performance
25738 return function(e){
25739 var k = e.getKey(), r;
25742 r = this.doc.selection.createRange();
25745 r.pasteHTML('    ');
25752 r = this.doc.selection.createRange();
25754 var target = r.parentElement();
25755 if(!target || target.tagName.toLowerCase() != 'li'){
25757 r.pasteHTML('<br />');
25763 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25764 this.cleanUpPaste.defer(100, this);
25770 }else if(Roo.isOpera){
25771 return function(e){
25772 var k = e.getKey();
25776 this.execCmd('InsertHTML','    ');
25779 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25780 this.cleanUpPaste.defer(100, this);
25785 }else if(Roo.isSafari){
25786 return function(e){
25787 var k = e.getKey();
25791 this.execCmd('InsertText','\t');
25795 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25796 this.cleanUpPaste.defer(100, this);
25804 getAllAncestors: function()
25806 var p = this.getSelectedNode();
25809 a.push(p); // push blank onto stack..
25810 p = this.getParentElement();
25814 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25818 a.push(this.doc.body);
25822 lastSelNode : false,
25825 getSelection : function()
25827 this.assignDocWin();
25828 return Roo.isIE ? this.doc.selection : this.win.getSelection();
25831 getSelectedNode: function()
25833 // this may only work on Gecko!!!
25835 // should we cache this!!!!
25840 var range = this.createRange(this.getSelection()).cloneRange();
25843 var parent = range.parentElement();
25845 var testRange = range.duplicate();
25846 testRange.moveToElementText(parent);
25847 if (testRange.inRange(range)) {
25850 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25853 parent = parent.parentElement;
25858 // is ancestor a text element.
25859 var ac = range.commonAncestorContainer;
25860 if (ac.nodeType == 3) {
25861 ac = ac.parentNode;
25864 var ar = ac.childNodes;
25867 var other_nodes = [];
25868 var has_other_nodes = false;
25869 for (var i=0;i<ar.length;i++) {
25870 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
25873 // fullly contained node.
25875 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25880 // probably selected..
25881 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25882 other_nodes.push(ar[i]);
25886 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
25891 has_other_nodes = true;
25893 if (!nodes.length && other_nodes.length) {
25894 nodes= other_nodes;
25896 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25902 createRange: function(sel)
25904 // this has strange effects when using with
25905 // top toolbar - not sure if it's a great idea.
25906 //this.editor.contentWindow.focus();
25907 if (typeof sel != "undefined") {
25909 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25911 return this.doc.createRange();
25914 return this.doc.createRange();
25917 getParentElement: function()
25920 this.assignDocWin();
25921 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25923 var range = this.createRange(sel);
25926 var p = range.commonAncestorContainer;
25927 while (p.nodeType == 3) { // text node
25938 * Range intersection.. the hard stuff...
25942 * [ -- selected range --- ]
25946 * if end is before start or hits it. fail.
25947 * if start is after end or hits it fail.
25949 * if either hits (but other is outside. - then it's not
25955 // @see http://www.thismuchiknow.co.uk/?p=64.
25956 rangeIntersectsNode : function(range, node)
25958 var nodeRange = node.ownerDocument.createRange();
25960 nodeRange.selectNode(node);
25962 nodeRange.selectNodeContents(node);
25965 var rangeStartRange = range.cloneRange();
25966 rangeStartRange.collapse(true);
25968 var rangeEndRange = range.cloneRange();
25969 rangeEndRange.collapse(false);
25971 var nodeStartRange = nodeRange.cloneRange();
25972 nodeStartRange.collapse(true);
25974 var nodeEndRange = nodeRange.cloneRange();
25975 nodeEndRange.collapse(false);
25977 return rangeStartRange.compareBoundaryPoints(
25978 Range.START_TO_START, nodeEndRange) == -1 &&
25979 rangeEndRange.compareBoundaryPoints(
25980 Range.START_TO_START, nodeStartRange) == 1;
25984 rangeCompareNode : function(range, node)
25986 var nodeRange = node.ownerDocument.createRange();
25988 nodeRange.selectNode(node);
25990 nodeRange.selectNodeContents(node);
25994 range.collapse(true);
25996 nodeRange.collapse(true);
25998 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25999 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
26001 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26003 var nodeIsBefore = ss == 1;
26004 var nodeIsAfter = ee == -1;
26006 if (nodeIsBefore && nodeIsAfter) {
26009 if (!nodeIsBefore && nodeIsAfter) {
26010 return 1; //right trailed.
26013 if (nodeIsBefore && !nodeIsAfter) {
26014 return 2; // left trailed.
26020 // private? - in a new class?
26021 cleanUpPaste : function()
26023 // cleans up the whole document..
26024 Roo.log('cleanuppaste');
26026 this.cleanUpChildren(this.doc.body);
26027 var clean = this.cleanWordChars(this.doc.body.innerHTML);
26028 if (clean != this.doc.body.innerHTML) {
26029 this.doc.body.innerHTML = clean;
26034 cleanWordChars : function(input) {// change the chars to hex code
26035 var he = Roo.HtmlEditorCore;
26037 var output = input;
26038 Roo.each(he.swapCodes, function(sw) {
26039 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26041 output = output.replace(swapper, sw[1]);
26048 cleanUpChildren : function (n)
26050 if (!n.childNodes.length) {
26053 for (var i = n.childNodes.length-1; i > -1 ; i--) {
26054 this.cleanUpChild(n.childNodes[i]);
26061 cleanUpChild : function (node)
26064 //console.log(node);
26065 if (node.nodeName == "#text") {
26066 // clean up silly Windows -- stuff?
26069 if (node.nodeName == "#comment") {
26070 node.parentNode.removeChild(node);
26071 // clean up silly Windows -- stuff?
26074 var lcname = node.tagName.toLowerCase();
26075 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26076 // whitelist of tags..
26078 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26080 node.parentNode.removeChild(node);
26085 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
26087 // spans with no attributes - just remove them..
26088 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
26089 remove_keep_children = true;
26092 // remove <a name=....> as rendering on yahoo mailer is borked with this.
26093 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26095 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26096 // remove_keep_children = true;
26099 if (remove_keep_children) {
26100 this.cleanUpChildren(node);
26101 // inserts everything just before this node...
26102 while (node.childNodes.length) {
26103 var cn = node.childNodes[0];
26104 node.removeChild(cn);
26105 node.parentNode.insertBefore(cn, node);
26107 node.parentNode.removeChild(node);
26111 if (!node.attributes || !node.attributes.length) {
26116 this.cleanUpChildren(node);
26120 function cleanAttr(n,v)
26123 if (v.match(/^\./) || v.match(/^\//)) {
26126 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
26129 if (v.match(/^#/)) {
26132 if (v.match(/^\{/)) { // allow template editing.
26135 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26136 node.removeAttribute(n);
26140 var cwhite = this.cwhite;
26141 var cblack = this.cblack;
26143 function cleanStyle(n,v)
26145 if (v.match(/expression/)) { //XSS?? should we even bother..
26146 node.removeAttribute(n);
26150 var parts = v.split(/;/);
26153 Roo.each(parts, function(p) {
26154 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26158 var l = p.split(':').shift().replace(/\s+/g,'');
26159 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26161 if ( cwhite.length && cblack.indexOf(l) > -1) {
26162 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26163 //node.removeAttribute(n);
26167 // only allow 'c whitelisted system attributes'
26168 if ( cwhite.length && cwhite.indexOf(l) < 0) {
26169 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26170 //node.removeAttribute(n);
26180 if (clean.length) {
26181 node.setAttribute(n, clean.join(';'));
26183 node.removeAttribute(n);
26189 for (var i = node.attributes.length-1; i > -1 ; i--) {
26190 var a = node.attributes[i];
26193 if (a.name.toLowerCase().substr(0,2)=='on') {
26194 node.removeAttribute(a.name);
26197 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
26198 node.removeAttribute(a.name);
26201 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
26202 cleanAttr(a.name,a.value); // fixme..
26205 if (a.name == 'style') {
26206 cleanStyle(a.name,a.value);
26209 /// clean up MS crap..
26210 // tecnically this should be a list of valid class'es..
26213 if (a.name == 'class') {
26214 if (a.value.match(/^Mso/)) {
26215 node.removeAttribute('class');
26218 if (a.value.match(/^body$/)) {
26219 node.removeAttribute('class');
26230 this.cleanUpChildren(node);
26236 * Clean up MS wordisms...
26238 cleanWord : function(node)
26241 this.cleanWord(this.doc.body);
26246 node.nodeName == 'SPAN' &&
26247 !node.hasAttributes() &&
26248 node.childNodes.length == 1 &&
26249 node.firstChild.nodeName == "#text"
26251 var textNode = node.firstChild;
26252 node.removeChild(textNode);
26253 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
26254 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26256 node.parentNode.insertBefore(textNode, node);
26257 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
26258 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26260 node.parentNode.removeChild(node);
26263 if (node.nodeName == "#text") {
26264 // clean up silly Windows -- stuff?
26267 if (node.nodeName == "#comment") {
26268 node.parentNode.removeChild(node);
26269 // clean up silly Windows -- stuff?
26273 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26274 node.parentNode.removeChild(node);
26277 //Roo.log(node.tagName);
26278 // remove - but keep children..
26279 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26280 //Roo.log('-- removed');
26281 while (node.childNodes.length) {
26282 var cn = node.childNodes[0];
26283 node.removeChild(cn);
26284 node.parentNode.insertBefore(cn, node);
26285 // move node to parent - and clean it..
26286 this.cleanWord(cn);
26288 node.parentNode.removeChild(node);
26289 /// no need to iterate chidlren = it's got none..
26290 //this.iterateChildren(node, this.cleanWord);
26294 if (node.className.length) {
26296 var cn = node.className.split(/\W+/);
26298 Roo.each(cn, function(cls) {
26299 if (cls.match(/Mso[a-zA-Z]+/)) {
26304 node.className = cna.length ? cna.join(' ') : '';
26306 node.removeAttribute("class");
26310 if (node.hasAttribute("lang")) {
26311 node.removeAttribute("lang");
26314 if (node.hasAttribute("style")) {
26316 var styles = node.getAttribute("style").split(";");
26318 Roo.each(styles, function(s) {
26319 if (!s.match(/:/)) {
26322 var kv = s.split(":");
26323 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26326 // what ever is left... we allow.
26329 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26330 if (!nstyle.length) {
26331 node.removeAttribute('style');
26334 this.iterateChildren(node, this.cleanWord);
26340 * iterateChildren of a Node, calling fn each time, using this as the scole..
26341 * @param {DomNode} node node to iterate children of.
26342 * @param {Function} fn method of this class to call on each item.
26344 iterateChildren : function(node, fn)
26346 if (!node.childNodes.length) {
26349 for (var i = node.childNodes.length-1; i > -1 ; i--) {
26350 fn.call(this, node.childNodes[i])
26356 * cleanTableWidths.
26358 * Quite often pasting from word etc.. results in tables with column and widths.
26359 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26362 cleanTableWidths : function(node)
26367 this.cleanTableWidths(this.doc.body);
26372 if (node.nodeName == "#text" || node.nodeName == "#comment") {
26375 Roo.log(node.tagName);
26376 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
26377 this.iterateChildren(node, this.cleanTableWidths);
26380 if (node.hasAttribute('width')) {
26381 node.removeAttribute('width');
26385 if (node.hasAttribute("style")) {
26388 var styles = node.getAttribute("style").split(";");
26390 Roo.each(styles, function(s) {
26391 if (!s.match(/:/)) {
26394 var kv = s.split(":");
26395 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26398 // what ever is left... we allow.
26401 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26402 if (!nstyle.length) {
26403 node.removeAttribute('style');
26407 this.iterateChildren(node, this.cleanTableWidths);
26415 domToHTML : function(currentElement, depth, nopadtext) {
26417 depth = depth || 0;
26418 nopadtext = nopadtext || false;
26420 if (!currentElement) {
26421 return this.domToHTML(this.doc.body);
26424 //Roo.log(currentElement);
26426 var allText = false;
26427 var nodeName = currentElement.nodeName;
26428 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
26430 if (nodeName == '#text') {
26432 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
26437 if (nodeName != 'BODY') {
26440 // Prints the node tagName, such as <A>, <IMG>, etc
26443 for(i = 0; i < currentElement.attributes.length;i++) {
26445 var aname = currentElement.attributes.item(i).name;
26446 if (!currentElement.attributes.item(i).value.length) {
26449 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
26452 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
26461 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
26464 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
26469 // Traverse the tree
26471 var currentElementChild = currentElement.childNodes.item(i);
26472 var allText = true;
26473 var innerHTML = '';
26475 while (currentElementChild) {
26476 // Formatting code (indent the tree so it looks nice on the screen)
26477 var nopad = nopadtext;
26478 if (lastnode == 'SPAN') {
26482 if (currentElementChild.nodeName == '#text') {
26483 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
26484 toadd = nopadtext ? toadd : toadd.trim();
26485 if (!nopad && toadd.length > 80) {
26486 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
26488 innerHTML += toadd;
26491 currentElementChild = currentElement.childNodes.item(i);
26497 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
26499 // Recursively traverse the tree structure of the child node
26500 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
26501 lastnode = currentElementChild.nodeName;
26503 currentElementChild=currentElement.childNodes.item(i);
26509 // The remaining code is mostly for formatting the tree
26510 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
26515 ret+= "</"+tagName+">";
26521 applyBlacklists : function()
26523 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
26524 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
26528 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
26529 if (b.indexOf(tag) > -1) {
26532 this.white.push(tag);
26536 Roo.each(w, function(tag) {
26537 if (b.indexOf(tag) > -1) {
26540 if (this.white.indexOf(tag) > -1) {
26543 this.white.push(tag);
26548 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
26549 if (w.indexOf(tag) > -1) {
26552 this.black.push(tag);
26556 Roo.each(b, function(tag) {
26557 if (w.indexOf(tag) > -1) {
26560 if (this.black.indexOf(tag) > -1) {
26563 this.black.push(tag);
26568 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
26569 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
26573 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
26574 if (b.indexOf(tag) > -1) {
26577 this.cwhite.push(tag);
26581 Roo.each(w, function(tag) {
26582 if (b.indexOf(tag) > -1) {
26585 if (this.cwhite.indexOf(tag) > -1) {
26588 this.cwhite.push(tag);
26593 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
26594 if (w.indexOf(tag) > -1) {
26597 this.cblack.push(tag);
26601 Roo.each(b, function(tag) {
26602 if (w.indexOf(tag) > -1) {
26605 if (this.cblack.indexOf(tag) > -1) {
26608 this.cblack.push(tag);
26613 setStylesheets : function(stylesheets)
26615 if(typeof(stylesheets) == 'string'){
26616 Roo.get(this.iframe.contentDocument.head).createChild({
26618 rel : 'stylesheet',
26627 Roo.each(stylesheets, function(s) {
26632 Roo.get(_this.iframe.contentDocument.head).createChild({
26634 rel : 'stylesheet',
26643 removeStylesheets : function()
26647 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
26652 setStyle : function(style)
26654 Roo.get(this.iframe.contentDocument.head).createChild({
26663 // hide stuff that is not compatible
26677 * @event specialkey
26681 * @cfg {String} fieldClass @hide
26684 * @cfg {String} focusClass @hide
26687 * @cfg {String} autoCreate @hide
26690 * @cfg {String} inputType @hide
26693 * @cfg {String} invalidClass @hide
26696 * @cfg {String} invalidText @hide
26699 * @cfg {String} msgFx @hide
26702 * @cfg {String} validateOnBlur @hide
26706 Roo.HtmlEditorCore.white = [
26707 'area', 'br', 'img', 'input', 'hr', 'wbr',
26709 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
26710 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
26711 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
26712 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
26713 'table', 'ul', 'xmp',
26715 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
26718 'dir', 'menu', 'ol', 'ul', 'dl',
26724 Roo.HtmlEditorCore.black = [
26725 // 'embed', 'object', // enable - backend responsiblity to clean thiese
26727 'base', 'basefont', 'bgsound', 'blink', 'body',
26728 'frame', 'frameset', 'head', 'html', 'ilayer',
26729 'iframe', 'layer', 'link', 'meta', 'object',
26730 'script', 'style' ,'title', 'xml' // clean later..
26732 Roo.HtmlEditorCore.clean = [
26733 'script', 'style', 'title', 'xml'
26735 Roo.HtmlEditorCore.remove = [
26740 Roo.HtmlEditorCore.ablack = [
26744 Roo.HtmlEditorCore.aclean = [
26745 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
26749 Roo.HtmlEditorCore.pwhite= [
26750 'http', 'https', 'mailto'
26753 // white listed style attributes.
26754 Roo.HtmlEditorCore.cwhite= [
26755 // 'text-align', /// default is to allow most things..
26761 // black listed style attributes.
26762 Roo.HtmlEditorCore.cblack= [
26763 // 'font-size' -- this can be set by the project
26767 Roo.HtmlEditorCore.swapCodes =[
26768 [ 8211, "–" ],
26769 [ 8212, "—" ],
26786 * @class Roo.bootstrap.HtmlEditor
26787 * @extends Roo.bootstrap.TextArea
26788 * Bootstrap HtmlEditor class
26791 * Create a new HtmlEditor
26792 * @param {Object} config The config object
26795 Roo.bootstrap.HtmlEditor = function(config){
26796 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
26797 if (!this.toolbars) {
26798 this.toolbars = [];
26801 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26804 * @event initialize
26805 * Fires when the editor is fully initialized (including the iframe)
26806 * @param {HtmlEditor} this
26811 * Fires when the editor is first receives the focus. Any insertion must wait
26812 * until after this event.
26813 * @param {HtmlEditor} this
26817 * @event beforesync
26818 * Fires before the textarea is updated with content from the editor iframe. Return false
26819 * to cancel the sync.
26820 * @param {HtmlEditor} this
26821 * @param {String} html
26825 * @event beforepush
26826 * Fires before the iframe editor is updated with content from the textarea. Return false
26827 * to cancel the push.
26828 * @param {HtmlEditor} this
26829 * @param {String} html
26834 * Fires when the textarea is updated with content from the editor iframe.
26835 * @param {HtmlEditor} this
26836 * @param {String} html
26841 * Fires when the iframe editor is updated with content from the textarea.
26842 * @param {HtmlEditor} this
26843 * @param {String} html
26847 * @event editmodechange
26848 * Fires when the editor switches edit modes
26849 * @param {HtmlEditor} this
26850 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26852 editmodechange: true,
26854 * @event editorevent
26855 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26856 * @param {HtmlEditor} this
26860 * @event firstfocus
26861 * Fires when on first focus - needed by toolbars..
26862 * @param {HtmlEditor} this
26867 * Auto save the htmlEditor value as a file into Events
26868 * @param {HtmlEditor} this
26872 * @event savedpreview
26873 * preview the saved version of htmlEditor
26874 * @param {HtmlEditor} this
26881 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
26885 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26890 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
26895 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
26900 * @cfg {Number} height (in pixels)
26904 * @cfg {Number} width (in pixels)
26909 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26912 stylesheets: false,
26917 // private properties
26918 validationEvent : false,
26920 initialized : false,
26923 onFocus : Roo.emptyFn,
26925 hideMode:'offsets',
26927 tbContainer : false,
26931 toolbarContainer :function() {
26932 return this.wrap.select('.x-html-editor-tb',true).first();
26936 * Protected method that will not generally be called directly. It
26937 * is called when the editor creates its toolbar. Override this method if you need to
26938 * add custom toolbar buttons.
26939 * @param {HtmlEditor} editor
26941 createToolbar : function(){
26942 Roo.log('renewing');
26943 Roo.log("create toolbars");
26945 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26946 this.toolbars[0].render(this.toolbarContainer());
26950 // if (!editor.toolbars || !editor.toolbars.length) {
26951 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26954 // for (var i =0 ; i < editor.toolbars.length;i++) {
26955 // editor.toolbars[i] = Roo.factory(
26956 // typeof(editor.toolbars[i]) == 'string' ?
26957 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
26958 // Roo.bootstrap.HtmlEditor);
26959 // editor.toolbars[i].init(editor);
26965 onRender : function(ct, position)
26967 // Roo.log("Call onRender: " + this.xtype);
26969 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26971 this.wrap = this.inputEl().wrap({
26972 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26975 this.editorcore.onRender(ct, position);
26977 if (this.resizable) {
26978 this.resizeEl = new Roo.Resizable(this.wrap, {
26982 minHeight : this.height,
26983 height: this.height,
26984 handles : this.resizable,
26987 resize : function(r, w, h) {
26988 _t.onResize(w,h); // -something
26994 this.createToolbar(this);
26997 if(!this.width && this.resizable){
26998 this.setSize(this.wrap.getSize());
27000 if (this.resizeEl) {
27001 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27002 // should trigger onReize..
27008 onResize : function(w, h)
27010 Roo.log('resize: ' +w + ',' + h );
27011 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
27015 if(this.inputEl() ){
27016 if(typeof w == 'number'){
27017 var aw = w - this.wrap.getFrameWidth('lr');
27018 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27021 if(typeof h == 'number'){
27022 var tbh = -11; // fixme it needs to tool bar size!
27023 for (var i =0; i < this.toolbars.length;i++) {
27024 // fixme - ask toolbars for heights?
27025 tbh += this.toolbars[i].el.getHeight();
27026 //if (this.toolbars[i].footer) {
27027 // tbh += this.toolbars[i].footer.el.getHeight();
27035 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27036 ah -= 5; // knock a few pixes off for look..
27037 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27041 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27042 this.editorcore.onResize(ew,eh);
27047 * Toggles the editor between standard and source edit mode.
27048 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27050 toggleSourceEdit : function(sourceEditMode)
27052 this.editorcore.toggleSourceEdit(sourceEditMode);
27054 if(this.editorcore.sourceEditMode){
27055 Roo.log('editor - showing textarea');
27058 // Roo.log(this.syncValue());
27060 this.inputEl().removeClass(['hide', 'x-hidden']);
27061 this.inputEl().dom.removeAttribute('tabIndex');
27062 this.inputEl().focus();
27064 Roo.log('editor - hiding textarea');
27066 // Roo.log(this.pushValue());
27069 this.inputEl().addClass(['hide', 'x-hidden']);
27070 this.inputEl().dom.setAttribute('tabIndex', -1);
27071 //this.deferFocus();
27074 if(this.resizable){
27075 this.setSize(this.wrap.getSize());
27078 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27081 // private (for BoxComponent)
27082 adjustSize : Roo.BoxComponent.prototype.adjustSize,
27084 // private (for BoxComponent)
27085 getResizeEl : function(){
27089 // private (for BoxComponent)
27090 getPositionEl : function(){
27095 initEvents : function(){
27096 this.originalValue = this.getValue();
27100 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27103 // markInvalid : Roo.emptyFn,
27105 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27108 // clearInvalid : Roo.emptyFn,
27110 setValue : function(v){
27111 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
27112 this.editorcore.pushValue();
27117 deferFocus : function(){
27118 this.focus.defer(10, this);
27122 focus : function(){
27123 this.editorcore.focus();
27129 onDestroy : function(){
27135 for (var i =0; i < this.toolbars.length;i++) {
27136 // fixme - ask toolbars for heights?
27137 this.toolbars[i].onDestroy();
27140 this.wrap.dom.innerHTML = '';
27141 this.wrap.remove();
27146 onFirstFocus : function(){
27147 //Roo.log("onFirstFocus");
27148 this.editorcore.onFirstFocus();
27149 for (var i =0; i < this.toolbars.length;i++) {
27150 this.toolbars[i].onFirstFocus();
27156 syncValue : function()
27158 this.editorcore.syncValue();
27161 pushValue : function()
27163 this.editorcore.pushValue();
27167 // hide stuff that is not compatible
27181 * @event specialkey
27185 * @cfg {String} fieldClass @hide
27188 * @cfg {String} focusClass @hide
27191 * @cfg {String} autoCreate @hide
27194 * @cfg {String} inputType @hide
27198 * @cfg {String} invalidText @hide
27201 * @cfg {String} msgFx @hide
27204 * @cfg {String} validateOnBlur @hide
27213 Roo.namespace('Roo.bootstrap.htmleditor');
27215 * @class Roo.bootstrap.HtmlEditorToolbar1
27221 new Roo.bootstrap.HtmlEditor({
27224 new Roo.bootstrap.HtmlEditorToolbar1({
27225 disable : { fonts: 1 , format: 1, ..., ... , ...],
27231 * @cfg {Object} disable List of elements to disable..
27232 * @cfg {Array} btns List of additional buttons.
27236 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27239 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
27242 Roo.apply(this, config);
27244 // default disabled, based on 'good practice'..
27245 this.disable = this.disable || {};
27246 Roo.applyIf(this.disable, {
27249 specialElements : true
27251 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
27253 this.editor = config.editor;
27254 this.editorcore = config.editor.editorcore;
27256 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
27258 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27259 // dont call parent... till later.
27261 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
27266 editorcore : false,
27271 "h1","h2","h3","h4","h5","h6",
27273 "abbr", "acronym", "address", "cite", "samp", "var",
27277 onRender : function(ct, position)
27279 // Roo.log("Call onRender: " + this.xtype);
27281 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
27283 this.el.dom.style.marginBottom = '0';
27285 var editorcore = this.editorcore;
27286 var editor= this.editor;
27289 var btn = function(id,cmd , toggle, handler, html){
27291 var event = toggle ? 'toggle' : 'click';
27296 xns: Roo.bootstrap,
27300 enableToggle:toggle !== false,
27302 pressed : toggle ? false : null,
27305 a.listeners[toggle ? 'toggle' : 'click'] = function() {
27306 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
27312 // var cb_box = function...
27317 xns: Roo.bootstrap,
27322 xns: Roo.bootstrap,
27326 Roo.each(this.formats, function(f) {
27327 style.menu.items.push({
27329 xns: Roo.bootstrap,
27330 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
27335 editorcore.insertTag(this.tagname);
27342 children.push(style);
27344 btn('bold',false,true);
27345 btn('italic',false,true);
27346 btn('align-left', 'justifyleft',true);
27347 btn('align-center', 'justifycenter',true);
27348 btn('align-right' , 'justifyright',true);
27349 btn('link', false, false, function(btn) {
27350 //Roo.log("create link?");
27351 var url = prompt(this.createLinkText, this.defaultLinkValue);
27352 if(url && url != 'http:/'+'/'){
27353 this.editorcore.relayCmd('createlink', url);
27356 btn('list','insertunorderedlist',true);
27357 btn('pencil', false,true, function(btn){
27359 this.toggleSourceEdit(btn.pressed);
27362 if (this.editor.btns.length > 0) {
27363 for (var i = 0; i<this.editor.btns.length; i++) {
27364 children.push(this.editor.btns[i]);
27372 xns: Roo.bootstrap,
27377 xns: Roo.bootstrap,
27382 cog.menu.items.push({
27384 xns: Roo.bootstrap,
27385 html : Clean styles,
27390 editorcore.insertTag(this.tagname);
27399 this.xtype = 'NavSimplebar';
27401 for(var i=0;i< children.length;i++) {
27403 this.buttons.add(this.addxtypeChild(children[i]));
27407 editor.on('editorevent', this.updateToolbar, this);
27409 onBtnClick : function(id)
27411 this.editorcore.relayCmd(id);
27412 this.editorcore.focus();
27416 * Protected method that will not generally be called directly. It triggers
27417 * a toolbar update by reading the markup state of the current selection in the editor.
27419 updateToolbar: function(){
27421 if(!this.editorcore.activated){
27422 this.editor.onFirstFocus(); // is this neeed?
27426 var btns = this.buttons;
27427 var doc = this.editorcore.doc;
27428 btns.get('bold').setActive(doc.queryCommandState('bold'));
27429 btns.get('italic').setActive(doc.queryCommandState('italic'));
27430 //btns.get('underline').setActive(doc.queryCommandState('underline'));
27432 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
27433 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
27434 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
27436 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
27437 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
27440 var ans = this.editorcore.getAllAncestors();
27441 if (this.formatCombo) {
27444 var store = this.formatCombo.store;
27445 this.formatCombo.setValue("");
27446 for (var i =0; i < ans.length;i++) {
27447 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27449 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27457 // hides menus... - so this cant be on a menu...
27458 Roo.bootstrap.MenuMgr.hideAll();
27460 Roo.bootstrap.MenuMgr.hideAll();
27461 //this.editorsyncValue();
27463 onFirstFocus: function() {
27464 this.buttons.each(function(item){
27468 toggleSourceEdit : function(sourceEditMode){
27471 if(sourceEditMode){
27472 Roo.log("disabling buttons");
27473 this.buttons.each( function(item){
27474 if(item.cmd != 'pencil'){
27480 Roo.log("enabling buttons");
27481 if(this.editorcore.initialized){
27482 this.buttons.each( function(item){
27488 Roo.log("calling toggole on editor");
27489 // tell the editor that it's been pressed..
27490 this.editor.toggleSourceEdit(sourceEditMode);
27504 * @class Roo.bootstrap.Markdown
27505 * @extends Roo.bootstrap.TextArea
27506 * Bootstrap Showdown editable area
27507 * @cfg {string} content
27510 * Create a new Showdown
27513 Roo.bootstrap.Markdown = function(config){
27514 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
27518 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
27522 initEvents : function()
27525 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
27526 this.markdownEl = this.el.createChild({
27527 cls : 'roo-markdown-area'
27529 this.inputEl().addClass('d-none');
27530 if (this.getValue() == '') {
27531 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
27534 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
27536 this.markdownEl.on('click', this.toggleTextEdit, this);
27537 this.on('blur', this.toggleTextEdit, this);
27538 this.on('specialkey', this.resizeTextArea, this);
27541 toggleTextEdit : function()
27543 var sh = this.markdownEl.getHeight();
27544 this.inputEl().addClass('d-none');
27545 this.markdownEl.addClass('d-none');
27546 if (!this.editing) {
27548 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
27549 this.inputEl().removeClass('d-none');
27550 this.inputEl().focus();
27551 this.editing = true;
27554 // show showdown...
27555 this.updateMarkdown();
27556 this.markdownEl.removeClass('d-none');
27557 this.editing = false;
27560 updateMarkdown : function()
27562 if (this.getValue() == '') {
27563 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
27567 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
27570 resizeTextArea: function () {
27573 Roo.log([sh, this.getValue().split("\n").length * 30]);
27574 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
27576 setValue : function(val)
27578 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
27579 if (!this.editing) {
27580 this.updateMarkdown();
27586 if (!this.editing) {
27587 this.toggleTextEdit();
27595 * Ext JS Library 1.1.1
27596 * Copyright(c) 2006-2007, Ext JS, LLC.
27598 * Originally Released Under LGPL - original licence link has changed is not relivant.
27601 * <script type="text/javascript">
27605 * @class Roo.bootstrap.PagingToolbar
27606 * @extends Roo.bootstrap.NavSimplebar
27607 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27609 * Create a new PagingToolbar
27610 * @param {Object} config The config object
27611 * @param {Roo.data.Store} store
27613 Roo.bootstrap.PagingToolbar = function(config)
27615 // old args format still supported... - xtype is prefered..
27616 // created from xtype...
27618 this.ds = config.dataSource;
27620 if (config.store && !this.ds) {
27621 this.store= Roo.factory(config.store, Roo.data);
27622 this.ds = this.store;
27623 this.ds.xmodule = this.xmodule || false;
27626 this.toolbarItems = [];
27627 if (config.items) {
27628 this.toolbarItems = config.items;
27631 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27636 this.bind(this.ds);
27639 if (Roo.bootstrap.version == 4) {
27640 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27642 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27647 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27649 * @cfg {Roo.data.Store} dataSource
27650 * The underlying data store providing the paged data
27653 * @cfg {String/HTMLElement/Element} container
27654 * container The id or element that will contain the toolbar
27657 * @cfg {Boolean} displayInfo
27658 * True to display the displayMsg (defaults to false)
27661 * @cfg {Number} pageSize
27662 * The number of records to display per page (defaults to 20)
27666 * @cfg {String} displayMsg
27667 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27669 displayMsg : 'Displaying {0} - {1} of {2}',
27671 * @cfg {String} emptyMsg
27672 * The message to display when no records are found (defaults to "No data to display")
27674 emptyMsg : 'No data to display',
27676 * Customizable piece of the default paging text (defaults to "Page")
27679 beforePageText : "Page",
27681 * Customizable piece of the default paging text (defaults to "of %0")
27684 afterPageText : "of {0}",
27686 * Customizable piece of the default paging text (defaults to "First Page")
27689 firstText : "First Page",
27691 * Customizable piece of the default paging text (defaults to "Previous Page")
27694 prevText : "Previous Page",
27696 * Customizable piece of the default paging text (defaults to "Next Page")
27699 nextText : "Next Page",
27701 * Customizable piece of the default paging text (defaults to "Last Page")
27704 lastText : "Last Page",
27706 * Customizable piece of the default paging text (defaults to "Refresh")
27709 refreshText : "Refresh",
27713 onRender : function(ct, position)
27715 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27716 this.navgroup.parentId = this.id;
27717 this.navgroup.onRender(this.el, null);
27718 // add the buttons to the navgroup
27720 if(this.displayInfo){
27721 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27722 this.displayEl = this.el.select('.x-paging-info', true).first();
27723 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27724 // this.displayEl = navel.el.select('span',true).first();
27730 Roo.each(_this.buttons, function(e){ // this might need to use render????
27731 Roo.factory(e).render(_this.el);
27735 Roo.each(_this.toolbarItems, function(e) {
27736 _this.navgroup.addItem(e);
27740 this.first = this.navgroup.addItem({
27741 tooltip: this.firstText,
27742 cls: "prev btn-outline-secondary",
27743 html : ' <i class="fa fa-step-backward"></i>',
27745 preventDefault: true,
27746 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27749 this.prev = this.navgroup.addItem({
27750 tooltip: this.prevText,
27751 cls: "prev btn-outline-secondary",
27752 html : ' <i class="fa fa-backward"></i>',
27754 preventDefault: true,
27755 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
27757 //this.addSeparator();
27760 var field = this.navgroup.addItem( {
27762 cls : 'x-paging-position btn-outline-secondary',
27764 html : this.beforePageText +
27765 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27766 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
27769 this.field = field.el.select('input', true).first();
27770 this.field.on("keydown", this.onPagingKeydown, this);
27771 this.field.on("focus", function(){this.dom.select();});
27774 this.afterTextEl = field.el.select('.x-paging-after',true).first();
27775 //this.field.setHeight(18);
27776 //this.addSeparator();
27777 this.next = this.navgroup.addItem({
27778 tooltip: this.nextText,
27779 cls: "next btn-outline-secondary",
27780 html : ' <i class="fa fa-forward"></i>',
27782 preventDefault: true,
27783 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
27785 this.last = this.navgroup.addItem({
27786 tooltip: this.lastText,
27787 html : ' <i class="fa fa-step-forward"></i>',
27788 cls: "next btn-outline-secondary",
27790 preventDefault: true,
27791 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
27793 //this.addSeparator();
27794 this.loading = this.navgroup.addItem({
27795 tooltip: this.refreshText,
27796 cls: "btn-outline-secondary",
27797 html : ' <i class="fa fa-refresh"></i>',
27798 preventDefault: true,
27799 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27805 updateInfo : function(){
27806 if(this.displayEl){
27807 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27808 var msg = count == 0 ?
27812 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
27814 this.displayEl.update(msg);
27819 onLoad : function(ds, r, o)
27821 this.cursor = o.params && o.params.start ? o.params.start : 0;
27823 var d = this.getPageData(),
27828 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27829 this.field.dom.value = ap;
27830 this.first.setDisabled(ap == 1);
27831 this.prev.setDisabled(ap == 1);
27832 this.next.setDisabled(ap == ps);
27833 this.last.setDisabled(ap == ps);
27834 this.loading.enable();
27839 getPageData : function(){
27840 var total = this.ds.getTotalCount();
27843 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27844 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27849 onLoadError : function(){
27850 this.loading.enable();
27854 onPagingKeydown : function(e){
27855 var k = e.getKey();
27856 var d = this.getPageData();
27858 var v = this.field.dom.value, pageNum;
27859 if(!v || isNaN(pageNum = parseInt(v, 10))){
27860 this.field.dom.value = d.activePage;
27863 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27864 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27867 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))
27869 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27870 this.field.dom.value = pageNum;
27871 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27874 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27876 var v = this.field.dom.value, pageNum;
27877 var increment = (e.shiftKey) ? 10 : 1;
27878 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27881 if(!v || isNaN(pageNum = parseInt(v, 10))) {
27882 this.field.dom.value = d.activePage;
27885 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27887 this.field.dom.value = parseInt(v, 10) + increment;
27888 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27889 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27896 beforeLoad : function(){
27898 this.loading.disable();
27903 onClick : function(which){
27912 ds.load({params:{start: 0, limit: this.pageSize}});
27915 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27918 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27921 var total = ds.getTotalCount();
27922 var extra = total % this.pageSize;
27923 var lastStart = extra ? (total - extra) : total-this.pageSize;
27924 ds.load({params:{start: lastStart, limit: this.pageSize}});
27927 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27933 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27934 * @param {Roo.data.Store} store The data store to unbind
27936 unbind : function(ds){
27937 ds.un("beforeload", this.beforeLoad, this);
27938 ds.un("load", this.onLoad, this);
27939 ds.un("loadexception", this.onLoadError, this);
27940 ds.un("remove", this.updateInfo, this);
27941 ds.un("add", this.updateInfo, this);
27942 this.ds = undefined;
27946 * Binds the paging toolbar to the specified {@link Roo.data.Store}
27947 * @param {Roo.data.Store} store The data store to bind
27949 bind : function(ds){
27950 ds.on("beforeload", this.beforeLoad, this);
27951 ds.on("load", this.onLoad, this);
27952 ds.on("loadexception", this.onLoadError, this);
27953 ds.on("remove", this.updateInfo, this);
27954 ds.on("add", this.updateInfo, this);
27965 * @class Roo.bootstrap.MessageBar
27966 * @extends Roo.bootstrap.Component
27967 * Bootstrap MessageBar class
27968 * @cfg {String} html contents of the MessageBar
27969 * @cfg {String} weight (info | success | warning | danger) default info
27970 * @cfg {String} beforeClass insert the bar before the given class
27971 * @cfg {Boolean} closable (true | false) default false
27972 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27975 * Create a new Element
27976 * @param {Object} config The config object
27979 Roo.bootstrap.MessageBar = function(config){
27980 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27983 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
27989 beforeClass: 'bootstrap-sticky-wrap',
27991 getAutoCreate : function(){
27995 cls: 'alert alert-dismissable alert-' + this.weight,
28000 html: this.html || ''
28006 cfg.cls += ' alert-messages-fixed';
28020 onRender : function(ct, position)
28022 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28025 var cfg = Roo.apply({}, this.getAutoCreate());
28029 cfg.cls += ' ' + this.cls;
28032 cfg.style = this.style;
28034 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28036 this.el.setVisibilityMode(Roo.Element.DISPLAY);
28039 this.el.select('>button.close').on('click', this.hide, this);
28045 if (!this.rendered) {
28051 this.fireEvent('show', this);
28057 if (!this.rendered) {
28063 this.fireEvent('hide', this);
28066 update : function()
28068 // var e = this.el.dom.firstChild;
28070 // if(this.closable){
28071 // e = e.nextSibling;
28074 // e.data = this.html || '';
28076 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28092 * @class Roo.bootstrap.Graph
28093 * @extends Roo.bootstrap.Component
28094 * Bootstrap Graph class
28098 @cfg {String} graphtype bar | vbar | pie
28099 @cfg {number} g_x coodinator | centre x (pie)
28100 @cfg {number} g_y coodinator | centre y (pie)
28101 @cfg {number} g_r radius (pie)
28102 @cfg {number} g_height height of the chart (respected by all elements in the set)
28103 @cfg {number} g_width width of the chart (respected by all elements in the set)
28104 @cfg {Object} title The title of the chart
28107 -opts (object) options for the chart
28109 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28110 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28112 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.
28113 o stacked (boolean) whether or not to tread values as in a stacked bar chart
28115 o stretch (boolean)
28117 -opts (object) options for the pie
28120 o startAngle (number)
28121 o endAngle (number)
28125 * Create a new Input
28126 * @param {Object} config The config object
28129 Roo.bootstrap.Graph = function(config){
28130 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28136 * The img click event for the img.
28137 * @param {Roo.EventObject} e
28143 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
28154 //g_colors: this.colors,
28161 getAutoCreate : function(){
28172 onRender : function(ct,position){
28175 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28177 if (typeof(Raphael) == 'undefined') {
28178 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28182 this.raphael = Raphael(this.el.dom);
28184 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28185 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28186 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28187 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28189 r.text(160, 10, "Single Series Chart").attr(txtattr);
28190 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28191 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28192 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28194 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28195 r.barchart(330, 10, 300, 220, data1);
28196 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28197 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28200 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28201 // r.barchart(30, 30, 560, 250, xdata, {
28202 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28203 // axis : "0 0 1 1",
28204 // axisxlabels : xdata
28205 // //yvalues : cols,
28208 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28210 // this.load(null,xdata,{
28211 // axis : "0 0 1 1",
28212 // axisxlabels : xdata
28217 load : function(graphtype,xdata,opts)
28219 this.raphael.clear();
28221 graphtype = this.graphtype;
28226 var r = this.raphael,
28227 fin = function () {
28228 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28230 fout = function () {
28231 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28233 pfin = function() {
28234 this.sector.stop();
28235 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28238 this.label[0].stop();
28239 this.label[0].attr({ r: 7.5 });
28240 this.label[1].attr({ "font-weight": 800 });
28243 pfout = function() {
28244 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28247 this.label[0].animate({ r: 5 }, 500, "bounce");
28248 this.label[1].attr({ "font-weight": 400 });
28254 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28257 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28260 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
28261 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28263 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28270 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28275 setTitle: function(o)
28280 initEvents: function() {
28283 this.el.on('click', this.onClick, this);
28287 onClick : function(e)
28289 Roo.log('img onclick');
28290 this.fireEvent('click', this, e);
28302 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28305 * @class Roo.bootstrap.dash.NumberBox
28306 * @extends Roo.bootstrap.Component
28307 * Bootstrap NumberBox class
28308 * @cfg {String} headline Box headline
28309 * @cfg {String} content Box content
28310 * @cfg {String} icon Box icon
28311 * @cfg {String} footer Footer text
28312 * @cfg {String} fhref Footer href
28315 * Create a new NumberBox
28316 * @param {Object} config The config object
28320 Roo.bootstrap.dash.NumberBox = function(config){
28321 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28325 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
28334 getAutoCreate : function(){
28338 cls : 'small-box ',
28346 cls : 'roo-headline',
28347 html : this.headline
28351 cls : 'roo-content',
28352 html : this.content
28366 cls : 'ion ' + this.icon
28375 cls : 'small-box-footer',
28376 href : this.fhref || '#',
28380 cfg.cn.push(footer);
28387 onRender : function(ct,position){
28388 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28395 setHeadline: function (value)
28397 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28400 setFooter: function (value, href)
28402 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28405 this.el.select('a.small-box-footer',true).first().attr('href', href);
28410 setContent: function (value)
28412 this.el.select('.roo-content',true).first().dom.innerHTML = value;
28415 initEvents: function()
28429 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28432 * @class Roo.bootstrap.dash.TabBox
28433 * @extends Roo.bootstrap.Component
28434 * Bootstrap TabBox class
28435 * @cfg {String} title Title of the TabBox
28436 * @cfg {String} icon Icon of the TabBox
28437 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28438 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28441 * Create a new TabBox
28442 * @param {Object} config The config object
28446 Roo.bootstrap.dash.TabBox = function(config){
28447 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28452 * When a pane is added
28453 * @param {Roo.bootstrap.dash.TabPane} pane
28457 * @event activatepane
28458 * When a pane is activated
28459 * @param {Roo.bootstrap.dash.TabPane} pane
28461 "activatepane" : true
28469 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28474 tabScrollable : false,
28476 getChildContainer : function()
28478 return this.el.select('.tab-content', true).first();
28481 getAutoCreate : function(){
28485 cls: 'pull-left header',
28493 cls: 'fa ' + this.icon
28499 cls: 'nav nav-tabs pull-right',
28505 if(this.tabScrollable){
28512 cls: 'nav nav-tabs pull-right',
28523 cls: 'nav-tabs-custom',
28528 cls: 'tab-content no-padding',
28536 initEvents : function()
28538 //Roo.log('add add pane handler');
28539 this.on('addpane', this.onAddPane, this);
28542 * Updates the box title
28543 * @param {String} html to set the title to.
28545 setTitle : function(value)
28547 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28549 onAddPane : function(pane)
28551 this.panes.push(pane);
28552 //Roo.log('addpane');
28554 // tabs are rendere left to right..
28555 if(!this.showtabs){
28559 var ctr = this.el.select('.nav-tabs', true).first();
28562 var existing = ctr.select('.nav-tab',true);
28563 var qty = existing.getCount();;
28566 var tab = ctr.createChild({
28568 cls : 'nav-tab' + (qty ? '' : ' active'),
28576 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28579 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28581 pane.el.addClass('active');
28586 onTabClick : function(ev,un,ob,pane)
28588 //Roo.log('tab - prev default');
28589 ev.preventDefault();
28592 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28593 pane.tab.addClass('active');
28594 //Roo.log(pane.title);
28595 this.getChildContainer().select('.tab-pane',true).removeClass('active');
28596 // technically we should have a deactivate event.. but maybe add later.
28597 // and it should not de-activate the selected tab...
28598 this.fireEvent('activatepane', pane);
28599 pane.el.addClass('active');
28600 pane.fireEvent('activate');
28605 getActivePane : function()
28608 Roo.each(this.panes, function(p) {
28609 if(p.el.hasClass('active')){
28630 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28632 * @class Roo.bootstrap.TabPane
28633 * @extends Roo.bootstrap.Component
28634 * Bootstrap TabPane class
28635 * @cfg {Boolean} active (false | true) Default false
28636 * @cfg {String} title title of panel
28640 * Create a new TabPane
28641 * @param {Object} config The config object
28644 Roo.bootstrap.dash.TabPane = function(config){
28645 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28651 * When a pane is activated
28652 * @param {Roo.bootstrap.dash.TabPane} pane
28659 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
28664 // the tabBox that this is attached to.
28667 getAutoCreate : function()
28675 cfg.cls += ' active';
28680 initEvents : function()
28682 //Roo.log('trigger add pane handler');
28683 this.parent().fireEvent('addpane', this)
28687 * Updates the tab title
28688 * @param {String} html to set the title to.
28690 setTitle: function(str)
28696 this.tab.select('a', true).first().dom.innerHTML = str;
28713 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28716 * @class Roo.bootstrap.menu.Menu
28717 * @extends Roo.bootstrap.Component
28718 * Bootstrap Menu class - container for Menu
28719 * @cfg {String} html Text of the menu
28720 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28721 * @cfg {String} icon Font awesome icon
28722 * @cfg {String} pos Menu align to (top | bottom) default bottom
28726 * Create a new Menu
28727 * @param {Object} config The config object
28731 Roo.bootstrap.menu.Menu = function(config){
28732 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28736 * @event beforeshow
28737 * Fires before this menu is displayed
28738 * @param {Roo.bootstrap.menu.Menu} this
28742 * @event beforehide
28743 * Fires before this menu is hidden
28744 * @param {Roo.bootstrap.menu.Menu} this
28749 * Fires after this menu is displayed
28750 * @param {Roo.bootstrap.menu.Menu} this
28755 * Fires after this menu is hidden
28756 * @param {Roo.bootstrap.menu.Menu} this
28761 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28762 * @param {Roo.bootstrap.menu.Menu} this
28763 * @param {Roo.EventObject} e
28770 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
28774 weight : 'default',
28779 getChildContainer : function() {
28780 if(this.isSubMenu){
28784 return this.el.select('ul.dropdown-menu', true).first();
28787 getAutoCreate : function()
28792 cls : 'roo-menu-text',
28800 cls : 'fa ' + this.icon
28811 cls : 'dropdown-button btn btn-' + this.weight,
28816 cls : 'dropdown-toggle btn btn-' + this.weight,
28826 cls : 'dropdown-menu'
28832 if(this.pos == 'top'){
28833 cfg.cls += ' dropup';
28836 if(this.isSubMenu){
28839 cls : 'dropdown-menu'
28846 onRender : function(ct, position)
28848 this.isSubMenu = ct.hasClass('dropdown-submenu');
28850 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28853 initEvents : function()
28855 if(this.isSubMenu){
28859 this.hidden = true;
28861 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28862 this.triggerEl.on('click', this.onTriggerPress, this);
28864 this.buttonEl = this.el.select('button.dropdown-button', true).first();
28865 this.buttonEl.on('click', this.onClick, this);
28871 if(this.isSubMenu){
28875 return this.el.select('ul.dropdown-menu', true).first();
28878 onClick : function(e)
28880 this.fireEvent("click", this, e);
28883 onTriggerPress : function(e)
28885 if (this.isVisible()) {
28892 isVisible : function(){
28893 return !this.hidden;
28898 this.fireEvent("beforeshow", this);
28900 this.hidden = false;
28901 this.el.addClass('open');
28903 Roo.get(document).on("mouseup", this.onMouseUp, this);
28905 this.fireEvent("show", this);
28912 this.fireEvent("beforehide", this);
28914 this.hidden = true;
28915 this.el.removeClass('open');
28917 Roo.get(document).un("mouseup", this.onMouseUp);
28919 this.fireEvent("hide", this);
28922 onMouseUp : function()
28936 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28939 * @class Roo.bootstrap.menu.Item
28940 * @extends Roo.bootstrap.Component
28941 * Bootstrap MenuItem class
28942 * @cfg {Boolean} submenu (true | false) default false
28943 * @cfg {String} html text of the item
28944 * @cfg {String} href the link
28945 * @cfg {Boolean} disable (true | false) default false
28946 * @cfg {Boolean} preventDefault (true | false) default true
28947 * @cfg {String} icon Font awesome icon
28948 * @cfg {String} pos Submenu align to (left | right) default right
28952 * Create a new Item
28953 * @param {Object} config The config object
28957 Roo.bootstrap.menu.Item = function(config){
28958 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28962 * Fires when the mouse is hovering over this menu
28963 * @param {Roo.bootstrap.menu.Item} this
28964 * @param {Roo.EventObject} e
28969 * Fires when the mouse exits this menu
28970 * @param {Roo.bootstrap.menu.Item} this
28971 * @param {Roo.EventObject} e
28977 * The raw click event for the entire grid.
28978 * @param {Roo.EventObject} e
28984 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
28989 preventDefault: true,
28994 getAutoCreate : function()
28999 cls : 'roo-menu-item-text',
29007 cls : 'fa ' + this.icon
29016 href : this.href || '#',
29023 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
29027 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
29029 if(this.pos == 'left'){
29030 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
29037 initEvents : function()
29039 this.el.on('mouseover', this.onMouseOver, this);
29040 this.el.on('mouseout', this.onMouseOut, this);
29042 this.el.select('a', true).first().on('click', this.onClick, this);
29046 onClick : function(e)
29048 if(this.preventDefault){
29049 e.preventDefault();
29052 this.fireEvent("click", this, e);
29055 onMouseOver : function(e)
29057 if(this.submenu && this.pos == 'left'){
29058 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29061 this.fireEvent("mouseover", this, e);
29064 onMouseOut : function(e)
29066 this.fireEvent("mouseout", this, e);
29078 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29081 * @class Roo.bootstrap.menu.Separator
29082 * @extends Roo.bootstrap.Component
29083 * Bootstrap Separator class
29086 * Create a new Separator
29087 * @param {Object} config The config object
29091 Roo.bootstrap.menu.Separator = function(config){
29092 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29095 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
29097 getAutoCreate : function(){
29100 cls: 'dropdown-divider divider'
29118 * @class Roo.bootstrap.Tooltip
29119 * Bootstrap Tooltip class
29120 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29121 * to determine which dom element triggers the tooltip.
29123 * It needs to add support for additional attributes like tooltip-position
29126 * Create a new Toolti
29127 * @param {Object} config The config object
29130 Roo.bootstrap.Tooltip = function(config){
29131 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29133 this.alignment = Roo.bootstrap.Tooltip.alignment;
29135 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29136 this.alignment = config.alignment;
29141 Roo.apply(Roo.bootstrap.Tooltip, {
29143 * @function init initialize tooltip monitoring.
29147 currentTip : false,
29148 currentRegion : false,
29154 Roo.get(document).on('mouseover', this.enter ,this);
29155 Roo.get(document).on('mouseout', this.leave, this);
29158 this.currentTip = new Roo.bootstrap.Tooltip();
29161 enter : function(ev)
29163 var dom = ev.getTarget();
29165 //Roo.log(['enter',dom]);
29166 var el = Roo.fly(dom);
29167 if (this.currentEl) {
29169 //Roo.log(this.currentEl);
29170 //Roo.log(this.currentEl.contains(dom));
29171 if (this.currentEl == el) {
29174 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29180 if (this.currentTip.el) {
29181 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29185 if(!el || el.dom == document){
29191 if (!el.attr('tooltip')) {
29192 pel = el.findParent("[tooltip]");
29194 bindEl = Roo.get(pel);
29200 // you can not look for children, as if el is the body.. then everythign is the child..
29201 if (!pel && !el.attr('tooltip')) { //
29202 if (!el.select("[tooltip]").elements.length) {
29205 // is the mouse over this child...?
29206 bindEl = el.select("[tooltip]").first();
29207 var xy = ev.getXY();
29208 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29209 //Roo.log("not in region.");
29212 //Roo.log("child element over..");
29215 this.currentEl = el;
29216 this.currentTip.bind(bindEl);
29217 this.currentRegion = Roo.lib.Region.getRegion(dom);
29218 this.currentTip.enter();
29221 leave : function(ev)
29223 var dom = ev.getTarget();
29224 //Roo.log(['leave',dom]);
29225 if (!this.currentEl) {
29230 if (dom != this.currentEl.dom) {
29233 var xy = ev.getXY();
29234 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
29237 // only activate leave if mouse cursor is outside... bounding box..
29242 if (this.currentTip) {
29243 this.currentTip.leave();
29245 //Roo.log('clear currentEl');
29246 this.currentEl = false;
29251 'left' : ['r-l', [-2,0], 'right'],
29252 'right' : ['l-r', [2,0], 'left'],
29253 'bottom' : ['t-b', [0,2], 'top'],
29254 'top' : [ 'b-t', [0,-2], 'bottom']
29260 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
29265 delay : null, // can be { show : 300 , hide: 500}
29269 hoverState : null, //???
29271 placement : 'bottom',
29275 getAutoCreate : function(){
29282 cls : 'tooltip-arrow arrow'
29285 cls : 'tooltip-inner'
29292 bind : function(el)
29297 initEvents : function()
29299 this.arrowEl = this.el.select('.arrow', true).first();
29300 this.innerEl = this.el.select('.tooltip-inner', true).first();
29303 enter : function () {
29305 if (this.timeout != null) {
29306 clearTimeout(this.timeout);
29309 this.hoverState = 'in';
29310 //Roo.log("enter - show");
29311 if (!this.delay || !this.delay.show) {
29316 this.timeout = setTimeout(function () {
29317 if (_t.hoverState == 'in') {
29320 }, this.delay.show);
29324 clearTimeout(this.timeout);
29326 this.hoverState = 'out';
29327 if (!this.delay || !this.delay.hide) {
29333 this.timeout = setTimeout(function () {
29334 //Roo.log("leave - timeout");
29336 if (_t.hoverState == 'out') {
29338 Roo.bootstrap.Tooltip.currentEl = false;
29343 show : function (msg)
29346 this.render(document.body);
29349 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29351 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29353 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29355 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29356 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29358 var placement = typeof this.placement == 'function' ?
29359 this.placement.call(this, this.el, on_el) :
29362 var autoToken = /\s?auto?\s?/i;
29363 var autoPlace = autoToken.test(placement);
29365 placement = placement.replace(autoToken, '') || 'top';
29369 //this.el.setXY([0,0]);
29371 //this.el.dom.style.display='block';
29373 //this.el.appendTo(on_el);
29375 var p = this.getPosition();
29376 var box = this.el.getBox();
29382 var align = this.alignment[placement];
29384 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29386 if(placement == 'top' || placement == 'bottom'){
29388 placement = 'right';
29391 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29392 placement = 'left';
29395 var scroll = Roo.select('body', true).first().getScroll();
29397 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29401 align = this.alignment[placement];
29403 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29407 var elems = document.getElementsByTagName('div');
29408 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29409 for (var i = 0; i < elems.length; i++) {
29410 var zindex = Number.parseInt(
29411 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29414 if (zindex > highest) {
29421 this.el.dom.style.zIndex = highest;
29423 this.el.alignTo(this.bindEl, align[0],align[1]);
29424 //var arrow = this.el.select('.arrow',true).first();
29425 //arrow.set(align[2],
29427 this.el.addClass(placement);
29428 this.el.addClass("bs-tooltip-"+ placement);
29430 this.el.addClass('in fade show');
29432 this.hoverState = null;
29434 if (this.el.hasClass('fade')) {
29449 //this.el.setXY([0,0]);
29450 this.el.removeClass(['show', 'in']);
29466 * @class Roo.bootstrap.LocationPicker
29467 * @extends Roo.bootstrap.Component
29468 * Bootstrap LocationPicker class
29469 * @cfg {Number} latitude Position when init default 0
29470 * @cfg {Number} longitude Position when init default 0
29471 * @cfg {Number} zoom default 15
29472 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29473 * @cfg {Boolean} mapTypeControl default false
29474 * @cfg {Boolean} disableDoubleClickZoom default false
29475 * @cfg {Boolean} scrollwheel default true
29476 * @cfg {Boolean} streetViewControl default false
29477 * @cfg {Number} radius default 0
29478 * @cfg {String} locationName
29479 * @cfg {Boolean} draggable default true
29480 * @cfg {Boolean} enableAutocomplete default false
29481 * @cfg {Boolean} enableReverseGeocode default true
29482 * @cfg {String} markerTitle
29485 * Create a new LocationPicker
29486 * @param {Object} config The config object
29490 Roo.bootstrap.LocationPicker = function(config){
29492 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29497 * Fires when the picker initialized.
29498 * @param {Roo.bootstrap.LocationPicker} this
29499 * @param {Google Location} location
29503 * @event positionchanged
29504 * Fires when the picker position changed.
29505 * @param {Roo.bootstrap.LocationPicker} this
29506 * @param {Google Location} location
29508 positionchanged : true,
29511 * Fires when the map resize.
29512 * @param {Roo.bootstrap.LocationPicker} this
29517 * Fires when the map show.
29518 * @param {Roo.bootstrap.LocationPicker} this
29523 * Fires when the map hide.
29524 * @param {Roo.bootstrap.LocationPicker} this
29529 * Fires when click the map.
29530 * @param {Roo.bootstrap.LocationPicker} this
29531 * @param {Map event} e
29535 * @event mapRightClick
29536 * Fires when right click the map.
29537 * @param {Roo.bootstrap.LocationPicker} this
29538 * @param {Map event} e
29540 mapRightClick : true,
29542 * @event markerClick
29543 * Fires when click the marker.
29544 * @param {Roo.bootstrap.LocationPicker} this
29545 * @param {Map event} e
29547 markerClick : true,
29549 * @event markerRightClick
29550 * Fires when right click the marker.
29551 * @param {Roo.bootstrap.LocationPicker} this
29552 * @param {Map event} e
29554 markerRightClick : true,
29556 * @event OverlayViewDraw
29557 * Fires when OverlayView Draw
29558 * @param {Roo.bootstrap.LocationPicker} this
29560 OverlayViewDraw : true,
29562 * @event OverlayViewOnAdd
29563 * Fires when OverlayView Draw
29564 * @param {Roo.bootstrap.LocationPicker} this
29566 OverlayViewOnAdd : true,
29568 * @event OverlayViewOnRemove
29569 * Fires when OverlayView Draw
29570 * @param {Roo.bootstrap.LocationPicker} this
29572 OverlayViewOnRemove : true,
29574 * @event OverlayViewShow
29575 * Fires when OverlayView Draw
29576 * @param {Roo.bootstrap.LocationPicker} this
29577 * @param {Pixel} cpx
29579 OverlayViewShow : true,
29581 * @event OverlayViewHide
29582 * Fires when OverlayView Draw
29583 * @param {Roo.bootstrap.LocationPicker} this
29585 OverlayViewHide : true,
29587 * @event loadexception
29588 * Fires when load google lib failed.
29589 * @param {Roo.bootstrap.LocationPicker} this
29591 loadexception : true
29596 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
29598 gMapContext: false,
29604 mapTypeControl: false,
29605 disableDoubleClickZoom: false,
29607 streetViewControl: false,
29611 enableAutocomplete: false,
29612 enableReverseGeocode: true,
29615 getAutoCreate: function()
29620 cls: 'roo-location-picker'
29626 initEvents: function(ct, position)
29628 if(!this.el.getWidth() || this.isApplied()){
29632 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29637 initial: function()
29639 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29640 this.fireEvent('loadexception', this);
29644 if(!this.mapTypeId){
29645 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29648 this.gMapContext = this.GMapContext();
29650 this.initOverlayView();
29652 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29656 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29657 _this.setPosition(_this.gMapContext.marker.position);
29660 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29661 _this.fireEvent('mapClick', this, event);
29665 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29666 _this.fireEvent('mapRightClick', this, event);
29670 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29671 _this.fireEvent('markerClick', this, event);
29675 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29676 _this.fireEvent('markerRightClick', this, event);
29680 this.setPosition(this.gMapContext.location);
29682 this.fireEvent('initial', this, this.gMapContext.location);
29685 initOverlayView: function()
29689 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29693 _this.fireEvent('OverlayViewDraw', _this);
29698 _this.fireEvent('OverlayViewOnAdd', _this);
29701 onRemove: function()
29703 _this.fireEvent('OverlayViewOnRemove', _this);
29706 show: function(cpx)
29708 _this.fireEvent('OverlayViewShow', _this, cpx);
29713 _this.fireEvent('OverlayViewHide', _this);
29719 fromLatLngToContainerPixel: function(event)
29721 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29724 isApplied: function()
29726 return this.getGmapContext() == false ? false : true;
29729 getGmapContext: function()
29731 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29734 GMapContext: function()
29736 var position = new google.maps.LatLng(this.latitude, this.longitude);
29738 var _map = new google.maps.Map(this.el.dom, {
29741 mapTypeId: this.mapTypeId,
29742 mapTypeControl: this.mapTypeControl,
29743 disableDoubleClickZoom: this.disableDoubleClickZoom,
29744 scrollwheel: this.scrollwheel,
29745 streetViewControl: this.streetViewControl,
29746 locationName: this.locationName,
29747 draggable: this.draggable,
29748 enableAutocomplete: this.enableAutocomplete,
29749 enableReverseGeocode: this.enableReverseGeocode
29752 var _marker = new google.maps.Marker({
29753 position: position,
29755 title: this.markerTitle,
29756 draggable: this.draggable
29763 location: position,
29764 radius: this.radius,
29765 locationName: this.locationName,
29766 addressComponents: {
29767 formatted_address: null,
29768 addressLine1: null,
29769 addressLine2: null,
29771 streetNumber: null,
29775 stateOrProvince: null
29778 domContainer: this.el.dom,
29779 geodecoder: new google.maps.Geocoder()
29783 drawCircle: function(center, radius, options)
29785 if (this.gMapContext.circle != null) {
29786 this.gMapContext.circle.setMap(null);
29790 options = Roo.apply({}, options, {
29791 strokeColor: "#0000FF",
29792 strokeOpacity: .35,
29794 fillColor: "#0000FF",
29798 options.map = this.gMapContext.map;
29799 options.radius = radius;
29800 options.center = center;
29801 this.gMapContext.circle = new google.maps.Circle(options);
29802 return this.gMapContext.circle;
29808 setPosition: function(location)
29810 this.gMapContext.location = location;
29811 this.gMapContext.marker.setPosition(location);
29812 this.gMapContext.map.panTo(location);
29813 this.drawCircle(location, this.gMapContext.radius, {});
29817 if (this.gMapContext.settings.enableReverseGeocode) {
29818 this.gMapContext.geodecoder.geocode({
29819 latLng: this.gMapContext.location
29820 }, function(results, status) {
29822 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29823 _this.gMapContext.locationName = results[0].formatted_address;
29824 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29826 _this.fireEvent('positionchanged', this, location);
29833 this.fireEvent('positionchanged', this, location);
29838 google.maps.event.trigger(this.gMapContext.map, "resize");
29840 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29842 this.fireEvent('resize', this);
29845 setPositionByLatLng: function(latitude, longitude)
29847 this.setPosition(new google.maps.LatLng(latitude, longitude));
29850 getCurrentPosition: function()
29853 latitude: this.gMapContext.location.lat(),
29854 longitude: this.gMapContext.location.lng()
29858 getAddressName: function()
29860 return this.gMapContext.locationName;
29863 getAddressComponents: function()
29865 return this.gMapContext.addressComponents;
29868 address_component_from_google_geocode: function(address_components)
29872 for (var i = 0; i < address_components.length; i++) {
29873 var component = address_components[i];
29874 if (component.types.indexOf("postal_code") >= 0) {
29875 result.postalCode = component.short_name;
29876 } else if (component.types.indexOf("street_number") >= 0) {
29877 result.streetNumber = component.short_name;
29878 } else if (component.types.indexOf("route") >= 0) {
29879 result.streetName = component.short_name;
29880 } else if (component.types.indexOf("neighborhood") >= 0) {
29881 result.city = component.short_name;
29882 } else if (component.types.indexOf("locality") >= 0) {
29883 result.city = component.short_name;
29884 } else if (component.types.indexOf("sublocality") >= 0) {
29885 result.district = component.short_name;
29886 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29887 result.stateOrProvince = component.short_name;
29888 } else if (component.types.indexOf("country") >= 0) {
29889 result.country = component.short_name;
29893 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29894 result.addressLine2 = "";
29898 setZoomLevel: function(zoom)
29900 this.gMapContext.map.setZoom(zoom);
29913 this.fireEvent('show', this);
29924 this.fireEvent('hide', this);
29929 Roo.apply(Roo.bootstrap.LocationPicker, {
29931 OverlayView : function(map, options)
29933 options = options || {};
29940 * @class Roo.bootstrap.Alert
29941 * @extends Roo.bootstrap.Component
29942 * Bootstrap Alert class - shows an alert area box
29944 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29945 Enter a valid email address
29948 * @cfg {String} title The title of alert
29949 * @cfg {String} html The content of alert
29950 * @cfg {String} weight (success|info|warning|danger) Weight of the message
29951 * @cfg {String} fa font-awesomeicon
29952 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
29953 * @cfg {Boolean} close true to show a x closer
29957 * Create a new alert
29958 * @param {Object} config The config object
29962 Roo.bootstrap.Alert = function(config){
29963 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29967 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
29973 faicon: false, // BC
29977 getAutoCreate : function()
29989 style : this.close ? '' : 'display:none'
29993 cls : 'roo-alert-icon'
29998 cls : 'roo-alert-title',
30003 cls : 'roo-alert-text',
30010 cfg.cn[0].cls += ' fa ' + this.faicon;
30013 cfg.cn[0].cls += ' fa ' + this.fa;
30017 cfg.cls += ' alert-' + this.weight;
30023 initEvents: function()
30025 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30026 this.titleEl = this.el.select('.roo-alert-title',true).first();
30027 this.iconEl = this.el.select('.roo-alert-icon',true).first();
30028 this.htmlEl = this.el.select('.roo-alert-text',true).first();
30029 if (this.seconds > 0) {
30030 this.hide.defer(this.seconds, this);
30034 * Set the Title Message HTML
30035 * @param {String} html
30037 setTitle : function(str)
30039 this.titleEl.dom.innerHTML = str;
30043 * Set the Body Message HTML
30044 * @param {String} html
30046 setHtml : function(str)
30048 this.htmlEl.dom.innerHTML = str;
30051 * Set the Weight of the alert
30052 * @param {String} (success|info|warning|danger) weight
30055 setWeight : function(weight)
30058 this.el.removeClass('alert-' + this.weight);
30061 this.weight = weight;
30063 this.el.addClass('alert-' + this.weight);
30066 * Set the Icon of the alert
30067 * @param {String} see fontawsome names (name without the 'fa-' bit)
30069 setIcon : function(icon)
30072 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30075 this.faicon = icon;
30077 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30102 * @class Roo.bootstrap.UploadCropbox
30103 * @extends Roo.bootstrap.Component
30104 * Bootstrap UploadCropbox class
30105 * @cfg {String} emptyText show when image has been loaded
30106 * @cfg {String} rotateNotify show when image too small to rotate
30107 * @cfg {Number} errorTimeout default 3000
30108 * @cfg {Number} minWidth default 300
30109 * @cfg {Number} minHeight default 300
30110 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30111 * @cfg {Boolean} isDocument (true|false) default false
30112 * @cfg {String} url action url
30113 * @cfg {String} paramName default 'imageUpload'
30114 * @cfg {String} method default POST
30115 * @cfg {Boolean} loadMask (true|false) default true
30116 * @cfg {Boolean} loadingText default 'Loading...'
30119 * Create a new UploadCropbox
30120 * @param {Object} config The config object
30123 Roo.bootstrap.UploadCropbox = function(config){
30124 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30128 * @event beforeselectfile
30129 * Fire before select file
30130 * @param {Roo.bootstrap.UploadCropbox} this
30132 "beforeselectfile" : true,
30135 * Fire after initEvent
30136 * @param {Roo.bootstrap.UploadCropbox} this
30141 * Fire after initEvent
30142 * @param {Roo.bootstrap.UploadCropbox} this
30143 * @param {String} data
30148 * Fire when preparing the file data
30149 * @param {Roo.bootstrap.UploadCropbox} this
30150 * @param {Object} file
30155 * Fire when get exception
30156 * @param {Roo.bootstrap.UploadCropbox} this
30157 * @param {XMLHttpRequest} xhr
30159 "exception" : true,
30161 * @event beforeloadcanvas
30162 * Fire before load the canvas
30163 * @param {Roo.bootstrap.UploadCropbox} this
30164 * @param {String} src
30166 "beforeloadcanvas" : true,
30169 * Fire when trash image
30170 * @param {Roo.bootstrap.UploadCropbox} this
30175 * Fire when download the image
30176 * @param {Roo.bootstrap.UploadCropbox} this
30180 * @event footerbuttonclick
30181 * Fire when footerbuttonclick
30182 * @param {Roo.bootstrap.UploadCropbox} this
30183 * @param {String} type
30185 "footerbuttonclick" : true,
30189 * @param {Roo.bootstrap.UploadCropbox} this
30194 * Fire when rotate the image
30195 * @param {Roo.bootstrap.UploadCropbox} this
30196 * @param {String} pos
30201 * Fire when inspect the file
30202 * @param {Roo.bootstrap.UploadCropbox} this
30203 * @param {Object} file
30208 * Fire when xhr upload the file
30209 * @param {Roo.bootstrap.UploadCropbox} this
30210 * @param {Object} data
30215 * Fire when arrange the file data
30216 * @param {Roo.bootstrap.UploadCropbox} this
30217 * @param {Object} formData
30222 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30225 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
30227 emptyText : 'Click to upload image',
30228 rotateNotify : 'Image is too small to rotate',
30229 errorTimeout : 3000,
30243 cropType : 'image/jpeg',
30245 canvasLoaded : false,
30246 isDocument : false,
30248 paramName : 'imageUpload',
30250 loadingText : 'Loading...',
30253 getAutoCreate : function()
30257 cls : 'roo-upload-cropbox',
30261 cls : 'roo-upload-cropbox-selector',
30266 cls : 'roo-upload-cropbox-body',
30267 style : 'cursor:pointer',
30271 cls : 'roo-upload-cropbox-preview'
30275 cls : 'roo-upload-cropbox-thumb'
30279 cls : 'roo-upload-cropbox-empty-notify',
30280 html : this.emptyText
30284 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30285 html : this.rotateNotify
30291 cls : 'roo-upload-cropbox-footer',
30294 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30304 onRender : function(ct, position)
30306 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30308 if (this.buttons.length) {
30310 Roo.each(this.buttons, function(bb) {
30312 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30314 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30320 this.maskEl = this.el;
30324 initEvents : function()
30326 this.urlAPI = (window.createObjectURL && window) ||
30327 (window.URL && URL.revokeObjectURL && URL) ||
30328 (window.webkitURL && webkitURL);
30330 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30331 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30333 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30334 this.selectorEl.hide();
30336 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30337 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30339 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30340 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30341 this.thumbEl.hide();
30343 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30344 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30346 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30347 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30348 this.errorEl.hide();
30350 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30351 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30352 this.footerEl.hide();
30354 this.setThumbBoxSize();
30360 this.fireEvent('initial', this);
30367 window.addEventListener("resize", function() { _this.resize(); } );
30369 this.bodyEl.on('click', this.beforeSelectFile, this);
30372 this.bodyEl.on('touchstart', this.onTouchStart, this);
30373 this.bodyEl.on('touchmove', this.onTouchMove, this);
30374 this.bodyEl.on('touchend', this.onTouchEnd, this);
30378 this.bodyEl.on('mousedown', this.onMouseDown, this);
30379 this.bodyEl.on('mousemove', this.onMouseMove, this);
30380 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30381 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30382 Roo.get(document).on('mouseup', this.onMouseUp, this);
30385 this.selectorEl.on('change', this.onFileSelected, this);
30391 this.baseScale = 1;
30393 this.baseRotate = 1;
30394 this.dragable = false;
30395 this.pinching = false;
30398 this.cropData = false;
30399 this.notifyEl.dom.innerHTML = this.emptyText;
30401 this.selectorEl.dom.value = '';
30405 resize : function()
30407 if(this.fireEvent('resize', this) != false){
30408 this.setThumbBoxPosition();
30409 this.setCanvasPosition();
30413 onFooterButtonClick : function(e, el, o, type)
30416 case 'rotate-left' :
30417 this.onRotateLeft(e);
30419 case 'rotate-right' :
30420 this.onRotateRight(e);
30423 this.beforeSelectFile(e);
30438 this.fireEvent('footerbuttonclick', this, type);
30441 beforeSelectFile : function(e)
30443 e.preventDefault();
30445 if(this.fireEvent('beforeselectfile', this) != false){
30446 this.selectorEl.dom.click();
30450 onFileSelected : function(e)
30452 e.preventDefault();
30454 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30458 var file = this.selectorEl.dom.files[0];
30460 if(this.fireEvent('inspect', this, file) != false){
30461 this.prepare(file);
30466 trash : function(e)
30468 this.fireEvent('trash', this);
30471 download : function(e)
30473 this.fireEvent('download', this);
30476 loadCanvas : function(src)
30478 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30482 this.imageEl = document.createElement('img');
30486 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30488 this.imageEl.src = src;
30492 onLoadCanvas : function()
30494 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30495 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30497 this.bodyEl.un('click', this.beforeSelectFile, this);
30499 this.notifyEl.hide();
30500 this.thumbEl.show();
30501 this.footerEl.show();
30503 this.baseRotateLevel();
30505 if(this.isDocument){
30506 this.setThumbBoxSize();
30509 this.setThumbBoxPosition();
30511 this.baseScaleLevel();
30517 this.canvasLoaded = true;
30520 this.maskEl.unmask();
30525 setCanvasPosition : function()
30527 if(!this.canvasEl){
30531 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30532 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30534 this.previewEl.setLeft(pw);
30535 this.previewEl.setTop(ph);
30539 onMouseDown : function(e)
30543 this.dragable = true;
30544 this.pinching = false;
30546 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30547 this.dragable = false;
30551 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30552 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30556 onMouseMove : function(e)
30560 if(!this.canvasLoaded){
30564 if (!this.dragable){
30568 var minX = Math.ceil(this.thumbEl.getLeft(true));
30569 var minY = Math.ceil(this.thumbEl.getTop(true));
30571 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30572 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30574 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30575 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30577 x = x - this.mouseX;
30578 y = y - this.mouseY;
30580 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30581 var bgY = Math.ceil(y + this.previewEl.getTop(true));
30583 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30584 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30586 this.previewEl.setLeft(bgX);
30587 this.previewEl.setTop(bgY);
30589 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30590 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30593 onMouseUp : function(e)
30597 this.dragable = false;
30600 onMouseWheel : function(e)
30604 this.startScale = this.scale;
30606 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30608 if(!this.zoomable()){
30609 this.scale = this.startScale;
30618 zoomable : function()
30620 var minScale = this.thumbEl.getWidth() / this.minWidth;
30622 if(this.minWidth < this.minHeight){
30623 minScale = this.thumbEl.getHeight() / this.minHeight;
30626 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30627 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30631 (this.rotate == 0 || this.rotate == 180) &&
30633 width > this.imageEl.OriginWidth ||
30634 height > this.imageEl.OriginHeight ||
30635 (width < this.minWidth && height < this.minHeight)
30643 (this.rotate == 90 || this.rotate == 270) &&
30645 width > this.imageEl.OriginWidth ||
30646 height > this.imageEl.OriginHeight ||
30647 (width < this.minHeight && height < this.minWidth)
30654 !this.isDocument &&
30655 (this.rotate == 0 || this.rotate == 180) &&
30657 width < this.minWidth ||
30658 width > this.imageEl.OriginWidth ||
30659 height < this.minHeight ||
30660 height > this.imageEl.OriginHeight
30667 !this.isDocument &&
30668 (this.rotate == 90 || this.rotate == 270) &&
30670 width < this.minHeight ||
30671 width > this.imageEl.OriginWidth ||
30672 height < this.minWidth ||
30673 height > this.imageEl.OriginHeight
30683 onRotateLeft : function(e)
30685 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30687 var minScale = this.thumbEl.getWidth() / this.minWidth;
30689 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30690 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30692 this.startScale = this.scale;
30694 while (this.getScaleLevel() < minScale){
30696 this.scale = this.scale + 1;
30698 if(!this.zoomable()){
30703 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30704 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30709 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30716 this.scale = this.startScale;
30718 this.onRotateFail();
30723 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30725 if(this.isDocument){
30726 this.setThumbBoxSize();
30727 this.setThumbBoxPosition();
30728 this.setCanvasPosition();
30733 this.fireEvent('rotate', this, 'left');
30737 onRotateRight : function(e)
30739 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30741 var minScale = this.thumbEl.getWidth() / this.minWidth;
30743 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30744 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30746 this.startScale = this.scale;
30748 while (this.getScaleLevel() < minScale){
30750 this.scale = this.scale + 1;
30752 if(!this.zoomable()){
30757 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30758 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30763 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30770 this.scale = this.startScale;
30772 this.onRotateFail();
30777 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30779 if(this.isDocument){
30780 this.setThumbBoxSize();
30781 this.setThumbBoxPosition();
30782 this.setCanvasPosition();
30787 this.fireEvent('rotate', this, 'right');
30790 onRotateFail : function()
30792 this.errorEl.show(true);
30796 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30801 this.previewEl.dom.innerHTML = '';
30803 var canvasEl = document.createElement("canvas");
30805 var contextEl = canvasEl.getContext("2d");
30807 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30808 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30809 var center = this.imageEl.OriginWidth / 2;
30811 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30812 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30813 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30814 center = this.imageEl.OriginHeight / 2;
30817 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30819 contextEl.translate(center, center);
30820 contextEl.rotate(this.rotate * Math.PI / 180);
30822 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30824 this.canvasEl = document.createElement("canvas");
30826 this.contextEl = this.canvasEl.getContext("2d");
30828 switch (this.rotate) {
30831 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30832 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30834 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30839 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30840 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30842 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30843 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);
30847 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30852 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30853 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30855 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30856 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);
30860 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);
30865 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30866 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30868 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30869 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30873 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);
30880 this.previewEl.appendChild(this.canvasEl);
30882 this.setCanvasPosition();
30887 if(!this.canvasLoaded){
30891 var imageCanvas = document.createElement("canvas");
30893 var imageContext = imageCanvas.getContext("2d");
30895 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30896 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30898 var center = imageCanvas.width / 2;
30900 imageContext.translate(center, center);
30902 imageContext.rotate(this.rotate * Math.PI / 180);
30904 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30906 var canvas = document.createElement("canvas");
30908 var context = canvas.getContext("2d");
30910 canvas.width = this.minWidth;
30911 canvas.height = this.minHeight;
30913 switch (this.rotate) {
30916 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30917 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30919 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30920 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30922 var targetWidth = this.minWidth - 2 * x;
30923 var targetHeight = this.minHeight - 2 * y;
30927 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30928 scale = targetWidth / width;
30931 if(x > 0 && y == 0){
30932 scale = targetHeight / height;
30935 if(x > 0 && y > 0){
30936 scale = targetWidth / width;
30938 if(width < height){
30939 scale = targetHeight / height;
30943 context.scale(scale, scale);
30945 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30946 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30948 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30949 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30951 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30956 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30957 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30959 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30960 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30962 var targetWidth = this.minWidth - 2 * x;
30963 var targetHeight = this.minHeight - 2 * y;
30967 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30968 scale = targetWidth / width;
30971 if(x > 0 && y == 0){
30972 scale = targetHeight / height;
30975 if(x > 0 && y > 0){
30976 scale = targetWidth / width;
30978 if(width < height){
30979 scale = targetHeight / height;
30983 context.scale(scale, scale);
30985 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30986 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30988 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30989 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30991 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30993 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30998 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30999 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31001 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31002 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31004 var targetWidth = this.minWidth - 2 * x;
31005 var targetHeight = this.minHeight - 2 * y;
31009 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31010 scale = targetWidth / width;
31013 if(x > 0 && y == 0){
31014 scale = targetHeight / height;
31017 if(x > 0 && y > 0){
31018 scale = targetWidth / width;
31020 if(width < height){
31021 scale = targetHeight / height;
31025 context.scale(scale, scale);
31027 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31028 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31030 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31031 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31033 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31034 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31036 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31041 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31042 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31044 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31045 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31047 var targetWidth = this.minWidth - 2 * x;
31048 var targetHeight = this.minHeight - 2 * y;
31052 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31053 scale = targetWidth / width;
31056 if(x > 0 && y == 0){
31057 scale = targetHeight / height;
31060 if(x > 0 && y > 0){
31061 scale = targetWidth / width;
31063 if(width < height){
31064 scale = targetHeight / height;
31068 context.scale(scale, scale);
31070 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31071 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31073 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31074 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31076 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31078 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31085 this.cropData = canvas.toDataURL(this.cropType);
31087 if(this.fireEvent('crop', this, this.cropData) !== false){
31088 this.process(this.file, this.cropData);
31095 setThumbBoxSize : function()
31099 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31100 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31101 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31103 this.minWidth = width;
31104 this.minHeight = height;
31106 if(this.rotate == 90 || this.rotate == 270){
31107 this.minWidth = height;
31108 this.minHeight = width;
31113 width = Math.ceil(this.minWidth * height / this.minHeight);
31115 if(this.minWidth > this.minHeight){
31117 height = Math.ceil(this.minHeight * width / this.minWidth);
31120 this.thumbEl.setStyle({
31121 width : width + 'px',
31122 height : height + 'px'
31129 setThumbBoxPosition : function()
31131 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31132 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31134 this.thumbEl.setLeft(x);
31135 this.thumbEl.setTop(y);
31139 baseRotateLevel : function()
31141 this.baseRotate = 1;
31144 typeof(this.exif) != 'undefined' &&
31145 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31146 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31148 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31151 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31155 baseScaleLevel : function()
31159 if(this.isDocument){
31161 if(this.baseRotate == 6 || this.baseRotate == 8){
31163 height = this.thumbEl.getHeight();
31164 this.baseScale = height / this.imageEl.OriginWidth;
31166 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31167 width = this.thumbEl.getWidth();
31168 this.baseScale = width / this.imageEl.OriginHeight;
31174 height = this.thumbEl.getHeight();
31175 this.baseScale = height / this.imageEl.OriginHeight;
31177 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31178 width = this.thumbEl.getWidth();
31179 this.baseScale = width / this.imageEl.OriginWidth;
31185 if(this.baseRotate == 6 || this.baseRotate == 8){
31187 width = this.thumbEl.getHeight();
31188 this.baseScale = width / this.imageEl.OriginHeight;
31190 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31191 height = this.thumbEl.getWidth();
31192 this.baseScale = height / this.imageEl.OriginHeight;
31195 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31196 height = this.thumbEl.getWidth();
31197 this.baseScale = height / this.imageEl.OriginHeight;
31199 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31200 width = this.thumbEl.getHeight();
31201 this.baseScale = width / this.imageEl.OriginWidth;
31208 width = this.thumbEl.getWidth();
31209 this.baseScale = width / this.imageEl.OriginWidth;
31211 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31212 height = this.thumbEl.getHeight();
31213 this.baseScale = height / this.imageEl.OriginHeight;
31216 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31218 height = this.thumbEl.getHeight();
31219 this.baseScale = height / this.imageEl.OriginHeight;
31221 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31222 width = this.thumbEl.getWidth();
31223 this.baseScale = width / this.imageEl.OriginWidth;
31231 getScaleLevel : function()
31233 return this.baseScale * Math.pow(1.1, this.scale);
31236 onTouchStart : function(e)
31238 if(!this.canvasLoaded){
31239 this.beforeSelectFile(e);
31243 var touches = e.browserEvent.touches;
31249 if(touches.length == 1){
31250 this.onMouseDown(e);
31254 if(touches.length != 2){
31260 for(var i = 0, finger; finger = touches[i]; i++){
31261 coords.push(finger.pageX, finger.pageY);
31264 var x = Math.pow(coords[0] - coords[2], 2);
31265 var y = Math.pow(coords[1] - coords[3], 2);
31267 this.startDistance = Math.sqrt(x + y);
31269 this.startScale = this.scale;
31271 this.pinching = true;
31272 this.dragable = false;
31276 onTouchMove : function(e)
31278 if(!this.pinching && !this.dragable){
31282 var touches = e.browserEvent.touches;
31289 this.onMouseMove(e);
31295 for(var i = 0, finger; finger = touches[i]; i++){
31296 coords.push(finger.pageX, finger.pageY);
31299 var x = Math.pow(coords[0] - coords[2], 2);
31300 var y = Math.pow(coords[1] - coords[3], 2);
31302 this.endDistance = Math.sqrt(x + y);
31304 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31306 if(!this.zoomable()){
31307 this.scale = this.startScale;
31315 onTouchEnd : function(e)
31317 this.pinching = false;
31318 this.dragable = false;
31322 process : function(file, crop)
31325 this.maskEl.mask(this.loadingText);
31328 this.xhr = new XMLHttpRequest();
31330 file.xhr = this.xhr;
31332 this.xhr.open(this.method, this.url, true);
31335 "Accept": "application/json",
31336 "Cache-Control": "no-cache",
31337 "X-Requested-With": "XMLHttpRequest"
31340 for (var headerName in headers) {
31341 var headerValue = headers[headerName];
31343 this.xhr.setRequestHeader(headerName, headerValue);
31349 this.xhr.onload = function()
31351 _this.xhrOnLoad(_this.xhr);
31354 this.xhr.onerror = function()
31356 _this.xhrOnError(_this.xhr);
31359 var formData = new FormData();
31361 formData.append('returnHTML', 'NO');
31364 formData.append('crop', crop);
31367 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31368 formData.append(this.paramName, file, file.name);
31371 if(typeof(file.filename) != 'undefined'){
31372 formData.append('filename', file.filename);
31375 if(typeof(file.mimetype) != 'undefined'){
31376 formData.append('mimetype', file.mimetype);
31379 if(this.fireEvent('arrange', this, formData) != false){
31380 this.xhr.send(formData);
31384 xhrOnLoad : function(xhr)
31387 this.maskEl.unmask();
31390 if (xhr.readyState !== 4) {
31391 this.fireEvent('exception', this, xhr);
31395 var response = Roo.decode(xhr.responseText);
31397 if(!response.success){
31398 this.fireEvent('exception', this, xhr);
31402 var response = Roo.decode(xhr.responseText);
31404 this.fireEvent('upload', this, response);
31408 xhrOnError : function()
31411 this.maskEl.unmask();
31414 Roo.log('xhr on error');
31416 var response = Roo.decode(xhr.responseText);
31422 prepare : function(file)
31425 this.maskEl.mask(this.loadingText);
31431 if(typeof(file) === 'string'){
31432 this.loadCanvas(file);
31436 if(!file || !this.urlAPI){
31441 this.cropType = file.type;
31445 if(this.fireEvent('prepare', this, this.file) != false){
31447 var reader = new FileReader();
31449 reader.onload = function (e) {
31450 if (e.target.error) {
31451 Roo.log(e.target.error);
31455 var buffer = e.target.result,
31456 dataView = new DataView(buffer),
31458 maxOffset = dataView.byteLength - 4,
31462 if (dataView.getUint16(0) === 0xffd8) {
31463 while (offset < maxOffset) {
31464 markerBytes = dataView.getUint16(offset);
31466 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31467 markerLength = dataView.getUint16(offset + 2) + 2;
31468 if (offset + markerLength > dataView.byteLength) {
31469 Roo.log('Invalid meta data: Invalid segment size.');
31473 if(markerBytes == 0xffe1){
31474 _this.parseExifData(
31481 offset += markerLength;
31491 var url = _this.urlAPI.createObjectURL(_this.file);
31493 _this.loadCanvas(url);
31498 reader.readAsArrayBuffer(this.file);
31504 parseExifData : function(dataView, offset, length)
31506 var tiffOffset = offset + 10,
31510 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31511 // No Exif data, might be XMP data instead
31515 // Check for the ASCII code for "Exif" (0x45786966):
31516 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31517 // No Exif data, might be XMP data instead
31520 if (tiffOffset + 8 > dataView.byteLength) {
31521 Roo.log('Invalid Exif data: Invalid segment size.');
31524 // Check for the two null bytes:
31525 if (dataView.getUint16(offset + 8) !== 0x0000) {
31526 Roo.log('Invalid Exif data: Missing byte alignment offset.');
31529 // Check the byte alignment:
31530 switch (dataView.getUint16(tiffOffset)) {
31532 littleEndian = true;
31535 littleEndian = false;
31538 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31541 // Check for the TIFF tag marker (0x002A):
31542 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31543 Roo.log('Invalid Exif data: Missing TIFF marker.');
31546 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31547 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31549 this.parseExifTags(
31552 tiffOffset + dirOffset,
31557 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31562 if (dirOffset + 6 > dataView.byteLength) {
31563 Roo.log('Invalid Exif data: Invalid directory offset.');
31566 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31567 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31568 if (dirEndOffset + 4 > dataView.byteLength) {
31569 Roo.log('Invalid Exif data: Invalid directory size.');
31572 for (i = 0; i < tagsNumber; i += 1) {
31576 dirOffset + 2 + 12 * i, // tag offset
31580 // Return the offset to the next directory:
31581 return dataView.getUint32(dirEndOffset, littleEndian);
31584 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
31586 var tag = dataView.getUint16(offset, littleEndian);
31588 this.exif[tag] = this.getExifValue(
31592 dataView.getUint16(offset + 2, littleEndian), // tag type
31593 dataView.getUint32(offset + 4, littleEndian), // tag length
31598 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31600 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31609 Roo.log('Invalid Exif data: Invalid tag type.');
31613 tagSize = tagType.size * length;
31614 // Determine if the value is contained in the dataOffset bytes,
31615 // or if the value at the dataOffset is a pointer to the actual data:
31616 dataOffset = tagSize > 4 ?
31617 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31618 if (dataOffset + tagSize > dataView.byteLength) {
31619 Roo.log('Invalid Exif data: Invalid data offset.');
31622 if (length === 1) {
31623 return tagType.getValue(dataView, dataOffset, littleEndian);
31626 for (i = 0; i < length; i += 1) {
31627 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31630 if (tagType.ascii) {
31632 // Concatenate the chars:
31633 for (i = 0; i < values.length; i += 1) {
31635 // Ignore the terminating NULL byte(s):
31636 if (c === '\u0000') {
31648 Roo.apply(Roo.bootstrap.UploadCropbox, {
31650 'Orientation': 0x0112
31654 1: 0, //'top-left',
31656 3: 180, //'bottom-right',
31657 // 4: 'bottom-left',
31659 6: 90, //'right-top',
31660 // 7: 'right-bottom',
31661 8: 270 //'left-bottom'
31665 // byte, 8-bit unsigned int:
31667 getValue: function (dataView, dataOffset) {
31668 return dataView.getUint8(dataOffset);
31672 // ascii, 8-bit byte:
31674 getValue: function (dataView, dataOffset) {
31675 return String.fromCharCode(dataView.getUint8(dataOffset));
31680 // short, 16 bit int:
31682 getValue: function (dataView, dataOffset, littleEndian) {
31683 return dataView.getUint16(dataOffset, littleEndian);
31687 // long, 32 bit int:
31689 getValue: function (dataView, dataOffset, littleEndian) {
31690 return dataView.getUint32(dataOffset, littleEndian);
31694 // rational = two long values, first is numerator, second is denominator:
31696 getValue: function (dataView, dataOffset, littleEndian) {
31697 return dataView.getUint32(dataOffset, littleEndian) /
31698 dataView.getUint32(dataOffset + 4, littleEndian);
31702 // slong, 32 bit signed int:
31704 getValue: function (dataView, dataOffset, littleEndian) {
31705 return dataView.getInt32(dataOffset, littleEndian);
31709 // srational, two slongs, first is numerator, second is denominator:
31711 getValue: function (dataView, dataOffset, littleEndian) {
31712 return dataView.getInt32(dataOffset, littleEndian) /
31713 dataView.getInt32(dataOffset + 4, littleEndian);
31723 cls : 'btn-group roo-upload-cropbox-rotate-left',
31724 action : 'rotate-left',
31728 cls : 'btn btn-default',
31729 html : '<i class="fa fa-undo"></i>'
31735 cls : 'btn-group roo-upload-cropbox-picture',
31736 action : 'picture',
31740 cls : 'btn btn-default',
31741 html : '<i class="fa fa-picture-o"></i>'
31747 cls : 'btn-group roo-upload-cropbox-rotate-right',
31748 action : 'rotate-right',
31752 cls : 'btn btn-default',
31753 html : '<i class="fa fa-repeat"></i>'
31761 cls : 'btn-group roo-upload-cropbox-rotate-left',
31762 action : 'rotate-left',
31766 cls : 'btn btn-default',
31767 html : '<i class="fa fa-undo"></i>'
31773 cls : 'btn-group roo-upload-cropbox-download',
31774 action : 'download',
31778 cls : 'btn btn-default',
31779 html : '<i class="fa fa-download"></i>'
31785 cls : 'btn-group roo-upload-cropbox-crop',
31790 cls : 'btn btn-default',
31791 html : '<i class="fa fa-crop"></i>'
31797 cls : 'btn-group roo-upload-cropbox-trash',
31802 cls : 'btn btn-default',
31803 html : '<i class="fa fa-trash"></i>'
31809 cls : 'btn-group roo-upload-cropbox-rotate-right',
31810 action : 'rotate-right',
31814 cls : 'btn btn-default',
31815 html : '<i class="fa fa-repeat"></i>'
31823 cls : 'btn-group roo-upload-cropbox-rotate-left',
31824 action : 'rotate-left',
31828 cls : 'btn btn-default',
31829 html : '<i class="fa fa-undo"></i>'
31835 cls : 'btn-group roo-upload-cropbox-rotate-right',
31836 action : 'rotate-right',
31840 cls : 'btn btn-default',
31841 html : '<i class="fa fa-repeat"></i>'
31854 * @class Roo.bootstrap.DocumentManager
31855 * @extends Roo.bootstrap.Component
31856 * Bootstrap DocumentManager class
31857 * @cfg {String} paramName default 'imageUpload'
31858 * @cfg {String} toolTipName default 'filename'
31859 * @cfg {String} method default POST
31860 * @cfg {String} url action url
31861 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31862 * @cfg {Boolean} multiple multiple upload default true
31863 * @cfg {Number} thumbSize default 300
31864 * @cfg {String} fieldLabel
31865 * @cfg {Number} labelWidth default 4
31866 * @cfg {String} labelAlign (left|top) default left
31867 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31868 * @cfg {Number} labellg set the width of label (1-12)
31869 * @cfg {Number} labelmd set the width of label (1-12)
31870 * @cfg {Number} labelsm set the width of label (1-12)
31871 * @cfg {Number} labelxs set the width of label (1-12)
31874 * Create a new DocumentManager
31875 * @param {Object} config The config object
31878 Roo.bootstrap.DocumentManager = function(config){
31879 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31882 this.delegates = [];
31887 * Fire when initial the DocumentManager
31888 * @param {Roo.bootstrap.DocumentManager} this
31893 * inspect selected file
31894 * @param {Roo.bootstrap.DocumentManager} this
31895 * @param {File} file
31900 * Fire when xhr load exception
31901 * @param {Roo.bootstrap.DocumentManager} this
31902 * @param {XMLHttpRequest} xhr
31904 "exception" : true,
31906 * @event afterupload
31907 * Fire when xhr load exception
31908 * @param {Roo.bootstrap.DocumentManager} this
31909 * @param {XMLHttpRequest} xhr
31911 "afterupload" : true,
31914 * prepare the form data
31915 * @param {Roo.bootstrap.DocumentManager} this
31916 * @param {Object} formData
31921 * Fire when remove the file
31922 * @param {Roo.bootstrap.DocumentManager} this
31923 * @param {Object} file
31928 * Fire after refresh the file
31929 * @param {Roo.bootstrap.DocumentManager} this
31934 * Fire after click the image
31935 * @param {Roo.bootstrap.DocumentManager} this
31936 * @param {Object} file
31941 * Fire when upload a image and editable set to true
31942 * @param {Roo.bootstrap.DocumentManager} this
31943 * @param {Object} file
31947 * @event beforeselectfile
31948 * Fire before select file
31949 * @param {Roo.bootstrap.DocumentManager} this
31951 "beforeselectfile" : true,
31954 * Fire before process file
31955 * @param {Roo.bootstrap.DocumentManager} this
31956 * @param {Object} file
31960 * @event previewrendered
31961 * Fire when preview rendered
31962 * @param {Roo.bootstrap.DocumentManager} this
31963 * @param {Object} file
31965 "previewrendered" : true,
31968 "previewResize" : true
31973 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
31982 paramName : 'imageUpload',
31983 toolTipName : 'filename',
31986 labelAlign : 'left',
31996 getAutoCreate : function()
31998 var managerWidget = {
32000 cls : 'roo-document-manager',
32004 cls : 'roo-document-manager-selector',
32009 cls : 'roo-document-manager-uploader',
32013 cls : 'roo-document-manager-upload-btn',
32014 html : '<i class="fa fa-plus"></i>'
32025 cls : 'column col-md-12',
32030 if(this.fieldLabel.length){
32035 cls : 'column col-md-12',
32036 html : this.fieldLabel
32040 cls : 'column col-md-12',
32045 if(this.labelAlign == 'left'){
32050 html : this.fieldLabel
32059 if(this.labelWidth > 12){
32060 content[0].style = "width: " + this.labelWidth + 'px';
32063 if(this.labelWidth < 13 && this.labelmd == 0){
32064 this.labelmd = this.labelWidth;
32067 if(this.labellg > 0){
32068 content[0].cls += ' col-lg-' + this.labellg;
32069 content[1].cls += ' col-lg-' + (12 - this.labellg);
32072 if(this.labelmd > 0){
32073 content[0].cls += ' col-md-' + this.labelmd;
32074 content[1].cls += ' col-md-' + (12 - this.labelmd);
32077 if(this.labelsm > 0){
32078 content[0].cls += ' col-sm-' + this.labelsm;
32079 content[1].cls += ' col-sm-' + (12 - this.labelsm);
32082 if(this.labelxs > 0){
32083 content[0].cls += ' col-xs-' + this.labelxs;
32084 content[1].cls += ' col-xs-' + (12 - this.labelxs);
32092 cls : 'row clearfix',
32100 initEvents : function()
32102 this.managerEl = this.el.select('.roo-document-manager', true).first();
32103 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32105 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32106 this.selectorEl.hide();
32109 this.selectorEl.attr('multiple', 'multiple');
32112 this.selectorEl.on('change', this.onFileSelected, this);
32114 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32115 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32117 this.uploader.on('click', this.onUploaderClick, this);
32119 this.renderProgressDialog();
32123 window.addEventListener("resize", function() { _this.refresh(); } );
32125 this.fireEvent('initial', this);
32128 renderProgressDialog : function()
32132 this.progressDialog = new Roo.bootstrap.Modal({
32133 cls : 'roo-document-manager-progress-dialog',
32134 allow_close : false,
32145 btnclick : function() {
32146 _this.uploadCancel();
32152 this.progressDialog.render(Roo.get(document.body));
32154 this.progress = new Roo.bootstrap.Progress({
32155 cls : 'roo-document-manager-progress',
32160 this.progress.render(this.progressDialog.getChildContainer());
32162 this.progressBar = new Roo.bootstrap.ProgressBar({
32163 cls : 'roo-document-manager-progress-bar',
32166 aria_valuemax : 12,
32170 this.progressBar.render(this.progress.getChildContainer());
32173 onUploaderClick : function(e)
32175 e.preventDefault();
32177 if(this.fireEvent('beforeselectfile', this) != false){
32178 this.selectorEl.dom.click();
32183 onFileSelected : function(e)
32185 e.preventDefault();
32187 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32191 Roo.each(this.selectorEl.dom.files, function(file){
32192 if(this.fireEvent('inspect', this, file) != false){
32193 this.files.push(file);
32203 this.selectorEl.dom.value = '';
32205 if(!this.files || !this.files.length){
32209 if(this.boxes > 0 && this.files.length > this.boxes){
32210 this.files = this.files.slice(0, this.boxes);
32213 this.uploader.show();
32215 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32216 this.uploader.hide();
32225 Roo.each(this.files, function(file){
32227 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32228 var f = this.renderPreview(file);
32233 if(file.type.indexOf('image') != -1){
32234 this.delegates.push(
32236 _this.process(file);
32237 }).createDelegate(this)
32245 _this.process(file);
32246 }).createDelegate(this)
32251 this.files = files;
32253 this.delegates = this.delegates.concat(docs);
32255 if(!this.delegates.length){
32260 this.progressBar.aria_valuemax = this.delegates.length;
32267 arrange : function()
32269 if(!this.delegates.length){
32270 this.progressDialog.hide();
32275 var delegate = this.delegates.shift();
32277 this.progressDialog.show();
32279 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32281 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32286 refresh : function()
32288 this.uploader.show();
32290 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32291 this.uploader.hide();
32294 Roo.isTouch ? this.closable(false) : this.closable(true);
32296 this.fireEvent('refresh', this);
32299 onRemove : function(e, el, o)
32301 e.preventDefault();
32303 this.fireEvent('remove', this, o);
32307 remove : function(o)
32311 Roo.each(this.files, function(file){
32312 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32321 this.files = files;
32328 Roo.each(this.files, function(file){
32333 file.target.remove();
32342 onClick : function(e, el, o)
32344 e.preventDefault();
32346 this.fireEvent('click', this, o);
32350 closable : function(closable)
32352 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32354 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32366 xhrOnLoad : function(xhr)
32368 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32372 if (xhr.readyState !== 4) {
32374 this.fireEvent('exception', this, xhr);
32378 var response = Roo.decode(xhr.responseText);
32380 if(!response.success){
32382 this.fireEvent('exception', this, xhr);
32386 var file = this.renderPreview(response.data);
32388 this.files.push(file);
32392 this.fireEvent('afterupload', this, xhr);
32396 xhrOnError : function(xhr)
32398 Roo.log('xhr on error');
32400 var response = Roo.decode(xhr.responseText);
32407 process : function(file)
32409 if(this.fireEvent('process', this, file) !== false){
32410 if(this.editable && file.type.indexOf('image') != -1){
32411 this.fireEvent('edit', this, file);
32415 this.uploadStart(file, false);
32422 uploadStart : function(file, crop)
32424 this.xhr = new XMLHttpRequest();
32426 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32431 file.xhr = this.xhr;
32433 this.managerEl.createChild({
32435 cls : 'roo-document-manager-loading',
32439 tooltip : file.name,
32440 cls : 'roo-document-manager-thumb',
32441 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32447 this.xhr.open(this.method, this.url, true);
32450 "Accept": "application/json",
32451 "Cache-Control": "no-cache",
32452 "X-Requested-With": "XMLHttpRequest"
32455 for (var headerName in headers) {
32456 var headerValue = headers[headerName];
32458 this.xhr.setRequestHeader(headerName, headerValue);
32464 this.xhr.onload = function()
32466 _this.xhrOnLoad(_this.xhr);
32469 this.xhr.onerror = function()
32471 _this.xhrOnError(_this.xhr);
32474 var formData = new FormData();
32476 formData.append('returnHTML', 'NO');
32479 formData.append('crop', crop);
32482 formData.append(this.paramName, file, file.name);
32489 if(this.fireEvent('prepare', this, formData, options) != false){
32491 if(options.manually){
32495 this.xhr.send(formData);
32499 this.uploadCancel();
32502 uploadCancel : function()
32508 this.delegates = [];
32510 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32517 renderPreview : function(file)
32519 if(typeof(file.target) != 'undefined' && file.target){
32523 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32525 var previewEl = this.managerEl.createChild({
32527 cls : 'roo-document-manager-preview',
32531 tooltip : file[this.toolTipName],
32532 cls : 'roo-document-manager-thumb',
32533 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32538 html : '<i class="fa fa-times-circle"></i>'
32543 var close = previewEl.select('button.close', true).first();
32545 close.on('click', this.onRemove, this, file);
32547 file.target = previewEl;
32549 var image = previewEl.select('img', true).first();
32553 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32555 image.on('click', this.onClick, this, file);
32557 this.fireEvent('previewrendered', this, file);
32563 onPreviewLoad : function(file, image)
32565 if(typeof(file.target) == 'undefined' || !file.target){
32569 var width = image.dom.naturalWidth || image.dom.width;
32570 var height = image.dom.naturalHeight || image.dom.height;
32572 if(!this.previewResize) {
32576 if(width > height){
32577 file.target.addClass('wide');
32581 file.target.addClass('tall');
32586 uploadFromSource : function(file, crop)
32588 this.xhr = new XMLHttpRequest();
32590 this.managerEl.createChild({
32592 cls : 'roo-document-manager-loading',
32596 tooltip : file.name,
32597 cls : 'roo-document-manager-thumb',
32598 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32604 this.xhr.open(this.method, this.url, true);
32607 "Accept": "application/json",
32608 "Cache-Control": "no-cache",
32609 "X-Requested-With": "XMLHttpRequest"
32612 for (var headerName in headers) {
32613 var headerValue = headers[headerName];
32615 this.xhr.setRequestHeader(headerName, headerValue);
32621 this.xhr.onload = function()
32623 _this.xhrOnLoad(_this.xhr);
32626 this.xhr.onerror = function()
32628 _this.xhrOnError(_this.xhr);
32631 var formData = new FormData();
32633 formData.append('returnHTML', 'NO');
32635 formData.append('crop', crop);
32637 if(typeof(file.filename) != 'undefined'){
32638 formData.append('filename', file.filename);
32641 if(typeof(file.mimetype) != 'undefined'){
32642 formData.append('mimetype', file.mimetype);
32647 if(this.fireEvent('prepare', this, formData) != false){
32648 this.xhr.send(formData);
32658 * @class Roo.bootstrap.DocumentViewer
32659 * @extends Roo.bootstrap.Component
32660 * Bootstrap DocumentViewer class
32661 * @cfg {Boolean} showDownload (true|false) show download button (default true)
32662 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32665 * Create a new DocumentViewer
32666 * @param {Object} config The config object
32669 Roo.bootstrap.DocumentViewer = function(config){
32670 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32675 * Fire after initEvent
32676 * @param {Roo.bootstrap.DocumentViewer} this
32682 * @param {Roo.bootstrap.DocumentViewer} this
32687 * Fire after download button
32688 * @param {Roo.bootstrap.DocumentViewer} this
32693 * Fire after trash button
32694 * @param {Roo.bootstrap.DocumentViewer} this
32701 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
32703 showDownload : true,
32707 getAutoCreate : function()
32711 cls : 'roo-document-viewer',
32715 cls : 'roo-document-viewer-body',
32719 cls : 'roo-document-viewer-thumb',
32723 cls : 'roo-document-viewer-image'
32731 cls : 'roo-document-viewer-footer',
32734 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32738 cls : 'btn-group roo-document-viewer-download',
32742 cls : 'btn btn-default',
32743 html : '<i class="fa fa-download"></i>'
32749 cls : 'btn-group roo-document-viewer-trash',
32753 cls : 'btn btn-default',
32754 html : '<i class="fa fa-trash"></i>'
32767 initEvents : function()
32769 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32770 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32772 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32773 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32775 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32776 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32778 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32779 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32781 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32782 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32784 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32785 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32787 this.bodyEl.on('click', this.onClick, this);
32788 this.downloadBtn.on('click', this.onDownload, this);
32789 this.trashBtn.on('click', this.onTrash, this);
32791 this.downloadBtn.hide();
32792 this.trashBtn.hide();
32794 if(this.showDownload){
32795 this.downloadBtn.show();
32798 if(this.showTrash){
32799 this.trashBtn.show();
32802 if(!this.showDownload && !this.showTrash) {
32803 this.footerEl.hide();
32808 initial : function()
32810 this.fireEvent('initial', this);
32814 onClick : function(e)
32816 e.preventDefault();
32818 this.fireEvent('click', this);
32821 onDownload : function(e)
32823 e.preventDefault();
32825 this.fireEvent('download', this);
32828 onTrash : function(e)
32830 e.preventDefault();
32832 this.fireEvent('trash', this);
32844 * @class Roo.bootstrap.NavProgressBar
32845 * @extends Roo.bootstrap.Component
32846 * Bootstrap NavProgressBar class
32849 * Create a new nav progress bar
32850 * @param {Object} config The config object
32853 Roo.bootstrap.NavProgressBar = function(config){
32854 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32856 this.bullets = this.bullets || [];
32858 // Roo.bootstrap.NavProgressBar.register(this);
32862 * Fires when the active item changes
32863 * @param {Roo.bootstrap.NavProgressBar} this
32864 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32865 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
32872 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
32877 getAutoCreate : function()
32879 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32883 cls : 'roo-navigation-bar-group',
32887 cls : 'roo-navigation-top-bar'
32891 cls : 'roo-navigation-bullets-bar',
32895 cls : 'roo-navigation-bar'
32902 cls : 'roo-navigation-bottom-bar'
32912 initEvents: function()
32917 onRender : function(ct, position)
32919 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32921 if(this.bullets.length){
32922 Roo.each(this.bullets, function(b){
32931 addItem : function(cfg)
32933 var item = new Roo.bootstrap.NavProgressItem(cfg);
32935 item.parentId = this.id;
32936 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32939 var top = new Roo.bootstrap.Element({
32941 cls : 'roo-navigation-bar-text'
32944 var bottom = new Roo.bootstrap.Element({
32946 cls : 'roo-navigation-bar-text'
32949 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32950 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32952 var topText = new Roo.bootstrap.Element({
32954 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32957 var bottomText = new Roo.bootstrap.Element({
32959 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32962 topText.onRender(top.el, null);
32963 bottomText.onRender(bottom.el, null);
32966 item.bottomEl = bottom;
32969 this.barItems.push(item);
32974 getActive : function()
32976 var active = false;
32978 Roo.each(this.barItems, function(v){
32980 if (!v.isActive()) {
32992 setActiveItem : function(item)
32996 Roo.each(this.barItems, function(v){
32997 if (v.rid == item.rid) {
33001 if (v.isActive()) {
33002 v.setActive(false);
33007 item.setActive(true);
33009 this.fireEvent('changed', this, item, prev);
33012 getBarItem: function(rid)
33016 Roo.each(this.barItems, function(e) {
33017 if (e.rid != rid) {
33028 indexOfItem : function(item)
33032 Roo.each(this.barItems, function(v, i){
33034 if (v.rid != item.rid) {
33045 setActiveNext : function()
33047 var i = this.indexOfItem(this.getActive());
33049 if (i > this.barItems.length) {
33053 this.setActiveItem(this.barItems[i+1]);
33056 setActivePrev : function()
33058 var i = this.indexOfItem(this.getActive());
33064 this.setActiveItem(this.barItems[i-1]);
33067 format : function()
33069 if(!this.barItems.length){
33073 var width = 100 / this.barItems.length;
33075 Roo.each(this.barItems, function(i){
33076 i.el.setStyle('width', width + '%');
33077 i.topEl.el.setStyle('width', width + '%');
33078 i.bottomEl.el.setStyle('width', width + '%');
33087 * Nav Progress Item
33092 * @class Roo.bootstrap.NavProgressItem
33093 * @extends Roo.bootstrap.Component
33094 * Bootstrap NavProgressItem class
33095 * @cfg {String} rid the reference id
33096 * @cfg {Boolean} active (true|false) Is item active default false
33097 * @cfg {Boolean} disabled (true|false) Is item active default false
33098 * @cfg {String} html
33099 * @cfg {String} position (top|bottom) text position default bottom
33100 * @cfg {String} icon show icon instead of number
33103 * Create a new NavProgressItem
33104 * @param {Object} config The config object
33106 Roo.bootstrap.NavProgressItem = function(config){
33107 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33112 * The raw click event for the entire grid.
33113 * @param {Roo.bootstrap.NavProgressItem} this
33114 * @param {Roo.EventObject} e
33121 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
33127 position : 'bottom',
33130 getAutoCreate : function()
33132 var iconCls = 'roo-navigation-bar-item-icon';
33134 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33138 cls: 'roo-navigation-bar-item',
33148 cfg.cls += ' active';
33151 cfg.cls += ' disabled';
33157 disable : function()
33159 this.setDisabled(true);
33162 enable : function()
33164 this.setDisabled(false);
33167 initEvents: function()
33169 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33171 this.iconEl.on('click', this.onClick, this);
33174 onClick : function(e)
33176 e.preventDefault();
33182 if(this.fireEvent('click', this, e) === false){
33186 this.parent().setActiveItem(this);
33189 isActive: function ()
33191 return this.active;
33194 setActive : function(state)
33196 if(this.active == state){
33200 this.active = state;
33203 this.el.addClass('active');
33207 this.el.removeClass('active');
33212 setDisabled : function(state)
33214 if(this.disabled == state){
33218 this.disabled = state;
33221 this.el.addClass('disabled');
33225 this.el.removeClass('disabled');
33228 tooltipEl : function()
33230 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33243 * @class Roo.bootstrap.FieldLabel
33244 * @extends Roo.bootstrap.Component
33245 * Bootstrap FieldLabel class
33246 * @cfg {String} html contents of the element
33247 * @cfg {String} tag tag of the element default label
33248 * @cfg {String} cls class of the element
33249 * @cfg {String} target label target
33250 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33251 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33252 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33253 * @cfg {String} iconTooltip default "This field is required"
33254 * @cfg {String} indicatorpos (left|right) default left
33257 * Create a new FieldLabel
33258 * @param {Object} config The config object
33261 Roo.bootstrap.FieldLabel = function(config){
33262 Roo.bootstrap.Element.superclass.constructor.call(this, config);
33267 * Fires after the field has been marked as invalid.
33268 * @param {Roo.form.FieldLabel} this
33269 * @param {String} msg The validation message
33274 * Fires after the field has been validated with no errors.
33275 * @param {Roo.form.FieldLabel} this
33281 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
33288 invalidClass : 'has-warning',
33289 validClass : 'has-success',
33290 iconTooltip : 'This field is required',
33291 indicatorpos : 'left',
33293 getAutoCreate : function(){
33296 if (!this.allowBlank) {
33302 cls : 'roo-bootstrap-field-label ' + this.cls,
33307 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33308 tooltip : this.iconTooltip
33317 if(this.indicatorpos == 'right'){
33320 cls : 'roo-bootstrap-field-label ' + this.cls,
33329 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33330 tooltip : this.iconTooltip
33339 initEvents: function()
33341 Roo.bootstrap.Element.superclass.initEvents.call(this);
33343 this.indicator = this.indicatorEl();
33345 if(this.indicator){
33346 this.indicator.removeClass('visible');
33347 this.indicator.addClass('invisible');
33350 Roo.bootstrap.FieldLabel.register(this);
33353 indicatorEl : function()
33355 var indicator = this.el.select('i.roo-required-indicator',true).first();
33366 * Mark this field as valid
33368 markValid : function()
33370 if(this.indicator){
33371 this.indicator.removeClass('visible');
33372 this.indicator.addClass('invisible');
33374 if (Roo.bootstrap.version == 3) {
33375 this.el.removeClass(this.invalidClass);
33376 this.el.addClass(this.validClass);
33378 this.el.removeClass('is-invalid');
33379 this.el.addClass('is-valid');
33383 this.fireEvent('valid', this);
33387 * Mark this field as invalid
33388 * @param {String} msg The validation message
33390 markInvalid : function(msg)
33392 if(this.indicator){
33393 this.indicator.removeClass('invisible');
33394 this.indicator.addClass('visible');
33396 if (Roo.bootstrap.version == 3) {
33397 this.el.removeClass(this.validClass);
33398 this.el.addClass(this.invalidClass);
33400 this.el.removeClass('is-valid');
33401 this.el.addClass('is-invalid');
33405 this.fireEvent('invalid', this, msg);
33411 Roo.apply(Roo.bootstrap.FieldLabel, {
33416 * register a FieldLabel Group
33417 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33419 register : function(label)
33421 if(this.groups.hasOwnProperty(label.target)){
33425 this.groups[label.target] = label;
33429 * fetch a FieldLabel Group based on the target
33430 * @param {string} target
33431 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33433 get: function(target) {
33434 if (typeof(this.groups[target]) == 'undefined') {
33438 return this.groups[target] ;
33447 * page DateSplitField.
33453 * @class Roo.bootstrap.DateSplitField
33454 * @extends Roo.bootstrap.Component
33455 * Bootstrap DateSplitField class
33456 * @cfg {string} fieldLabel - the label associated
33457 * @cfg {Number} labelWidth set the width of label (0-12)
33458 * @cfg {String} labelAlign (top|left)
33459 * @cfg {Boolean} dayAllowBlank (true|false) default false
33460 * @cfg {Boolean} monthAllowBlank (true|false) default false
33461 * @cfg {Boolean} yearAllowBlank (true|false) default false
33462 * @cfg {string} dayPlaceholder
33463 * @cfg {string} monthPlaceholder
33464 * @cfg {string} yearPlaceholder
33465 * @cfg {string} dayFormat default 'd'
33466 * @cfg {string} monthFormat default 'm'
33467 * @cfg {string} yearFormat default 'Y'
33468 * @cfg {Number} labellg set the width of label (1-12)
33469 * @cfg {Number} labelmd set the width of label (1-12)
33470 * @cfg {Number} labelsm set the width of label (1-12)
33471 * @cfg {Number} labelxs set the width of label (1-12)
33475 * Create a new DateSplitField
33476 * @param {Object} config The config object
33479 Roo.bootstrap.DateSplitField = function(config){
33480 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33486 * getting the data of years
33487 * @param {Roo.bootstrap.DateSplitField} this
33488 * @param {Object} years
33493 * getting the data of days
33494 * @param {Roo.bootstrap.DateSplitField} this
33495 * @param {Object} days
33500 * Fires after the field has been marked as invalid.
33501 * @param {Roo.form.Field} this
33502 * @param {String} msg The validation message
33507 * Fires after the field has been validated with no errors.
33508 * @param {Roo.form.Field} this
33514 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33517 labelAlign : 'top',
33519 dayAllowBlank : false,
33520 monthAllowBlank : false,
33521 yearAllowBlank : false,
33522 dayPlaceholder : '',
33523 monthPlaceholder : '',
33524 yearPlaceholder : '',
33528 isFormField : true,
33534 getAutoCreate : function()
33538 cls : 'row roo-date-split-field-group',
33543 cls : 'form-hidden-field roo-date-split-field-group-value',
33549 var labelCls = 'col-md-12';
33550 var contentCls = 'col-md-4';
33552 if(this.fieldLabel){
33556 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33560 html : this.fieldLabel
33565 if(this.labelAlign == 'left'){
33567 if(this.labelWidth > 12){
33568 label.style = "width: " + this.labelWidth + 'px';
33571 if(this.labelWidth < 13 && this.labelmd == 0){
33572 this.labelmd = this.labelWidth;
33575 if(this.labellg > 0){
33576 labelCls = ' col-lg-' + this.labellg;
33577 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33580 if(this.labelmd > 0){
33581 labelCls = ' col-md-' + this.labelmd;
33582 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33585 if(this.labelsm > 0){
33586 labelCls = ' col-sm-' + this.labelsm;
33587 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33590 if(this.labelxs > 0){
33591 labelCls = ' col-xs-' + this.labelxs;
33592 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33596 label.cls += ' ' + labelCls;
33598 cfg.cn.push(label);
33601 Roo.each(['day', 'month', 'year'], function(t){
33604 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33611 inputEl: function ()
33613 return this.el.select('.roo-date-split-field-group-value', true).first();
33616 onRender : function(ct, position)
33620 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33622 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33624 this.dayField = new Roo.bootstrap.ComboBox({
33625 allowBlank : this.dayAllowBlank,
33626 alwaysQuery : true,
33627 displayField : 'value',
33630 forceSelection : true,
33632 placeholder : this.dayPlaceholder,
33633 selectOnFocus : true,
33634 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33635 triggerAction : 'all',
33637 valueField : 'value',
33638 store : new Roo.data.SimpleStore({
33639 data : (function() {
33641 _this.fireEvent('days', _this, days);
33644 fields : [ 'value' ]
33647 select : function (_self, record, index)
33649 _this.setValue(_this.getValue());
33654 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33656 this.monthField = new Roo.bootstrap.MonthField({
33657 after : '<i class=\"fa fa-calendar\"></i>',
33658 allowBlank : this.monthAllowBlank,
33659 placeholder : this.monthPlaceholder,
33662 render : function (_self)
33664 this.el.select('span.input-group-addon', true).first().on('click', function(e){
33665 e.preventDefault();
33669 select : function (_self, oldvalue, newvalue)
33671 _this.setValue(_this.getValue());
33676 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33678 this.yearField = new Roo.bootstrap.ComboBox({
33679 allowBlank : this.yearAllowBlank,
33680 alwaysQuery : true,
33681 displayField : 'value',
33684 forceSelection : true,
33686 placeholder : this.yearPlaceholder,
33687 selectOnFocus : true,
33688 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33689 triggerAction : 'all',
33691 valueField : 'value',
33692 store : new Roo.data.SimpleStore({
33693 data : (function() {
33695 _this.fireEvent('years', _this, years);
33698 fields : [ 'value' ]
33701 select : function (_self, record, index)
33703 _this.setValue(_this.getValue());
33708 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33711 setValue : function(v, format)
33713 this.inputEl.dom.value = v;
33715 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33717 var d = Date.parseDate(v, f);
33724 this.setDay(d.format(this.dayFormat));
33725 this.setMonth(d.format(this.monthFormat));
33726 this.setYear(d.format(this.yearFormat));
33733 setDay : function(v)
33735 this.dayField.setValue(v);
33736 this.inputEl.dom.value = this.getValue();
33741 setMonth : function(v)
33743 this.monthField.setValue(v, true);
33744 this.inputEl.dom.value = this.getValue();
33749 setYear : function(v)
33751 this.yearField.setValue(v);
33752 this.inputEl.dom.value = this.getValue();
33757 getDay : function()
33759 return this.dayField.getValue();
33762 getMonth : function()
33764 return this.monthField.getValue();
33767 getYear : function()
33769 return this.yearField.getValue();
33772 getValue : function()
33774 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33776 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33786 this.inputEl.dom.value = '';
33791 validate : function()
33793 var d = this.dayField.validate();
33794 var m = this.monthField.validate();
33795 var y = this.yearField.validate();
33800 (!this.dayAllowBlank && !d) ||
33801 (!this.monthAllowBlank && !m) ||
33802 (!this.yearAllowBlank && !y)
33807 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33816 this.markInvalid();
33821 markValid : function()
33824 var label = this.el.select('label', true).first();
33825 var icon = this.el.select('i.fa-star', true).first();
33831 this.fireEvent('valid', this);
33835 * Mark this field as invalid
33836 * @param {String} msg The validation message
33838 markInvalid : function(msg)
33841 var label = this.el.select('label', true).first();
33842 var icon = this.el.select('i.fa-star', true).first();
33844 if(label && !icon){
33845 this.el.select('.roo-date-split-field-label', true).createChild({
33847 cls : 'text-danger fa fa-lg fa-star',
33848 tooltip : 'This field is required',
33849 style : 'margin-right:5px;'
33853 this.fireEvent('invalid', this, msg);
33856 clearInvalid : function()
33858 var label = this.el.select('label', true).first();
33859 var icon = this.el.select('i.fa-star', true).first();
33865 this.fireEvent('valid', this);
33868 getName: function()
33878 * http://masonry.desandro.com
33880 * The idea is to render all the bricks based on vertical width...
33882 * The original code extends 'outlayer' - we might need to use that....
33888 * @class Roo.bootstrap.LayoutMasonry
33889 * @extends Roo.bootstrap.Component
33890 * Bootstrap Layout Masonry class
33893 * Create a new Element
33894 * @param {Object} config The config object
33897 Roo.bootstrap.LayoutMasonry = function(config){
33899 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33903 Roo.bootstrap.LayoutMasonry.register(this);
33909 * Fire after layout the items
33910 * @param {Roo.bootstrap.LayoutMasonry} this
33911 * @param {Roo.EventObject} e
33918 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
33921 * @cfg {Boolean} isLayoutInstant = no animation?
33923 isLayoutInstant : false, // needed?
33926 * @cfg {Number} boxWidth width of the columns
33931 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
33936 * @cfg {Number} padWidth padding below box..
33941 * @cfg {Number} gutter gutter width..
33946 * @cfg {Number} maxCols maximum number of columns
33952 * @cfg {Boolean} isAutoInitial defalut true
33954 isAutoInitial : true,
33959 * @cfg {Boolean} isHorizontal defalut false
33961 isHorizontal : false,
33963 currentSize : null,
33969 bricks: null, //CompositeElement
33973 _isLayoutInited : false,
33975 // isAlternative : false, // only use for vertical layout...
33978 * @cfg {Number} alternativePadWidth padding below box..
33980 alternativePadWidth : 50,
33982 selectedBrick : [],
33984 getAutoCreate : function(){
33986 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33990 cls: 'blog-masonary-wrapper ' + this.cls,
33992 cls : 'mas-boxes masonary'
33999 getChildContainer: function( )
34001 if (this.boxesEl) {
34002 return this.boxesEl;
34005 this.boxesEl = this.el.select('.mas-boxes').first();
34007 return this.boxesEl;
34011 initEvents : function()
34015 if(this.isAutoInitial){
34016 Roo.log('hook children rendered');
34017 this.on('childrenrendered', function() {
34018 Roo.log('children rendered');
34024 initial : function()
34026 this.selectedBrick = [];
34028 this.currentSize = this.el.getBox(true);
34030 Roo.EventManager.onWindowResize(this.resize, this);
34032 if(!this.isAutoInitial){
34040 //this.layout.defer(500,this);
34044 resize : function()
34046 var cs = this.el.getBox(true);
34049 this.currentSize.width == cs.width &&
34050 this.currentSize.x == cs.x &&
34051 this.currentSize.height == cs.height &&
34052 this.currentSize.y == cs.y
34054 Roo.log("no change in with or X or Y");
34058 this.currentSize = cs;
34064 layout : function()
34066 this._resetLayout();
34068 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34070 this.layoutItems( isInstant );
34072 this._isLayoutInited = true;
34074 this.fireEvent('layout', this);
34078 _resetLayout : function()
34080 if(this.isHorizontal){
34081 this.horizontalMeasureColumns();
34085 this.verticalMeasureColumns();
34089 verticalMeasureColumns : function()
34091 this.getContainerWidth();
34093 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34094 // this.colWidth = Math.floor(this.containerWidth * 0.8);
34098 var boxWidth = this.boxWidth + this.padWidth;
34100 if(this.containerWidth < this.boxWidth){
34101 boxWidth = this.containerWidth
34104 var containerWidth = this.containerWidth;
34106 var cols = Math.floor(containerWidth / boxWidth);
34108 this.cols = Math.max( cols, 1 );
34110 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34112 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34114 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34116 this.colWidth = boxWidth + avail - this.padWidth;
34118 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34119 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
34122 horizontalMeasureColumns : function()
34124 this.getContainerWidth();
34126 var boxWidth = this.boxWidth;
34128 if(this.containerWidth < boxWidth){
34129 boxWidth = this.containerWidth;
34132 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34134 this.el.setHeight(boxWidth);
34138 getContainerWidth : function()
34140 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34143 layoutItems : function( isInstant )
34145 Roo.log(this.bricks);
34147 var items = Roo.apply([], this.bricks);
34149 if(this.isHorizontal){
34150 this._horizontalLayoutItems( items , isInstant );
34154 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34155 // this._verticalAlternativeLayoutItems( items , isInstant );
34159 this._verticalLayoutItems( items , isInstant );
34163 _verticalLayoutItems : function ( items , isInstant)
34165 if ( !items || !items.length ) {
34170 ['xs', 'xs', 'xs', 'tall'],
34171 ['xs', 'xs', 'tall'],
34172 ['xs', 'xs', 'sm'],
34173 ['xs', 'xs', 'xs'],
34179 ['sm', 'xs', 'xs'],
34183 ['tall', 'xs', 'xs', 'xs'],
34184 ['tall', 'xs', 'xs'],
34196 Roo.each(items, function(item, k){
34198 switch (item.size) {
34199 // these layouts take up a full box,
34210 boxes.push([item]);
34233 var filterPattern = function(box, length)
34241 var pattern = box.slice(0, length);
34245 Roo.each(pattern, function(i){
34246 format.push(i.size);
34249 Roo.each(standard, function(s){
34251 if(String(s) != String(format)){
34260 if(!match && length == 1){
34265 filterPattern(box, length - 1);
34269 queue.push(pattern);
34271 box = box.slice(length, box.length);
34273 filterPattern(box, 4);
34279 Roo.each(boxes, function(box, k){
34285 if(box.length == 1){
34290 filterPattern(box, 4);
34294 this._processVerticalLayoutQueue( queue, isInstant );
34298 // _verticalAlternativeLayoutItems : function( items , isInstant )
34300 // if ( !items || !items.length ) {
34304 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
34308 _horizontalLayoutItems : function ( items , isInstant)
34310 if ( !items || !items.length || items.length < 3) {
34316 var eItems = items.slice(0, 3);
34318 items = items.slice(3, items.length);
34321 ['xs', 'xs', 'xs', 'wide'],
34322 ['xs', 'xs', 'wide'],
34323 ['xs', 'xs', 'sm'],
34324 ['xs', 'xs', 'xs'],
34330 ['sm', 'xs', 'xs'],
34334 ['wide', 'xs', 'xs', 'xs'],
34335 ['wide', 'xs', 'xs'],
34348 Roo.each(items, function(item, k){
34350 switch (item.size) {
34361 boxes.push([item]);
34385 var filterPattern = function(box, length)
34393 var pattern = box.slice(0, length);
34397 Roo.each(pattern, function(i){
34398 format.push(i.size);
34401 Roo.each(standard, function(s){
34403 if(String(s) != String(format)){
34412 if(!match && length == 1){
34417 filterPattern(box, length - 1);
34421 queue.push(pattern);
34423 box = box.slice(length, box.length);
34425 filterPattern(box, 4);
34431 Roo.each(boxes, function(box, k){
34437 if(box.length == 1){
34442 filterPattern(box, 4);
34449 var pos = this.el.getBox(true);
34453 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34455 var hit_end = false;
34457 Roo.each(queue, function(box){
34461 Roo.each(box, function(b){
34463 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34473 Roo.each(box, function(b){
34475 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34478 mx = Math.max(mx, b.x);
34482 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34486 Roo.each(box, function(b){
34488 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34502 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34505 /** Sets position of item in DOM
34506 * @param {Element} item
34507 * @param {Number} x - horizontal position
34508 * @param {Number} y - vertical position
34509 * @param {Boolean} isInstant - disables transitions
34511 _processVerticalLayoutQueue : function( queue, isInstant )
34513 var pos = this.el.getBox(true);
34518 for (var i = 0; i < this.cols; i++){
34522 Roo.each(queue, function(box, k){
34524 var col = k % this.cols;
34526 Roo.each(box, function(b,kk){
34528 b.el.position('absolute');
34530 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34531 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34533 if(b.size == 'md-left' || b.size == 'md-right'){
34534 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34535 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34538 b.el.setWidth(width);
34539 b.el.setHeight(height);
34541 b.el.select('iframe',true).setSize(width,height);
34545 for (var i = 0; i < this.cols; i++){
34547 if(maxY[i] < maxY[col]){
34552 col = Math.min(col, i);
34556 x = pos.x + col * (this.colWidth + this.padWidth);
34560 var positions = [];
34562 switch (box.length){
34564 positions = this.getVerticalOneBoxColPositions(x, y, box);
34567 positions = this.getVerticalTwoBoxColPositions(x, y, box);
34570 positions = this.getVerticalThreeBoxColPositions(x, y, box);
34573 positions = this.getVerticalFourBoxColPositions(x, y, box);
34579 Roo.each(box, function(b,kk){
34581 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34583 var sz = b.el.getSize();
34585 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34593 for (var i = 0; i < this.cols; i++){
34594 mY = Math.max(mY, maxY[i]);
34597 this.el.setHeight(mY - pos.y);
34601 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34603 // var pos = this.el.getBox(true);
34606 // var maxX = pos.right;
34608 // var maxHeight = 0;
34610 // Roo.each(items, function(item, k){
34614 // item.el.position('absolute');
34616 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34618 // item.el.setWidth(width);
34620 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34622 // item.el.setHeight(height);
34625 // item.el.setXY([x, y], isInstant ? false : true);
34627 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34630 // y = y + height + this.alternativePadWidth;
34632 // maxHeight = maxHeight + height + this.alternativePadWidth;
34636 // this.el.setHeight(maxHeight);
34640 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34642 var pos = this.el.getBox(true);
34647 var maxX = pos.right;
34649 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34651 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34653 Roo.each(queue, function(box, k){
34655 Roo.each(box, function(b, kk){
34657 b.el.position('absolute');
34659 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34660 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34662 if(b.size == 'md-left' || b.size == 'md-right'){
34663 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34664 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34667 b.el.setWidth(width);
34668 b.el.setHeight(height);
34676 var positions = [];
34678 switch (box.length){
34680 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34683 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34686 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34689 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34695 Roo.each(box, function(b,kk){
34697 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34699 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34707 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34709 Roo.each(eItems, function(b,k){
34711 b.size = (k == 0) ? 'sm' : 'xs';
34712 b.x = (k == 0) ? 2 : 1;
34713 b.y = (k == 0) ? 2 : 1;
34715 b.el.position('absolute');
34717 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34719 b.el.setWidth(width);
34721 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34723 b.el.setHeight(height);
34727 var positions = [];
34730 x : maxX - this.unitWidth * 2 - this.gutter,
34735 x : maxX - this.unitWidth,
34736 y : minY + (this.unitWidth + this.gutter) * 2
34740 x : maxX - this.unitWidth * 3 - this.gutter * 2,
34744 Roo.each(eItems, function(b,k){
34746 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34752 getVerticalOneBoxColPositions : function(x, y, box)
34756 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34758 if(box[0].size == 'md-left'){
34762 if(box[0].size == 'md-right'){
34767 x : x + (this.unitWidth + this.gutter) * rand,
34774 getVerticalTwoBoxColPositions : function(x, y, box)
34778 if(box[0].size == 'xs'){
34782 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34786 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34800 x : x + (this.unitWidth + this.gutter) * 2,
34801 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34808 getVerticalThreeBoxColPositions : function(x, y, box)
34812 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34820 x : x + (this.unitWidth + this.gutter) * 1,
34825 x : x + (this.unitWidth + this.gutter) * 2,
34833 if(box[0].size == 'xs' && box[1].size == 'xs'){
34842 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34846 x : x + (this.unitWidth + this.gutter) * 1,
34860 x : x + (this.unitWidth + this.gutter) * 2,
34865 x : x + (this.unitWidth + this.gutter) * 2,
34866 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34873 getVerticalFourBoxColPositions : function(x, y, box)
34877 if(box[0].size == 'xs'){
34886 y : y + (this.unitHeight + this.gutter) * 1
34891 y : y + (this.unitHeight + this.gutter) * 2
34895 x : x + (this.unitWidth + this.gutter) * 1,
34909 x : x + (this.unitWidth + this.gutter) * 2,
34914 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34915 y : y + (this.unitHeight + this.gutter) * 1
34919 x : x + (this.unitWidth + this.gutter) * 2,
34920 y : y + (this.unitWidth + this.gutter) * 2
34927 getHorizontalOneBoxColPositions : function(maxX, minY, box)
34931 if(box[0].size == 'md-left'){
34933 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34940 if(box[0].size == 'md-right'){
34942 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34943 y : minY + (this.unitWidth + this.gutter) * 1
34949 var rand = Math.floor(Math.random() * (4 - box[0].y));
34952 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34953 y : minY + (this.unitWidth + this.gutter) * rand
34960 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34964 if(box[0].size == 'xs'){
34967 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34972 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34973 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34981 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34986 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34987 y : minY + (this.unitWidth + this.gutter) * 2
34994 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34998 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35001 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35006 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35007 y : minY + (this.unitWidth + this.gutter) * 1
35011 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35012 y : minY + (this.unitWidth + this.gutter) * 2
35019 if(box[0].size == 'xs' && box[1].size == 'xs'){
35022 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35027 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35032 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35033 y : minY + (this.unitWidth + this.gutter) * 1
35041 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35046 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35047 y : minY + (this.unitWidth + this.gutter) * 2
35051 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35052 y : minY + (this.unitWidth + this.gutter) * 2
35059 getHorizontalFourBoxColPositions : function(maxX, minY, box)
35063 if(box[0].size == 'xs'){
35066 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35071 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35076 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),
35081 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35082 y : minY + (this.unitWidth + this.gutter) * 1
35090 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35095 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35096 y : minY + (this.unitWidth + this.gutter) * 2
35100 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35101 y : minY + (this.unitWidth + this.gutter) * 2
35105 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),
35106 y : minY + (this.unitWidth + this.gutter) * 2
35114 * remove a Masonry Brick
35115 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35117 removeBrick : function(brick_id)
35123 for (var i = 0; i<this.bricks.length; i++) {
35124 if (this.bricks[i].id == brick_id) {
35125 this.bricks.splice(i,1);
35126 this.el.dom.removeChild(Roo.get(brick_id).dom);
35133 * adds a Masonry Brick
35134 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35136 addBrick : function(cfg)
35138 var cn = new Roo.bootstrap.MasonryBrick(cfg);
35139 //this.register(cn);
35140 cn.parentId = this.id;
35141 cn.render(this.el);
35146 * register a Masonry Brick
35147 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35150 register : function(brick)
35152 this.bricks.push(brick);
35153 brick.masonryId = this.id;
35157 * clear all the Masonry Brick
35159 clearAll : function()
35162 //this.getChildContainer().dom.innerHTML = "";
35163 this.el.dom.innerHTML = '';
35166 getSelected : function()
35168 if (!this.selectedBrick) {
35172 return this.selectedBrick;
35176 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35180 * register a Masonry Layout
35181 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35184 register : function(layout)
35186 this.groups[layout.id] = layout;
35189 * fetch a Masonry Layout based on the masonry layout ID
35190 * @param {string} the masonry layout to add
35191 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35194 get: function(layout_id) {
35195 if (typeof(this.groups[layout_id]) == 'undefined') {
35198 return this.groups[layout_id] ;
35210 * http://masonry.desandro.com
35212 * The idea is to render all the bricks based on vertical width...
35214 * The original code extends 'outlayer' - we might need to use that....
35220 * @class Roo.bootstrap.LayoutMasonryAuto
35221 * @extends Roo.bootstrap.Component
35222 * Bootstrap Layout Masonry class
35225 * Create a new Element
35226 * @param {Object} config The config object
35229 Roo.bootstrap.LayoutMasonryAuto = function(config){
35230 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35233 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
35236 * @cfg {Boolean} isFitWidth - resize the width..
35238 isFitWidth : false, // options..
35240 * @cfg {Boolean} isOriginLeft = left align?
35242 isOriginLeft : true,
35244 * @cfg {Boolean} isOriginTop = top align?
35246 isOriginTop : false,
35248 * @cfg {Boolean} isLayoutInstant = no animation?
35250 isLayoutInstant : false, // needed?
35252 * @cfg {Boolean} isResizingContainer = not sure if this is used..
35254 isResizingContainer : true,
35256 * @cfg {Number} columnWidth width of the columns
35262 * @cfg {Number} maxCols maximum number of columns
35267 * @cfg {Number} padHeight padding below box..
35273 * @cfg {Boolean} isAutoInitial defalut true
35276 isAutoInitial : true,
35282 initialColumnWidth : 0,
35283 currentSize : null,
35285 colYs : null, // array.
35292 bricks: null, //CompositeElement
35293 cols : 0, // array?
35294 // element : null, // wrapped now this.el
35295 _isLayoutInited : null,
35298 getAutoCreate : function(){
35302 cls: 'blog-masonary-wrapper ' + this.cls,
35304 cls : 'mas-boxes masonary'
35311 getChildContainer: function( )
35313 if (this.boxesEl) {
35314 return this.boxesEl;
35317 this.boxesEl = this.el.select('.mas-boxes').first();
35319 return this.boxesEl;
35323 initEvents : function()
35327 if(this.isAutoInitial){
35328 Roo.log('hook children rendered');
35329 this.on('childrenrendered', function() {
35330 Roo.log('children rendered');
35337 initial : function()
35339 this.reloadItems();
35341 this.currentSize = this.el.getBox(true);
35343 /// was window resize... - let's see if this works..
35344 Roo.EventManager.onWindowResize(this.resize, this);
35346 if(!this.isAutoInitial){
35351 this.layout.defer(500,this);
35354 reloadItems: function()
35356 this.bricks = this.el.select('.masonry-brick', true);
35358 this.bricks.each(function(b) {
35359 //Roo.log(b.getSize());
35360 if (!b.attr('originalwidth')) {
35361 b.attr('originalwidth', b.getSize().width);
35366 Roo.log(this.bricks.elements.length);
35369 resize : function()
35372 var cs = this.el.getBox(true);
35374 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35375 Roo.log("no change in with or X");
35378 this.currentSize = cs;
35382 layout : function()
35385 this._resetLayout();
35386 //this._manageStamps();
35388 // don't animate first layout
35389 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35390 this.layoutItems( isInstant );
35392 // flag for initalized
35393 this._isLayoutInited = true;
35396 layoutItems : function( isInstant )
35398 //var items = this._getItemsForLayout( this.items );
35399 // original code supports filtering layout items.. we just ignore it..
35401 this._layoutItems( this.bricks , isInstant );
35403 this._postLayout();
35405 _layoutItems : function ( items , isInstant)
35407 //this.fireEvent( 'layout', this, items );
35410 if ( !items || !items.elements.length ) {
35411 // no items, emit event with empty array
35416 items.each(function(item) {
35417 Roo.log("layout item");
35419 // get x/y object from method
35420 var position = this._getItemLayoutPosition( item );
35422 position.item = item;
35423 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35424 queue.push( position );
35427 this._processLayoutQueue( queue );
35429 /** Sets position of item in DOM
35430 * @param {Element} item
35431 * @param {Number} x - horizontal position
35432 * @param {Number} y - vertical position
35433 * @param {Boolean} isInstant - disables transitions
35435 _processLayoutQueue : function( queue )
35437 for ( var i=0, len = queue.length; i < len; i++ ) {
35438 var obj = queue[i];
35439 obj.item.position('absolute');
35440 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35446 * Any logic you want to do after each layout,
35447 * i.e. size the container
35449 _postLayout : function()
35451 this.resizeContainer();
35454 resizeContainer : function()
35456 if ( !this.isResizingContainer ) {
35459 var size = this._getContainerSize();
35461 this.el.setSize(size.width,size.height);
35462 this.boxesEl.setSize(size.width,size.height);
35468 _resetLayout : function()
35470 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35471 this.colWidth = this.el.getWidth();
35472 //this.gutter = this.el.getWidth();
35474 this.measureColumns();
35480 this.colYs.push( 0 );
35486 measureColumns : function()
35488 this.getContainerWidth();
35489 // if columnWidth is 0, default to outerWidth of first item
35490 if ( !this.columnWidth ) {
35491 var firstItem = this.bricks.first();
35492 Roo.log(firstItem);
35493 this.columnWidth = this.containerWidth;
35494 if (firstItem && firstItem.attr('originalwidth') ) {
35495 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35497 // columnWidth fall back to item of first element
35498 Roo.log("set column width?");
35499 this.initialColumnWidth = this.columnWidth ;
35501 // if first elem has no width, default to size of container
35506 if (this.initialColumnWidth) {
35507 this.columnWidth = this.initialColumnWidth;
35512 // column width is fixed at the top - however if container width get's smaller we should
35515 // this bit calcs how man columns..
35517 var columnWidth = this.columnWidth += this.gutter;
35519 // calculate columns
35520 var containerWidth = this.containerWidth + this.gutter;
35522 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35523 // fix rounding errors, typically with gutters
35524 var excess = columnWidth - containerWidth % columnWidth;
35527 // if overshoot is less than a pixel, round up, otherwise floor it
35528 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35529 cols = Math[ mathMethod ]( cols );
35530 this.cols = Math.max( cols, 1 );
35531 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35533 // padding positioning..
35534 var totalColWidth = this.cols * this.columnWidth;
35535 var padavail = this.containerWidth - totalColWidth;
35536 // so for 2 columns - we need 3 'pads'
35538 var padNeeded = (1+this.cols) * this.padWidth;
35540 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35542 this.columnWidth += padExtra
35543 //this.padWidth = Math.floor(padavail / ( this.cols));
35545 // adjust colum width so that padding is fixed??
35547 // we have 3 columns ... total = width * 3
35548 // we have X left over... that should be used by
35550 //if (this.expandC) {
35558 getContainerWidth : function()
35560 /* // container is parent if fit width
35561 var container = this.isFitWidth ? this.element.parentNode : this.element;
35562 // check that this.size and size are there
35563 // IE8 triggers resize on body size change, so they might not be
35565 var size = getSize( container ); //FIXME
35566 this.containerWidth = size && size.innerWidth; //FIXME
35569 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
35573 _getItemLayoutPosition : function( item ) // what is item?
35575 // we resize the item to our columnWidth..
35577 item.setWidth(this.columnWidth);
35578 item.autoBoxAdjust = false;
35580 var sz = item.getSize();
35582 // how many columns does this brick span
35583 var remainder = this.containerWidth % this.columnWidth;
35585 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35586 // round if off by 1 pixel, otherwise use ceil
35587 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
35588 colSpan = Math.min( colSpan, this.cols );
35590 // normally this should be '1' as we dont' currently allow multi width columns..
35592 var colGroup = this._getColGroup( colSpan );
35593 // get the minimum Y value from the columns
35594 var minimumY = Math.min.apply( Math, colGroup );
35595 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35597 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35599 // position the brick
35601 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35602 y: this.currentSize.y + minimumY + this.padHeight
35606 // apply setHeight to necessary columns
35607 var setHeight = minimumY + sz.height + this.padHeight;
35608 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35610 var setSpan = this.cols + 1 - colGroup.length;
35611 for ( var i = 0; i < setSpan; i++ ) {
35612 this.colYs[ shortColIndex + i ] = setHeight ;
35619 * @param {Number} colSpan - number of columns the element spans
35620 * @returns {Array} colGroup
35622 _getColGroup : function( colSpan )
35624 if ( colSpan < 2 ) {
35625 // if brick spans only one column, use all the column Ys
35630 // how many different places could this brick fit horizontally
35631 var groupCount = this.cols + 1 - colSpan;
35632 // for each group potential horizontal position
35633 for ( var i = 0; i < groupCount; i++ ) {
35634 // make an array of colY values for that one group
35635 var groupColYs = this.colYs.slice( i, i + colSpan );
35636 // and get the max value of the array
35637 colGroup[i] = Math.max.apply( Math, groupColYs );
35642 _manageStamp : function( stamp )
35644 var stampSize = stamp.getSize();
35645 var offset = stamp.getBox();
35646 // get the columns that this stamp affects
35647 var firstX = this.isOriginLeft ? offset.x : offset.right;
35648 var lastX = firstX + stampSize.width;
35649 var firstCol = Math.floor( firstX / this.columnWidth );
35650 firstCol = Math.max( 0, firstCol );
35652 var lastCol = Math.floor( lastX / this.columnWidth );
35653 // lastCol should not go over if multiple of columnWidth #425
35654 lastCol -= lastX % this.columnWidth ? 0 : 1;
35655 lastCol = Math.min( this.cols - 1, lastCol );
35657 // set colYs to bottom of the stamp
35658 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35661 for ( var i = firstCol; i <= lastCol; i++ ) {
35662 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35667 _getContainerSize : function()
35669 this.maxY = Math.max.apply( Math, this.colYs );
35674 if ( this.isFitWidth ) {
35675 size.width = this._getContainerFitWidth();
35681 _getContainerFitWidth : function()
35683 var unusedCols = 0;
35684 // count unused columns
35687 if ( this.colYs[i] !== 0 ) {
35692 // fit container to columns that have been used
35693 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35696 needsResizeLayout : function()
35698 var previousWidth = this.containerWidth;
35699 this.getContainerWidth();
35700 return previousWidth !== this.containerWidth;
35715 * @class Roo.bootstrap.MasonryBrick
35716 * @extends Roo.bootstrap.Component
35717 * Bootstrap MasonryBrick class
35720 * Create a new MasonryBrick
35721 * @param {Object} config The config object
35724 Roo.bootstrap.MasonryBrick = function(config){
35726 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35728 Roo.bootstrap.MasonryBrick.register(this);
35734 * When a MasonryBrick is clcik
35735 * @param {Roo.bootstrap.MasonryBrick} this
35736 * @param {Roo.EventObject} e
35742 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
35745 * @cfg {String} title
35749 * @cfg {String} html
35753 * @cfg {String} bgimage
35757 * @cfg {String} videourl
35761 * @cfg {String} cls
35765 * @cfg {String} href
35769 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35774 * @cfg {String} placetitle (center|bottom)
35779 * @cfg {Boolean} isFitContainer defalut true
35781 isFitContainer : true,
35784 * @cfg {Boolean} preventDefault defalut false
35786 preventDefault : false,
35789 * @cfg {Boolean} inverse defalut false
35791 maskInverse : false,
35793 getAutoCreate : function()
35795 if(!this.isFitContainer){
35796 return this.getSplitAutoCreate();
35799 var cls = 'masonry-brick masonry-brick-full';
35801 if(this.href.length){
35802 cls += ' masonry-brick-link';
35805 if(this.bgimage.length){
35806 cls += ' masonry-brick-image';
35809 if(this.maskInverse){
35810 cls += ' mask-inverse';
35813 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35814 cls += ' enable-mask';
35818 cls += ' masonry-' + this.size + '-brick';
35821 if(this.placetitle.length){
35823 switch (this.placetitle) {
35825 cls += ' masonry-center-title';
35828 cls += ' masonry-bottom-title';
35835 if(!this.html.length && !this.bgimage.length){
35836 cls += ' masonry-center-title';
35839 if(!this.html.length && this.bgimage.length){
35840 cls += ' masonry-bottom-title';
35845 cls += ' ' + this.cls;
35849 tag: (this.href.length) ? 'a' : 'div',
35854 cls: 'masonry-brick-mask'
35858 cls: 'masonry-brick-paragraph',
35864 if(this.href.length){
35865 cfg.href = this.href;
35868 var cn = cfg.cn[1].cn;
35870 if(this.title.length){
35873 cls: 'masonry-brick-title',
35878 if(this.html.length){
35881 cls: 'masonry-brick-text',
35886 if (!this.title.length && !this.html.length) {
35887 cfg.cn[1].cls += ' hide';
35890 if(this.bgimage.length){
35893 cls: 'masonry-brick-image-view',
35898 if(this.videourl.length){
35899 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35900 // youtube support only?
35903 cls: 'masonry-brick-image-view',
35906 allowfullscreen : true
35914 getSplitAutoCreate : function()
35916 var cls = 'masonry-brick masonry-brick-split';
35918 if(this.href.length){
35919 cls += ' masonry-brick-link';
35922 if(this.bgimage.length){
35923 cls += ' masonry-brick-image';
35927 cls += ' masonry-' + this.size + '-brick';
35930 switch (this.placetitle) {
35932 cls += ' masonry-center-title';
35935 cls += ' masonry-bottom-title';
35938 if(!this.bgimage.length){
35939 cls += ' masonry-center-title';
35942 if(this.bgimage.length){
35943 cls += ' masonry-bottom-title';
35949 cls += ' ' + this.cls;
35953 tag: (this.href.length) ? 'a' : 'div',
35958 cls: 'masonry-brick-split-head',
35962 cls: 'masonry-brick-paragraph',
35969 cls: 'masonry-brick-split-body',
35975 if(this.href.length){
35976 cfg.href = this.href;
35979 if(this.title.length){
35980 cfg.cn[0].cn[0].cn.push({
35982 cls: 'masonry-brick-title',
35987 if(this.html.length){
35988 cfg.cn[1].cn.push({
35990 cls: 'masonry-brick-text',
35995 if(this.bgimage.length){
35996 cfg.cn[0].cn.push({
35998 cls: 'masonry-brick-image-view',
36003 if(this.videourl.length){
36004 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36005 // youtube support only?
36006 cfg.cn[0].cn.cn.push({
36008 cls: 'masonry-brick-image-view',
36011 allowfullscreen : true
36018 initEvents: function()
36020 switch (this.size) {
36053 this.el.on('touchstart', this.onTouchStart, this);
36054 this.el.on('touchmove', this.onTouchMove, this);
36055 this.el.on('touchend', this.onTouchEnd, this);
36056 this.el.on('contextmenu', this.onContextMenu, this);
36058 this.el.on('mouseenter' ,this.enter, this);
36059 this.el.on('mouseleave', this.leave, this);
36060 this.el.on('click', this.onClick, this);
36063 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36064 this.parent().bricks.push(this);
36069 onClick: function(e, el)
36071 var time = this.endTimer - this.startTimer;
36072 // Roo.log(e.preventDefault());
36075 e.preventDefault();
36080 if(!this.preventDefault){
36084 e.preventDefault();
36086 if (this.activeClass != '') {
36087 this.selectBrick();
36090 this.fireEvent('click', this, e);
36093 enter: function(e, el)
36095 e.preventDefault();
36097 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36101 if(this.bgimage.length && this.html.length){
36102 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36106 leave: function(e, el)
36108 e.preventDefault();
36110 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36114 if(this.bgimage.length && this.html.length){
36115 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36119 onTouchStart: function(e, el)
36121 // e.preventDefault();
36123 this.touchmoved = false;
36125 if(!this.isFitContainer){
36129 if(!this.bgimage.length || !this.html.length){
36133 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36135 this.timer = new Date().getTime();
36139 onTouchMove: function(e, el)
36141 this.touchmoved = true;
36144 onContextMenu : function(e,el)
36146 e.preventDefault();
36147 e.stopPropagation();
36151 onTouchEnd: function(e, el)
36153 // e.preventDefault();
36155 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36162 if(!this.bgimage.length || !this.html.length){
36164 if(this.href.length){
36165 window.location.href = this.href;
36171 if(!this.isFitContainer){
36175 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36177 window.location.href = this.href;
36180 //selection on single brick only
36181 selectBrick : function() {
36183 if (!this.parentId) {
36187 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36188 var index = m.selectedBrick.indexOf(this.id);
36191 m.selectedBrick.splice(index,1);
36192 this.el.removeClass(this.activeClass);
36196 for(var i = 0; i < m.selectedBrick.length; i++) {
36197 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36198 b.el.removeClass(b.activeClass);
36201 m.selectedBrick = [];
36203 m.selectedBrick.push(this.id);
36204 this.el.addClass(this.activeClass);
36208 isSelected : function(){
36209 return this.el.hasClass(this.activeClass);
36214 Roo.apply(Roo.bootstrap.MasonryBrick, {
36217 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36219 * register a Masonry Brick
36220 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36223 register : function(brick)
36225 //this.groups[brick.id] = brick;
36226 this.groups.add(brick.id, brick);
36229 * fetch a masonry brick based on the masonry brick ID
36230 * @param {string} the masonry brick to add
36231 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36234 get: function(brick_id)
36236 // if (typeof(this.groups[brick_id]) == 'undefined') {
36239 // return this.groups[brick_id] ;
36241 if(this.groups.key(brick_id)) {
36242 return this.groups.key(brick_id);
36260 * @class Roo.bootstrap.Brick
36261 * @extends Roo.bootstrap.Component
36262 * Bootstrap Brick class
36265 * Create a new Brick
36266 * @param {Object} config The config object
36269 Roo.bootstrap.Brick = function(config){
36270 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36276 * When a Brick is click
36277 * @param {Roo.bootstrap.Brick} this
36278 * @param {Roo.EventObject} e
36284 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
36287 * @cfg {String} title
36291 * @cfg {String} html
36295 * @cfg {String} bgimage
36299 * @cfg {String} cls
36303 * @cfg {String} href
36307 * @cfg {String} video
36311 * @cfg {Boolean} square
36315 getAutoCreate : function()
36317 var cls = 'roo-brick';
36319 if(this.href.length){
36320 cls += ' roo-brick-link';
36323 if(this.bgimage.length){
36324 cls += ' roo-brick-image';
36327 if(!this.html.length && !this.bgimage.length){
36328 cls += ' roo-brick-center-title';
36331 if(!this.html.length && this.bgimage.length){
36332 cls += ' roo-brick-bottom-title';
36336 cls += ' ' + this.cls;
36340 tag: (this.href.length) ? 'a' : 'div',
36345 cls: 'roo-brick-paragraph',
36351 if(this.href.length){
36352 cfg.href = this.href;
36355 var cn = cfg.cn[0].cn;
36357 if(this.title.length){
36360 cls: 'roo-brick-title',
36365 if(this.html.length){
36368 cls: 'roo-brick-text',
36375 if(this.bgimage.length){
36378 cls: 'roo-brick-image-view',
36386 initEvents: function()
36388 if(this.title.length || this.html.length){
36389 this.el.on('mouseenter' ,this.enter, this);
36390 this.el.on('mouseleave', this.leave, this);
36393 Roo.EventManager.onWindowResize(this.resize, this);
36395 if(this.bgimage.length){
36396 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36397 this.imageEl.on('load', this.onImageLoad, this);
36404 onImageLoad : function()
36409 resize : function()
36411 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36413 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36415 if(this.bgimage.length){
36416 var image = this.el.select('.roo-brick-image-view', true).first();
36418 image.setWidth(paragraph.getWidth());
36421 image.setHeight(paragraph.getWidth());
36424 this.el.setHeight(image.getHeight());
36425 paragraph.setHeight(image.getHeight());
36431 enter: function(e, el)
36433 e.preventDefault();
36435 if(this.bgimage.length){
36436 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36437 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36441 leave: function(e, el)
36443 e.preventDefault();
36445 if(this.bgimage.length){
36446 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36447 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36462 * @class Roo.bootstrap.NumberField
36463 * @extends Roo.bootstrap.Input
36464 * Bootstrap NumberField class
36470 * Create a new NumberField
36471 * @param {Object} config The config object
36474 Roo.bootstrap.NumberField = function(config){
36475 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36478 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36481 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36483 allowDecimals : true,
36485 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36487 decimalSeparator : ".",
36489 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36491 decimalPrecision : 2,
36493 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36495 allowNegative : true,
36498 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36502 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36504 minValue : Number.NEGATIVE_INFINITY,
36506 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36508 maxValue : Number.MAX_VALUE,
36510 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36512 minText : "The minimum value for this field is {0}",
36514 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36516 maxText : "The maximum value for this field is {0}",
36518 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36519 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36521 nanText : "{0} is not a valid number",
36523 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36525 thousandsDelimiter : false,
36527 * @cfg {String} valueAlign alignment of value
36529 valueAlign : "left",
36531 getAutoCreate : function()
36533 var hiddenInput = {
36537 cls: 'hidden-number-input'
36541 hiddenInput.name = this.name;
36546 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36548 this.name = hiddenInput.name;
36550 if(cfg.cn.length > 0) {
36551 cfg.cn.push(hiddenInput);
36558 initEvents : function()
36560 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36562 var allowed = "0123456789";
36564 if(this.allowDecimals){
36565 allowed += this.decimalSeparator;
36568 if(this.allowNegative){
36572 if(this.thousandsDelimiter) {
36576 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36578 var keyPress = function(e){
36580 var k = e.getKey();
36582 var c = e.getCharCode();
36585 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36586 allowed.indexOf(String.fromCharCode(c)) === -1
36592 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36596 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36601 this.el.on("keypress", keyPress, this);
36604 validateValue : function(value)
36607 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36611 var num = this.parseValue(value);
36614 this.markInvalid(String.format(this.nanText, value));
36618 if(num < this.minValue){
36619 this.markInvalid(String.format(this.minText, this.minValue));
36623 if(num > this.maxValue){
36624 this.markInvalid(String.format(this.maxText, this.maxValue));
36631 getValue : function()
36633 var v = this.hiddenEl().getValue();
36635 return this.fixPrecision(this.parseValue(v));
36638 parseValue : function(value)
36640 if(this.thousandsDelimiter) {
36642 r = new RegExp(",", "g");
36643 value = value.replace(r, "");
36646 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36647 return isNaN(value) ? '' : value;
36650 fixPrecision : function(value)
36652 if(this.thousandsDelimiter) {
36654 r = new RegExp(",", "g");
36655 value = value.replace(r, "");
36658 var nan = isNaN(value);
36660 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36661 return nan ? '' : value;
36663 return parseFloat(value).toFixed(this.decimalPrecision);
36666 setValue : function(v)
36668 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36674 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36676 this.inputEl().dom.value = (v == '') ? '' :
36677 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36679 if(!this.allowZero && v === '0') {
36680 this.hiddenEl().dom.value = '';
36681 this.inputEl().dom.value = '';
36688 decimalPrecisionFcn : function(v)
36690 return Math.floor(v);
36693 beforeBlur : function()
36695 var v = this.parseValue(this.getRawValue());
36697 if(v || v === 0 || v === ''){
36702 hiddenEl : function()
36704 return this.el.select('input.hidden-number-input',true).first();
36716 * @class Roo.bootstrap.DocumentSlider
36717 * @extends Roo.bootstrap.Component
36718 * Bootstrap DocumentSlider class
36721 * Create a new DocumentViewer
36722 * @param {Object} config The config object
36725 Roo.bootstrap.DocumentSlider = function(config){
36726 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36733 * Fire after initEvent
36734 * @param {Roo.bootstrap.DocumentSlider} this
36739 * Fire after update
36740 * @param {Roo.bootstrap.DocumentSlider} this
36746 * @param {Roo.bootstrap.DocumentSlider} this
36752 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
36758 getAutoCreate : function()
36762 cls : 'roo-document-slider',
36766 cls : 'roo-document-slider-header',
36770 cls : 'roo-document-slider-header-title'
36776 cls : 'roo-document-slider-body',
36780 cls : 'roo-document-slider-prev',
36784 cls : 'fa fa-chevron-left'
36790 cls : 'roo-document-slider-thumb',
36794 cls : 'roo-document-slider-image'
36800 cls : 'roo-document-slider-next',
36804 cls : 'fa fa-chevron-right'
36816 initEvents : function()
36818 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36819 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36821 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36822 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36824 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36825 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36827 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36828 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36830 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36831 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36833 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36834 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36836 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36837 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36839 this.thumbEl.on('click', this.onClick, this);
36841 this.prevIndicator.on('click', this.prev, this);
36843 this.nextIndicator.on('click', this.next, this);
36847 initial : function()
36849 if(this.files.length){
36850 this.indicator = 1;
36854 this.fireEvent('initial', this);
36857 update : function()
36859 this.imageEl.attr('src', this.files[this.indicator - 1]);
36861 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36863 this.prevIndicator.show();
36865 if(this.indicator == 1){
36866 this.prevIndicator.hide();
36869 this.nextIndicator.show();
36871 if(this.indicator == this.files.length){
36872 this.nextIndicator.hide();
36875 this.thumbEl.scrollTo('top');
36877 this.fireEvent('update', this);
36880 onClick : function(e)
36882 e.preventDefault();
36884 this.fireEvent('click', this);
36889 e.preventDefault();
36891 this.indicator = Math.max(1, this.indicator - 1);
36898 e.preventDefault();
36900 this.indicator = Math.min(this.files.length, this.indicator + 1);
36914 * @class Roo.bootstrap.RadioSet
36915 * @extends Roo.bootstrap.Input
36916 * Bootstrap RadioSet class
36917 * @cfg {String} indicatorpos (left|right) default left
36918 * @cfg {Boolean} inline (true|false) inline the element (default true)
36919 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36921 * Create a new RadioSet
36922 * @param {Object} config The config object
36925 Roo.bootstrap.RadioSet = function(config){
36927 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36931 Roo.bootstrap.RadioSet.register(this);
36936 * Fires when the element is checked or unchecked.
36937 * @param {Roo.bootstrap.RadioSet} this This radio
36938 * @param {Roo.bootstrap.Radio} item The checked item
36943 * Fires when the element is click.
36944 * @param {Roo.bootstrap.RadioSet} this This radio set
36945 * @param {Roo.bootstrap.Radio} item The checked item
36946 * @param {Roo.EventObject} e The event object
36953 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
36961 indicatorpos : 'left',
36963 getAutoCreate : function()
36967 cls : 'roo-radio-set-label',
36971 html : this.fieldLabel
36975 if (Roo.bootstrap.version == 3) {
36978 if(this.indicatorpos == 'left'){
36981 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36982 tooltip : 'This field is required'
36987 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36988 tooltip : 'This field is required'
36994 cls : 'roo-radio-set-items'
36997 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36999 if (align === 'left' && this.fieldLabel.length) {
37002 cls : "roo-radio-set-right",
37008 if(this.labelWidth > 12){
37009 label.style = "width: " + this.labelWidth + 'px';
37012 if(this.labelWidth < 13 && this.labelmd == 0){
37013 this.labelmd = this.labelWidth;
37016 if(this.labellg > 0){
37017 label.cls += ' col-lg-' + this.labellg;
37018 items.cls += ' col-lg-' + (12 - this.labellg);
37021 if(this.labelmd > 0){
37022 label.cls += ' col-md-' + this.labelmd;
37023 items.cls += ' col-md-' + (12 - this.labelmd);
37026 if(this.labelsm > 0){
37027 label.cls += ' col-sm-' + this.labelsm;
37028 items.cls += ' col-sm-' + (12 - this.labelsm);
37031 if(this.labelxs > 0){
37032 label.cls += ' col-xs-' + this.labelxs;
37033 items.cls += ' col-xs-' + (12 - this.labelxs);
37039 cls : 'roo-radio-set',
37043 cls : 'roo-radio-set-input',
37046 value : this.value ? this.value : ''
37053 if(this.weight.length){
37054 cfg.cls += ' roo-radio-' + this.weight;
37058 cfg.cls += ' roo-radio-set-inline';
37062 ['xs','sm','md','lg'].map(function(size){
37063 if (settings[size]) {
37064 cfg.cls += ' col-' + size + '-' + settings[size];
37072 initEvents : function()
37074 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37075 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37077 if(!this.fieldLabel.length){
37078 this.labelEl.hide();
37081 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37082 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37084 this.indicator = this.indicatorEl();
37086 if(this.indicator){
37087 this.indicator.addClass('invisible');
37090 this.originalValue = this.getValue();
37094 inputEl: function ()
37096 return this.el.select('.roo-radio-set-input', true).first();
37099 getChildContainer : function()
37101 return this.itemsEl;
37104 register : function(item)
37106 this.radioes.push(item);
37110 validate : function()
37112 if(this.getVisibilityEl().hasClass('hidden')){
37118 Roo.each(this.radioes, function(i){
37127 if(this.allowBlank) {
37131 if(this.disabled || valid){
37136 this.markInvalid();
37141 markValid : function()
37143 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37144 this.indicatorEl().removeClass('visible');
37145 this.indicatorEl().addClass('invisible');
37149 if (Roo.bootstrap.version == 3) {
37150 this.el.removeClass([this.invalidClass, this.validClass]);
37151 this.el.addClass(this.validClass);
37153 this.el.removeClass(['is-invalid','is-valid']);
37154 this.el.addClass(['is-valid']);
37156 this.fireEvent('valid', this);
37159 markInvalid : function(msg)
37161 if(this.allowBlank || this.disabled){
37165 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37166 this.indicatorEl().removeClass('invisible');
37167 this.indicatorEl().addClass('visible');
37169 if (Roo.bootstrap.version == 3) {
37170 this.el.removeClass([this.invalidClass, this.validClass]);
37171 this.el.addClass(this.invalidClass);
37173 this.el.removeClass(['is-invalid','is-valid']);
37174 this.el.addClass(['is-invalid']);
37177 this.fireEvent('invalid', this, msg);
37181 setValue : function(v, suppressEvent)
37183 if(this.value === v){
37190 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37193 Roo.each(this.radioes, function(i){
37195 i.el.removeClass('checked');
37198 Roo.each(this.radioes, function(i){
37200 if(i.value === v || i.value.toString() === v.toString()){
37202 i.el.addClass('checked');
37204 if(suppressEvent !== true){
37205 this.fireEvent('check', this, i);
37216 clearInvalid : function(){
37218 if(!this.el || this.preventMark){
37222 this.el.removeClass([this.invalidClass]);
37224 this.fireEvent('valid', this);
37229 Roo.apply(Roo.bootstrap.RadioSet, {
37233 register : function(set)
37235 this.groups[set.name] = set;
37238 get: function(name)
37240 if (typeof(this.groups[name]) == 'undefined') {
37244 return this.groups[name] ;
37250 * Ext JS Library 1.1.1
37251 * Copyright(c) 2006-2007, Ext JS, LLC.
37253 * Originally Released Under LGPL - original licence link has changed is not relivant.
37256 * <script type="text/javascript">
37261 * @class Roo.bootstrap.SplitBar
37262 * @extends Roo.util.Observable
37263 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37267 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37268 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37269 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37270 split.minSize = 100;
37271 split.maxSize = 600;
37272 split.animate = true;
37273 split.on('moved', splitterMoved);
37276 * Create a new SplitBar
37277 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
37278 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
37279 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37280 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
37281 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37282 position of the SplitBar).
37284 Roo.bootstrap.SplitBar = function(cfg){
37289 // dragElement : elm
37290 // resizingElement: el,
37292 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37293 // placement : Roo.bootstrap.SplitBar.LEFT ,
37294 // existingProxy ???
37297 this.el = Roo.get(cfg.dragElement, true);
37298 this.el.dom.unselectable = "on";
37300 this.resizingEl = Roo.get(cfg.resizingElement, true);
37304 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37305 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37308 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37311 * The minimum size of the resizing element. (Defaults to 0)
37317 * The maximum size of the resizing element. (Defaults to 2000)
37320 this.maxSize = 2000;
37323 * Whether to animate the transition to the new size
37326 this.animate = false;
37329 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37332 this.useShim = false;
37337 if(!cfg.existingProxy){
37339 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37341 this.proxy = Roo.get(cfg.existingProxy).dom;
37344 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37347 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37350 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37353 this.dragSpecs = {};
37356 * @private The adapter to use to positon and resize elements
37358 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37359 this.adapter.init(this);
37361 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37363 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37364 this.el.addClass("roo-splitbar-h");
37367 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37368 this.el.addClass("roo-splitbar-v");
37374 * Fires when the splitter is moved (alias for {@link #event-moved})
37375 * @param {Roo.bootstrap.SplitBar} this
37376 * @param {Number} newSize the new width or height
37381 * Fires when the splitter is moved
37382 * @param {Roo.bootstrap.SplitBar} this
37383 * @param {Number} newSize the new width or height
37387 * @event beforeresize
37388 * Fires before the splitter is dragged
37389 * @param {Roo.bootstrap.SplitBar} this
37391 "beforeresize" : true,
37393 "beforeapply" : true
37396 Roo.util.Observable.call(this);
37399 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37400 onStartProxyDrag : function(x, y){
37401 this.fireEvent("beforeresize", this);
37403 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37405 o.enableDisplayMode("block");
37406 // all splitbars share the same overlay
37407 Roo.bootstrap.SplitBar.prototype.overlay = o;
37409 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37410 this.overlay.show();
37411 Roo.get(this.proxy).setDisplayed("block");
37412 var size = this.adapter.getElementSize(this);
37413 this.activeMinSize = this.getMinimumSize();;
37414 this.activeMaxSize = this.getMaximumSize();;
37415 var c1 = size - this.activeMinSize;
37416 var c2 = Math.max(this.activeMaxSize - size, 0);
37417 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37418 this.dd.resetConstraints();
37419 this.dd.setXConstraint(
37420 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37421 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37423 this.dd.setYConstraint(0, 0);
37425 this.dd.resetConstraints();
37426 this.dd.setXConstraint(0, 0);
37427 this.dd.setYConstraint(
37428 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37429 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37432 this.dragSpecs.startSize = size;
37433 this.dragSpecs.startPoint = [x, y];
37434 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37438 * @private Called after the drag operation by the DDProxy
37440 onEndProxyDrag : function(e){
37441 Roo.get(this.proxy).setDisplayed(false);
37442 var endPoint = Roo.lib.Event.getXY(e);
37444 this.overlay.hide();
37447 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37448 newSize = this.dragSpecs.startSize +
37449 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37450 endPoint[0] - this.dragSpecs.startPoint[0] :
37451 this.dragSpecs.startPoint[0] - endPoint[0]
37454 newSize = this.dragSpecs.startSize +
37455 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37456 endPoint[1] - this.dragSpecs.startPoint[1] :
37457 this.dragSpecs.startPoint[1] - endPoint[1]
37460 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37461 if(newSize != this.dragSpecs.startSize){
37462 if(this.fireEvent('beforeapply', this, newSize) !== false){
37463 this.adapter.setElementSize(this, newSize);
37464 this.fireEvent("moved", this, newSize);
37465 this.fireEvent("resize", this, newSize);
37471 * Get the adapter this SplitBar uses
37472 * @return The adapter object
37474 getAdapter : function(){
37475 return this.adapter;
37479 * Set the adapter this SplitBar uses
37480 * @param {Object} adapter A SplitBar adapter object
37482 setAdapter : function(adapter){
37483 this.adapter = adapter;
37484 this.adapter.init(this);
37488 * Gets the minimum size for the resizing element
37489 * @return {Number} The minimum size
37491 getMinimumSize : function(){
37492 return this.minSize;
37496 * Sets the minimum size for the resizing element
37497 * @param {Number} minSize The minimum size
37499 setMinimumSize : function(minSize){
37500 this.minSize = minSize;
37504 * Gets the maximum size for the resizing element
37505 * @return {Number} The maximum size
37507 getMaximumSize : function(){
37508 return this.maxSize;
37512 * Sets the maximum size for the resizing element
37513 * @param {Number} maxSize The maximum size
37515 setMaximumSize : function(maxSize){
37516 this.maxSize = maxSize;
37520 * Sets the initialize size for the resizing element
37521 * @param {Number} size The initial size
37523 setCurrentSize : function(size){
37524 var oldAnimate = this.animate;
37525 this.animate = false;
37526 this.adapter.setElementSize(this, size);
37527 this.animate = oldAnimate;
37531 * Destroy this splitbar.
37532 * @param {Boolean} removeEl True to remove the element
37534 destroy : function(removeEl){
37536 this.shim.remove();
37539 this.proxy.parentNode.removeChild(this.proxy);
37547 * @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.
37549 Roo.bootstrap.SplitBar.createProxy = function(dir){
37550 var proxy = new Roo.Element(document.createElement("div"));
37551 proxy.unselectable();
37552 var cls = 'roo-splitbar-proxy';
37553 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37554 document.body.appendChild(proxy.dom);
37559 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37560 * Default Adapter. It assumes the splitter and resizing element are not positioned
37561 * elements and only gets/sets the width of the element. Generally used for table based layouts.
37563 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37566 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37567 // do nothing for now
37568 init : function(s){
37572 * Called before drag operations to get the current size of the resizing element.
37573 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37575 getElementSize : function(s){
37576 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37577 return s.resizingEl.getWidth();
37579 return s.resizingEl.getHeight();
37584 * Called after drag operations to set the size of the resizing element.
37585 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37586 * @param {Number} newSize The new size to set
37587 * @param {Function} onComplete A function to be invoked when resizing is complete
37589 setElementSize : function(s, newSize, onComplete){
37590 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37592 s.resizingEl.setWidth(newSize);
37594 onComplete(s, newSize);
37597 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37602 s.resizingEl.setHeight(newSize);
37604 onComplete(s, newSize);
37607 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37614 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37615 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37616 * Adapter that moves the splitter element to align with the resized sizing element.
37617 * Used with an absolute positioned SplitBar.
37618 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37619 * document.body, make sure you assign an id to the body element.
37621 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37622 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37623 this.container = Roo.get(container);
37626 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37627 init : function(s){
37628 this.basic.init(s);
37631 getElementSize : function(s){
37632 return this.basic.getElementSize(s);
37635 setElementSize : function(s, newSize, onComplete){
37636 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37639 moveSplitter : function(s){
37640 var yes = Roo.bootstrap.SplitBar;
37641 switch(s.placement){
37643 s.el.setX(s.resizingEl.getRight());
37646 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37649 s.el.setY(s.resizingEl.getBottom());
37652 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37659 * Orientation constant - Create a vertical SplitBar
37663 Roo.bootstrap.SplitBar.VERTICAL = 1;
37666 * Orientation constant - Create a horizontal SplitBar
37670 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37673 * Placement constant - The resizing element is to the left of the splitter element
37677 Roo.bootstrap.SplitBar.LEFT = 1;
37680 * Placement constant - The resizing element is to the right of the splitter element
37684 Roo.bootstrap.SplitBar.RIGHT = 2;
37687 * Placement constant - The resizing element is positioned above the splitter element
37691 Roo.bootstrap.SplitBar.TOP = 3;
37694 * Placement constant - The resizing element is positioned under splitter element
37698 Roo.bootstrap.SplitBar.BOTTOM = 4;
37699 Roo.namespace("Roo.bootstrap.layout");/*
37701 * Ext JS Library 1.1.1
37702 * Copyright(c) 2006-2007, Ext JS, LLC.
37704 * Originally Released Under LGPL - original licence link has changed is not relivant.
37707 * <script type="text/javascript">
37711 * @class Roo.bootstrap.layout.Manager
37712 * @extends Roo.bootstrap.Component
37713 * Base class for layout managers.
37715 Roo.bootstrap.layout.Manager = function(config)
37717 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37723 /** false to disable window resize monitoring @type Boolean */
37724 this.monitorWindowResize = true;
37729 * Fires when a layout is performed.
37730 * @param {Roo.LayoutManager} this
37734 * @event regionresized
37735 * Fires when the user resizes a region.
37736 * @param {Roo.LayoutRegion} region The resized region
37737 * @param {Number} newSize The new size (width for east/west, height for north/south)
37739 "regionresized" : true,
37741 * @event regioncollapsed
37742 * Fires when a region is collapsed.
37743 * @param {Roo.LayoutRegion} region The collapsed region
37745 "regioncollapsed" : true,
37747 * @event regionexpanded
37748 * Fires when a region is expanded.
37749 * @param {Roo.LayoutRegion} region The expanded region
37751 "regionexpanded" : true
37753 this.updating = false;
37756 this.el = Roo.get(config.el);
37762 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37767 monitorWindowResize : true,
37773 onRender : function(ct, position)
37776 this.el = Roo.get(ct);
37779 //this.fireEvent('render',this);
37783 initEvents: function()
37787 // ie scrollbar fix
37788 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37789 document.body.scroll = "no";
37790 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37791 this.el.position('relative');
37793 this.id = this.el.id;
37794 this.el.addClass("roo-layout-container");
37795 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37796 if(this.el.dom != document.body ) {
37797 this.el.on('resize', this.layout,this);
37798 this.el.on('show', this.layout,this);
37804 * Returns true if this layout is currently being updated
37805 * @return {Boolean}
37807 isUpdating : function(){
37808 return this.updating;
37812 * Suspend the LayoutManager from doing auto-layouts while
37813 * making multiple add or remove calls
37815 beginUpdate : function(){
37816 this.updating = true;
37820 * Restore auto-layouts and optionally disable the manager from performing a layout
37821 * @param {Boolean} noLayout true to disable a layout update
37823 endUpdate : function(noLayout){
37824 this.updating = false;
37830 layout: function(){
37834 onRegionResized : function(region, newSize){
37835 this.fireEvent("regionresized", region, newSize);
37839 onRegionCollapsed : function(region){
37840 this.fireEvent("regioncollapsed", region);
37843 onRegionExpanded : function(region){
37844 this.fireEvent("regionexpanded", region);
37848 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37849 * performs box-model adjustments.
37850 * @return {Object} The size as an object {width: (the width), height: (the height)}
37852 getViewSize : function()
37855 if(this.el.dom != document.body){
37856 size = this.el.getSize();
37858 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37860 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37861 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37866 * Returns the Element this layout is bound to.
37867 * @return {Roo.Element}
37869 getEl : function(){
37874 * Returns the specified region.
37875 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37876 * @return {Roo.LayoutRegion}
37878 getRegion : function(target){
37879 return this.regions[target.toLowerCase()];
37882 onWindowResize : function(){
37883 if(this.monitorWindowResize){
37890 * Ext JS Library 1.1.1
37891 * Copyright(c) 2006-2007, Ext JS, LLC.
37893 * Originally Released Under LGPL - original licence link has changed is not relivant.
37896 * <script type="text/javascript">
37899 * @class Roo.bootstrap.layout.Border
37900 * @extends Roo.bootstrap.layout.Manager
37901 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37902 * please see: examples/bootstrap/nested.html<br><br>
37904 <b>The container the layout is rendered into can be either the body element or any other element.
37905 If it is not the body element, the container needs to either be an absolute positioned element,
37906 or you will need to add "position:relative" to the css of the container. You will also need to specify
37907 the container size if it is not the body element.</b>
37910 * Create a new Border
37911 * @param {Object} config Configuration options
37913 Roo.bootstrap.layout.Border = function(config){
37914 config = config || {};
37915 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37919 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37920 if(config[region]){
37921 config[region].region = region;
37922 this.addRegion(config[region]);
37928 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
37930 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37932 parent : false, // this might point to a 'nest' or a ???
37935 * Creates and adds a new region if it doesn't already exist.
37936 * @param {String} target The target region key (north, south, east, west or center).
37937 * @param {Object} config The regions config object
37938 * @return {BorderLayoutRegion} The new region
37940 addRegion : function(config)
37942 if(!this.regions[config.region]){
37943 var r = this.factory(config);
37944 this.bindRegion(r);
37946 return this.regions[config.region];
37950 bindRegion : function(r){
37951 this.regions[r.config.region] = r;
37953 r.on("visibilitychange", this.layout, this);
37954 r.on("paneladded", this.layout, this);
37955 r.on("panelremoved", this.layout, this);
37956 r.on("invalidated", this.layout, this);
37957 r.on("resized", this.onRegionResized, this);
37958 r.on("collapsed", this.onRegionCollapsed, this);
37959 r.on("expanded", this.onRegionExpanded, this);
37963 * Performs a layout update.
37965 layout : function()
37967 if(this.updating) {
37971 // render all the rebions if they have not been done alreayd?
37972 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37973 if(this.regions[region] && !this.regions[region].bodyEl){
37974 this.regions[region].onRender(this.el)
37978 var size = this.getViewSize();
37979 var w = size.width;
37980 var h = size.height;
37985 //var x = 0, y = 0;
37987 var rs = this.regions;
37988 var north = rs["north"];
37989 var south = rs["south"];
37990 var west = rs["west"];
37991 var east = rs["east"];
37992 var center = rs["center"];
37993 //if(this.hideOnLayout){ // not supported anymore
37994 //c.el.setStyle("display", "none");
37996 if(north && north.isVisible()){
37997 var b = north.getBox();
37998 var m = north.getMargins();
37999 b.width = w - (m.left+m.right);
38002 centerY = b.height + b.y + m.bottom;
38003 centerH -= centerY;
38004 north.updateBox(this.safeBox(b));
38006 if(south && south.isVisible()){
38007 var b = south.getBox();
38008 var m = south.getMargins();
38009 b.width = w - (m.left+m.right);
38011 var totalHeight = (b.height + m.top + m.bottom);
38012 b.y = h - totalHeight + m.top;
38013 centerH -= totalHeight;
38014 south.updateBox(this.safeBox(b));
38016 if(west && west.isVisible()){
38017 var b = west.getBox();
38018 var m = west.getMargins();
38019 b.height = centerH - (m.top+m.bottom);
38021 b.y = centerY + m.top;
38022 var totalWidth = (b.width + m.left + m.right);
38023 centerX += totalWidth;
38024 centerW -= totalWidth;
38025 west.updateBox(this.safeBox(b));
38027 if(east && east.isVisible()){
38028 var b = east.getBox();
38029 var m = east.getMargins();
38030 b.height = centerH - (m.top+m.bottom);
38031 var totalWidth = (b.width + m.left + m.right);
38032 b.x = w - totalWidth + m.left;
38033 b.y = centerY + m.top;
38034 centerW -= totalWidth;
38035 east.updateBox(this.safeBox(b));
38038 var m = center.getMargins();
38040 x: centerX + m.left,
38041 y: centerY + m.top,
38042 width: centerW - (m.left+m.right),
38043 height: centerH - (m.top+m.bottom)
38045 //if(this.hideOnLayout){
38046 //center.el.setStyle("display", "block");
38048 center.updateBox(this.safeBox(centerBox));
38051 this.fireEvent("layout", this);
38055 safeBox : function(box){
38056 box.width = Math.max(0, box.width);
38057 box.height = Math.max(0, box.height);
38062 * Adds a ContentPanel (or subclass) to this layout.
38063 * @param {String} target The target region key (north, south, east, west or center).
38064 * @param {Roo.ContentPanel} panel The panel to add
38065 * @return {Roo.ContentPanel} The added panel
38067 add : function(target, panel){
38069 target = target.toLowerCase();
38070 return this.regions[target].add(panel);
38074 * Remove a ContentPanel (or subclass) to this layout.
38075 * @param {String} target The target region key (north, south, east, west or center).
38076 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38077 * @return {Roo.ContentPanel} The removed panel
38079 remove : function(target, panel){
38080 target = target.toLowerCase();
38081 return this.regions[target].remove(panel);
38085 * Searches all regions for a panel with the specified id
38086 * @param {String} panelId
38087 * @return {Roo.ContentPanel} The panel or null if it wasn't found
38089 findPanel : function(panelId){
38090 var rs = this.regions;
38091 for(var target in rs){
38092 if(typeof rs[target] != "function"){
38093 var p = rs[target].getPanel(panelId);
38103 * Searches all regions for a panel with the specified id and activates (shows) it.
38104 * @param {String/ContentPanel} panelId The panels id or the panel itself
38105 * @return {Roo.ContentPanel} The shown panel or null
38107 showPanel : function(panelId) {
38108 var rs = this.regions;
38109 for(var target in rs){
38110 var r = rs[target];
38111 if(typeof r != "function"){
38112 if(r.hasPanel(panelId)){
38113 return r.showPanel(panelId);
38121 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38122 * @param {Roo.state.Provider} provider (optional) An alternate state provider
38125 restoreState : function(provider){
38127 provider = Roo.state.Manager;
38129 var sm = new Roo.LayoutStateManager();
38130 sm.init(this, provider);
38136 * Adds a xtype elements to the layout.
38140 xtype : 'ContentPanel',
38147 xtype : 'NestedLayoutPanel',
38153 items : [ ... list of content panels or nested layout panels.. ]
38157 * @param {Object} cfg Xtype definition of item to add.
38159 addxtype : function(cfg)
38161 // basically accepts a pannel...
38162 // can accept a layout region..!?!?
38163 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38166 // theory? children can only be panels??
38168 //if (!cfg.xtype.match(/Panel$/)) {
38173 if (typeof(cfg.region) == 'undefined') {
38174 Roo.log("Failed to add Panel, region was not set");
38178 var region = cfg.region;
38184 xitems = cfg.items;
38189 if ( region == 'center') {
38190 Roo.log("Center: " + cfg.title);
38196 case 'Content': // ContentPanel (el, cfg)
38197 case 'Scroll': // ContentPanel (el, cfg)
38199 cfg.autoCreate = cfg.autoCreate || true;
38200 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38202 // var el = this.el.createChild();
38203 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38206 this.add(region, ret);
38210 case 'TreePanel': // our new panel!
38211 cfg.el = this.el.createChild();
38212 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38213 this.add(region, ret);
38218 // create a new Layout (which is a Border Layout...
38220 var clayout = cfg.layout;
38221 clayout.el = this.el.createChild();
38222 clayout.items = clayout.items || [];
38226 // replace this exitems with the clayout ones..
38227 xitems = clayout.items;
38229 // force background off if it's in center...
38230 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38231 cfg.background = false;
38233 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
38236 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38237 //console.log('adding nested layout panel ' + cfg.toSource());
38238 this.add(region, ret);
38239 nb = {}; /// find first...
38244 // needs grid and region
38246 //var el = this.getRegion(region).el.createChild();
38248 *var el = this.el.createChild();
38249 // create the grid first...
38250 cfg.grid.container = el;
38251 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38254 if (region == 'center' && this.active ) {
38255 cfg.background = false;
38258 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38260 this.add(region, ret);
38262 if (cfg.background) {
38263 // render grid on panel activation (if panel background)
38264 ret.on('activate', function(gp) {
38265 if (!gp.grid.rendered) {
38266 // gp.grid.render(el);
38270 // cfg.grid.render(el);
38276 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38277 // it was the old xcomponent building that caused this before.
38278 // espeically if border is the top element in the tree.
38288 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38290 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38291 this.add(region, ret);
38295 throw "Can not add '" + cfg.xtype + "' to Border";
38301 this.beginUpdate();
38305 Roo.each(xitems, function(i) {
38306 region = nb && i.region ? i.region : false;
38308 var add = ret.addxtype(i);
38311 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38312 if (!i.background) {
38313 abn[region] = nb[region] ;
38320 // make the last non-background panel active..
38321 //if (nb) { Roo.log(abn); }
38324 for(var r in abn) {
38325 region = this.getRegion(r);
38327 // tried using nb[r], but it does not work..
38329 region.showPanel(abn[r]);
38340 factory : function(cfg)
38343 var validRegions = Roo.bootstrap.layout.Border.regions;
38345 var target = cfg.region;
38348 var r = Roo.bootstrap.layout;
38352 return new r.North(cfg);
38354 return new r.South(cfg);
38356 return new r.East(cfg);
38358 return new r.West(cfg);
38360 return new r.Center(cfg);
38362 throw 'Layout region "'+target+'" not supported.';
38369 * Ext JS Library 1.1.1
38370 * Copyright(c) 2006-2007, Ext JS, LLC.
38372 * Originally Released Under LGPL - original licence link has changed is not relivant.
38375 * <script type="text/javascript">
38379 * @class Roo.bootstrap.layout.Basic
38380 * @extends Roo.util.Observable
38381 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38382 * and does not have a titlebar, tabs or any other features. All it does is size and position
38383 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38384 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38385 * @cfg {string} region the region that it inhabits..
38386 * @cfg {bool} skipConfig skip config?
38390 Roo.bootstrap.layout.Basic = function(config){
38392 this.mgr = config.mgr;
38394 this.position = config.region;
38396 var skipConfig = config.skipConfig;
38400 * @scope Roo.BasicLayoutRegion
38404 * @event beforeremove
38405 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38406 * @param {Roo.LayoutRegion} this
38407 * @param {Roo.ContentPanel} panel The panel
38408 * @param {Object} e The cancel event object
38410 "beforeremove" : true,
38412 * @event invalidated
38413 * Fires when the layout for this region is changed.
38414 * @param {Roo.LayoutRegion} this
38416 "invalidated" : true,
38418 * @event visibilitychange
38419 * Fires when this region is shown or hidden
38420 * @param {Roo.LayoutRegion} this
38421 * @param {Boolean} visibility true or false
38423 "visibilitychange" : true,
38425 * @event paneladded
38426 * Fires when a panel is added.
38427 * @param {Roo.LayoutRegion} this
38428 * @param {Roo.ContentPanel} panel The panel
38430 "paneladded" : true,
38432 * @event panelremoved
38433 * Fires when a panel is removed.
38434 * @param {Roo.LayoutRegion} this
38435 * @param {Roo.ContentPanel} panel The panel
38437 "panelremoved" : true,
38439 * @event beforecollapse
38440 * Fires when this region before collapse.
38441 * @param {Roo.LayoutRegion} this
38443 "beforecollapse" : true,
38446 * Fires when this region is collapsed.
38447 * @param {Roo.LayoutRegion} this
38449 "collapsed" : true,
38452 * Fires when this region is expanded.
38453 * @param {Roo.LayoutRegion} this
38458 * Fires when this region is slid into view.
38459 * @param {Roo.LayoutRegion} this
38461 "slideshow" : true,
38464 * Fires when this region slides out of view.
38465 * @param {Roo.LayoutRegion} this
38467 "slidehide" : true,
38469 * @event panelactivated
38470 * Fires when a panel is activated.
38471 * @param {Roo.LayoutRegion} this
38472 * @param {Roo.ContentPanel} panel The activated panel
38474 "panelactivated" : true,
38477 * Fires when the user resizes this region.
38478 * @param {Roo.LayoutRegion} this
38479 * @param {Number} newSize The new size (width for east/west, height for north/south)
38483 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38484 this.panels = new Roo.util.MixedCollection();
38485 this.panels.getKey = this.getPanelId.createDelegate(this);
38487 this.activePanel = null;
38488 // ensure listeners are added...
38490 if (config.listeners || config.events) {
38491 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38492 listeners : config.listeners || {},
38493 events : config.events || {}
38497 if(skipConfig !== true){
38498 this.applyConfig(config);
38502 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38504 getPanelId : function(p){
38508 applyConfig : function(config){
38509 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38510 this.config = config;
38515 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38516 * the width, for horizontal (north, south) the height.
38517 * @param {Number} newSize The new width or height
38519 resizeTo : function(newSize){
38520 var el = this.el ? this.el :
38521 (this.activePanel ? this.activePanel.getEl() : null);
38523 switch(this.position){
38526 el.setWidth(newSize);
38527 this.fireEvent("resized", this, newSize);
38531 el.setHeight(newSize);
38532 this.fireEvent("resized", this, newSize);
38538 getBox : function(){
38539 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38542 getMargins : function(){
38543 return this.margins;
38546 updateBox : function(box){
38548 var el = this.activePanel.getEl();
38549 el.dom.style.left = box.x + "px";
38550 el.dom.style.top = box.y + "px";
38551 this.activePanel.setSize(box.width, box.height);
38555 * Returns the container element for this region.
38556 * @return {Roo.Element}
38558 getEl : function(){
38559 return this.activePanel;
38563 * Returns true if this region is currently visible.
38564 * @return {Boolean}
38566 isVisible : function(){
38567 return this.activePanel ? true : false;
38570 setActivePanel : function(panel){
38571 panel = this.getPanel(panel);
38572 if(this.activePanel && this.activePanel != panel){
38573 this.activePanel.setActiveState(false);
38574 this.activePanel.getEl().setLeftTop(-10000,-10000);
38576 this.activePanel = panel;
38577 panel.setActiveState(true);
38579 panel.setSize(this.box.width, this.box.height);
38581 this.fireEvent("panelactivated", this, panel);
38582 this.fireEvent("invalidated");
38586 * Show the specified panel.
38587 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38588 * @return {Roo.ContentPanel} The shown panel or null
38590 showPanel : function(panel){
38591 panel = this.getPanel(panel);
38593 this.setActivePanel(panel);
38599 * Get the active panel for this region.
38600 * @return {Roo.ContentPanel} The active panel or null
38602 getActivePanel : function(){
38603 return this.activePanel;
38607 * Add the passed ContentPanel(s)
38608 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38609 * @return {Roo.ContentPanel} The panel added (if only one was added)
38611 add : function(panel){
38612 if(arguments.length > 1){
38613 for(var i = 0, len = arguments.length; i < len; i++) {
38614 this.add(arguments[i]);
38618 if(this.hasPanel(panel)){
38619 this.showPanel(panel);
38622 var el = panel.getEl();
38623 if(el.dom.parentNode != this.mgr.el.dom){
38624 this.mgr.el.dom.appendChild(el.dom);
38626 if(panel.setRegion){
38627 panel.setRegion(this);
38629 this.panels.add(panel);
38630 el.setStyle("position", "absolute");
38631 if(!panel.background){
38632 this.setActivePanel(panel);
38633 if(this.config.initialSize && this.panels.getCount()==1){
38634 this.resizeTo(this.config.initialSize);
38637 this.fireEvent("paneladded", this, panel);
38642 * Returns true if the panel is in this region.
38643 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38644 * @return {Boolean}
38646 hasPanel : function(panel){
38647 if(typeof panel == "object"){ // must be panel obj
38648 panel = panel.getId();
38650 return this.getPanel(panel) ? true : false;
38654 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38655 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38656 * @param {Boolean} preservePanel Overrides the config preservePanel option
38657 * @return {Roo.ContentPanel} The panel that was removed
38659 remove : function(panel, preservePanel){
38660 panel = this.getPanel(panel);
38665 this.fireEvent("beforeremove", this, panel, e);
38666 if(e.cancel === true){
38669 var panelId = panel.getId();
38670 this.panels.removeKey(panelId);
38675 * Returns the panel specified or null if it's not in this region.
38676 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38677 * @return {Roo.ContentPanel}
38679 getPanel : function(id){
38680 if(typeof id == "object"){ // must be panel obj
38683 return this.panels.get(id);
38687 * Returns this regions position (north/south/east/west/center).
38690 getPosition: function(){
38691 return this.position;
38695 * Ext JS Library 1.1.1
38696 * Copyright(c) 2006-2007, Ext JS, LLC.
38698 * Originally Released Under LGPL - original licence link has changed is not relivant.
38701 * <script type="text/javascript">
38705 * @class Roo.bootstrap.layout.Region
38706 * @extends Roo.bootstrap.layout.Basic
38707 * This class represents a region in a layout manager.
38709 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38710 * @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})
38711 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
38712 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
38713 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
38714 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
38715 * @cfg {String} title The title for the region (overrides panel titles)
38716 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
38717 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38718 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
38719 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38720 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
38721 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38722 * the space available, similar to FireFox 1.5 tabs (defaults to false)
38723 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
38724 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
38725 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
38727 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
38728 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
38729 * @cfg {Boolean} disableTabTips True to disable tab tooltips
38730 * @cfg {Number} width For East/West panels
38731 * @cfg {Number} height For North/South panels
38732 * @cfg {Boolean} split To show the splitter
38733 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
38735 * @cfg {string} cls Extra CSS classes to add to region
38737 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38738 * @cfg {string} region the region that it inhabits..
38741 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
38742 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
38744 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
38745 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
38746 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
38748 Roo.bootstrap.layout.Region = function(config)
38750 this.applyConfig(config);
38752 var mgr = config.mgr;
38753 var pos = config.region;
38754 config.skipConfig = true;
38755 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38758 this.onRender(mgr.el);
38761 this.visible = true;
38762 this.collapsed = false;
38763 this.unrendered_panels = [];
38766 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38768 position: '', // set by wrapper (eg. north/south etc..)
38769 unrendered_panels : null, // unrendered panels.
38771 tabPosition : false,
38773 mgr: false, // points to 'Border'
38776 createBody : function(){
38777 /** This region's body element
38778 * @type Roo.Element */
38779 this.bodyEl = this.el.createChild({
38781 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38785 onRender: function(ctr, pos)
38787 var dh = Roo.DomHelper;
38788 /** This region's container element
38789 * @type Roo.Element */
38790 this.el = dh.append(ctr.dom, {
38792 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38794 /** This region's title element
38795 * @type Roo.Element */
38797 this.titleEl = dh.append(this.el.dom, {
38799 unselectable: "on",
38800 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38802 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38803 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38807 this.titleEl.enableDisplayMode();
38808 /** This region's title text element
38809 * @type HTMLElement */
38810 this.titleTextEl = this.titleEl.dom.firstChild;
38811 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38813 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38814 this.closeBtn.enableDisplayMode();
38815 this.closeBtn.on("click", this.closeClicked, this);
38816 this.closeBtn.hide();
38818 this.createBody(this.config);
38819 if(this.config.hideWhenEmpty){
38821 this.on("paneladded", this.validateVisibility, this);
38822 this.on("panelremoved", this.validateVisibility, this);
38824 if(this.autoScroll){
38825 this.bodyEl.setStyle("overflow", "auto");
38827 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38829 //if(c.titlebar !== false){
38830 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38831 this.titleEl.hide();
38833 this.titleEl.show();
38834 if(this.config.title){
38835 this.titleTextEl.innerHTML = this.config.title;
38839 if(this.config.collapsed){
38840 this.collapse(true);
38842 if(this.config.hidden){
38846 if (this.unrendered_panels && this.unrendered_panels.length) {
38847 for (var i =0;i< this.unrendered_panels.length; i++) {
38848 this.add(this.unrendered_panels[i]);
38850 this.unrendered_panels = null;
38856 applyConfig : function(c)
38859 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38860 var dh = Roo.DomHelper;
38861 if(c.titlebar !== false){
38862 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38863 this.collapseBtn.on("click", this.collapse, this);
38864 this.collapseBtn.enableDisplayMode();
38866 if(c.showPin === true || this.showPin){
38867 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38868 this.stickBtn.enableDisplayMode();
38869 this.stickBtn.on("click", this.expand, this);
38870 this.stickBtn.hide();
38875 /** This region's collapsed element
38876 * @type Roo.Element */
38879 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38880 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38883 if(c.floatable !== false){
38884 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38885 this.collapsedEl.on("click", this.collapseClick, this);
38888 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38889 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38890 id: "message", unselectable: "on", style:{"float":"left"}});
38891 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38893 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38894 this.expandBtn.on("click", this.expand, this);
38898 if(this.collapseBtn){
38899 this.collapseBtn.setVisible(c.collapsible == true);
38902 this.cmargins = c.cmargins || this.cmargins ||
38903 (this.position == "west" || this.position == "east" ?
38904 {top: 0, left: 2, right:2, bottom: 0} :
38905 {top: 2, left: 0, right:0, bottom: 2});
38907 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38910 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38912 this.autoScroll = c.autoScroll || false;
38917 this.duration = c.duration || .30;
38918 this.slideDuration = c.slideDuration || .45;
38923 * Returns true if this region is currently visible.
38924 * @return {Boolean}
38926 isVisible : function(){
38927 return this.visible;
38931 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38932 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
38934 //setCollapsedTitle : function(title){
38935 // title = title || " ";
38936 // if(this.collapsedTitleTextEl){
38937 // this.collapsedTitleTextEl.innerHTML = title;
38941 getBox : function(){
38943 // if(!this.collapsed){
38944 b = this.el.getBox(false, true);
38946 // b = this.collapsedEl.getBox(false, true);
38951 getMargins : function(){
38952 return this.margins;
38953 //return this.collapsed ? this.cmargins : this.margins;
38956 highlight : function(){
38957 this.el.addClass("x-layout-panel-dragover");
38960 unhighlight : function(){
38961 this.el.removeClass("x-layout-panel-dragover");
38964 updateBox : function(box)
38966 if (!this.bodyEl) {
38967 return; // not rendered yet..
38971 if(!this.collapsed){
38972 this.el.dom.style.left = box.x + "px";
38973 this.el.dom.style.top = box.y + "px";
38974 this.updateBody(box.width, box.height);
38976 this.collapsedEl.dom.style.left = box.x + "px";
38977 this.collapsedEl.dom.style.top = box.y + "px";
38978 this.collapsedEl.setSize(box.width, box.height);
38981 this.tabs.autoSizeTabs();
38985 updateBody : function(w, h)
38988 this.el.setWidth(w);
38989 w -= this.el.getBorderWidth("rl");
38990 if(this.config.adjustments){
38991 w += this.config.adjustments[0];
38994 if(h !== null && h > 0){
38995 this.el.setHeight(h);
38996 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38997 h -= this.el.getBorderWidth("tb");
38998 if(this.config.adjustments){
38999 h += this.config.adjustments[1];
39001 this.bodyEl.setHeight(h);
39003 h = this.tabs.syncHeight(h);
39006 if(this.panelSize){
39007 w = w !== null ? w : this.panelSize.width;
39008 h = h !== null ? h : this.panelSize.height;
39010 if(this.activePanel){
39011 var el = this.activePanel.getEl();
39012 w = w !== null ? w : el.getWidth();
39013 h = h !== null ? h : el.getHeight();
39014 this.panelSize = {width: w, height: h};
39015 this.activePanel.setSize(w, h);
39017 if(Roo.isIE && this.tabs){
39018 this.tabs.el.repaint();
39023 * Returns the container element for this region.
39024 * @return {Roo.Element}
39026 getEl : function(){
39031 * Hides this region.
39034 //if(!this.collapsed){
39035 this.el.dom.style.left = "-2000px";
39038 // this.collapsedEl.dom.style.left = "-2000px";
39039 // this.collapsedEl.hide();
39041 this.visible = false;
39042 this.fireEvent("visibilitychange", this, false);
39046 * Shows this region if it was previously hidden.
39049 //if(!this.collapsed){
39052 // this.collapsedEl.show();
39054 this.visible = true;
39055 this.fireEvent("visibilitychange", this, true);
39058 closeClicked : function(){
39059 if(this.activePanel){
39060 this.remove(this.activePanel);
39064 collapseClick : function(e){
39066 e.stopPropagation();
39069 e.stopPropagation();
39075 * Collapses this region.
39076 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39079 collapse : function(skipAnim, skipCheck = false){
39080 if(this.collapsed) {
39084 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39086 this.collapsed = true;
39088 this.split.el.hide();
39090 if(this.config.animate && skipAnim !== true){
39091 this.fireEvent("invalidated", this);
39092 this.animateCollapse();
39094 this.el.setLocation(-20000,-20000);
39096 this.collapsedEl.show();
39097 this.fireEvent("collapsed", this);
39098 this.fireEvent("invalidated", this);
39104 animateCollapse : function(){
39109 * Expands this region if it was previously collapsed.
39110 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39111 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39114 expand : function(e, skipAnim){
39116 e.stopPropagation();
39118 if(!this.collapsed || this.el.hasActiveFx()) {
39122 this.afterSlideIn();
39125 this.collapsed = false;
39126 if(this.config.animate && skipAnim !== true){
39127 this.animateExpand();
39131 this.split.el.show();
39133 this.collapsedEl.setLocation(-2000,-2000);
39134 this.collapsedEl.hide();
39135 this.fireEvent("invalidated", this);
39136 this.fireEvent("expanded", this);
39140 animateExpand : function(){
39144 initTabs : function()
39146 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39148 var ts = new Roo.bootstrap.panel.Tabs({
39149 el: this.bodyEl.dom,
39151 tabPosition: this.tabPosition ? this.tabPosition : 'top',
39152 disableTooltips: this.config.disableTabTips,
39153 toolbar : this.config.toolbar
39156 if(this.config.hideTabs){
39157 ts.stripWrap.setDisplayed(false);
39160 ts.resizeTabs = this.config.resizeTabs === true;
39161 ts.minTabWidth = this.config.minTabWidth || 40;
39162 ts.maxTabWidth = this.config.maxTabWidth || 250;
39163 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39164 ts.monitorResize = false;
39165 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39166 ts.bodyEl.addClass('roo-layout-tabs-body');
39167 this.panels.each(this.initPanelAsTab, this);
39170 initPanelAsTab : function(panel){
39171 var ti = this.tabs.addTab(
39175 this.config.closeOnTab && panel.isClosable(),
39178 if(panel.tabTip !== undefined){
39179 ti.setTooltip(panel.tabTip);
39181 ti.on("activate", function(){
39182 this.setActivePanel(panel);
39185 if(this.config.closeOnTab){
39186 ti.on("beforeclose", function(t, e){
39188 this.remove(panel);
39192 panel.tabItem = ti;
39197 updatePanelTitle : function(panel, title)
39199 if(this.activePanel == panel){
39200 this.updateTitle(title);
39203 var ti = this.tabs.getTab(panel.getEl().id);
39205 if(panel.tabTip !== undefined){
39206 ti.setTooltip(panel.tabTip);
39211 updateTitle : function(title){
39212 if(this.titleTextEl && !this.config.title){
39213 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
39217 setActivePanel : function(panel)
39219 panel = this.getPanel(panel);
39220 if(this.activePanel && this.activePanel != panel){
39221 if(this.activePanel.setActiveState(false) === false){
39225 this.activePanel = panel;
39226 panel.setActiveState(true);
39227 if(this.panelSize){
39228 panel.setSize(this.panelSize.width, this.panelSize.height);
39231 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39233 this.updateTitle(panel.getTitle());
39235 this.fireEvent("invalidated", this);
39237 this.fireEvent("panelactivated", this, panel);
39241 * Shows the specified panel.
39242 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39243 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39245 showPanel : function(panel)
39247 panel = this.getPanel(panel);
39250 var tab = this.tabs.getTab(panel.getEl().id);
39251 if(tab.isHidden()){
39252 this.tabs.unhideTab(tab.id);
39256 this.setActivePanel(panel);
39263 * Get the active panel for this region.
39264 * @return {Roo.ContentPanel} The active panel or null
39266 getActivePanel : function(){
39267 return this.activePanel;
39270 validateVisibility : function(){
39271 if(this.panels.getCount() < 1){
39272 this.updateTitle(" ");
39273 this.closeBtn.hide();
39276 if(!this.isVisible()){
39283 * Adds the passed ContentPanel(s) to this region.
39284 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39285 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39287 add : function(panel)
39289 if(arguments.length > 1){
39290 for(var i = 0, len = arguments.length; i < len; i++) {
39291 this.add(arguments[i]);
39296 // if we have not been rendered yet, then we can not really do much of this..
39297 if (!this.bodyEl) {
39298 this.unrendered_panels.push(panel);
39305 if(this.hasPanel(panel)){
39306 this.showPanel(panel);
39309 panel.setRegion(this);
39310 this.panels.add(panel);
39311 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39312 // sinle panel - no tab...?? would it not be better to render it with the tabs,
39313 // and hide them... ???
39314 this.bodyEl.dom.appendChild(panel.getEl().dom);
39315 if(panel.background !== true){
39316 this.setActivePanel(panel);
39318 this.fireEvent("paneladded", this, panel);
39325 this.initPanelAsTab(panel);
39329 if(panel.background !== true){
39330 this.tabs.activate(panel.getEl().id);
39332 this.fireEvent("paneladded", this, panel);
39337 * Hides the tab for the specified panel.
39338 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39340 hidePanel : function(panel){
39341 if(this.tabs && (panel = this.getPanel(panel))){
39342 this.tabs.hideTab(panel.getEl().id);
39347 * Unhides the tab for a previously hidden panel.
39348 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39350 unhidePanel : function(panel){
39351 if(this.tabs && (panel = this.getPanel(panel))){
39352 this.tabs.unhideTab(panel.getEl().id);
39356 clearPanels : function(){
39357 while(this.panels.getCount() > 0){
39358 this.remove(this.panels.first());
39363 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39364 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39365 * @param {Boolean} preservePanel Overrides the config preservePanel option
39366 * @return {Roo.ContentPanel} The panel that was removed
39368 remove : function(panel, preservePanel)
39370 panel = this.getPanel(panel);
39375 this.fireEvent("beforeremove", this, panel, e);
39376 if(e.cancel === true){
39379 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39380 var panelId = panel.getId();
39381 this.panels.removeKey(panelId);
39383 document.body.appendChild(panel.getEl().dom);
39386 this.tabs.removeTab(panel.getEl().id);
39387 }else if (!preservePanel){
39388 this.bodyEl.dom.removeChild(panel.getEl().dom);
39390 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39391 var p = this.panels.first();
39392 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39393 tempEl.appendChild(p.getEl().dom);
39394 this.bodyEl.update("");
39395 this.bodyEl.dom.appendChild(p.getEl().dom);
39397 this.updateTitle(p.getTitle());
39399 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39400 this.setActivePanel(p);
39402 panel.setRegion(null);
39403 if(this.activePanel == panel){
39404 this.activePanel = null;
39406 if(this.config.autoDestroy !== false && preservePanel !== true){
39407 try{panel.destroy();}catch(e){}
39409 this.fireEvent("panelremoved", this, panel);
39414 * Returns the TabPanel component used by this region
39415 * @return {Roo.TabPanel}
39417 getTabs : function(){
39421 createTool : function(parentEl, className){
39422 var btn = Roo.DomHelper.append(parentEl, {
39424 cls: "x-layout-tools-button",
39427 cls: "roo-layout-tools-button-inner " + className,
39431 btn.addClassOnOver("roo-layout-tools-button-over");
39436 * Ext JS Library 1.1.1
39437 * Copyright(c) 2006-2007, Ext JS, LLC.
39439 * Originally Released Under LGPL - original licence link has changed is not relivant.
39442 * <script type="text/javascript">
39448 * @class Roo.SplitLayoutRegion
39449 * @extends Roo.LayoutRegion
39450 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39452 Roo.bootstrap.layout.Split = function(config){
39453 this.cursor = config.cursor;
39454 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39457 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39459 splitTip : "Drag to resize.",
39460 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39461 useSplitTips : false,
39463 applyConfig : function(config){
39464 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39467 onRender : function(ctr,pos) {
39469 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39470 if(!this.config.split){
39475 var splitEl = Roo.DomHelper.append(ctr.dom, {
39477 id: this.el.id + "-split",
39478 cls: "roo-layout-split roo-layout-split-"+this.position,
39481 /** The SplitBar for this region
39482 * @type Roo.SplitBar */
39483 // does not exist yet...
39484 Roo.log([this.position, this.orientation]);
39486 this.split = new Roo.bootstrap.SplitBar({
39487 dragElement : splitEl,
39488 resizingElement: this.el,
39489 orientation : this.orientation
39492 this.split.on("moved", this.onSplitMove, this);
39493 this.split.useShim = this.config.useShim === true;
39494 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39495 if(this.useSplitTips){
39496 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39498 //if(config.collapsible){
39499 // this.split.el.on("dblclick", this.collapse, this);
39502 if(typeof this.config.minSize != "undefined"){
39503 this.split.minSize = this.config.minSize;
39505 if(typeof this.config.maxSize != "undefined"){
39506 this.split.maxSize = this.config.maxSize;
39508 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39509 this.hideSplitter();
39514 getHMaxSize : function(){
39515 var cmax = this.config.maxSize || 10000;
39516 var center = this.mgr.getRegion("center");
39517 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39520 getVMaxSize : function(){
39521 var cmax = this.config.maxSize || 10000;
39522 var center = this.mgr.getRegion("center");
39523 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39526 onSplitMove : function(split, newSize){
39527 this.fireEvent("resized", this, newSize);
39531 * Returns the {@link Roo.SplitBar} for this region.
39532 * @return {Roo.SplitBar}
39534 getSplitBar : function(){
39539 this.hideSplitter();
39540 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39543 hideSplitter : function(){
39545 this.split.el.setLocation(-2000,-2000);
39546 this.split.el.hide();
39552 this.split.el.show();
39554 Roo.bootstrap.layout.Split.superclass.show.call(this);
39557 beforeSlide: function(){
39558 if(Roo.isGecko){// firefox overflow auto bug workaround
39559 this.bodyEl.clip();
39561 this.tabs.bodyEl.clip();
39563 if(this.activePanel){
39564 this.activePanel.getEl().clip();
39566 if(this.activePanel.beforeSlide){
39567 this.activePanel.beforeSlide();
39573 afterSlide : function(){
39574 if(Roo.isGecko){// firefox overflow auto bug workaround
39575 this.bodyEl.unclip();
39577 this.tabs.bodyEl.unclip();
39579 if(this.activePanel){
39580 this.activePanel.getEl().unclip();
39581 if(this.activePanel.afterSlide){
39582 this.activePanel.afterSlide();
39588 initAutoHide : function(){
39589 if(this.autoHide !== false){
39590 if(!this.autoHideHd){
39591 var st = new Roo.util.DelayedTask(this.slideIn, this);
39592 this.autoHideHd = {
39593 "mouseout": function(e){
39594 if(!e.within(this.el, true)){
39598 "mouseover" : function(e){
39604 this.el.on(this.autoHideHd);
39608 clearAutoHide : function(){
39609 if(this.autoHide !== false){
39610 this.el.un("mouseout", this.autoHideHd.mouseout);
39611 this.el.un("mouseover", this.autoHideHd.mouseover);
39615 clearMonitor : function(){
39616 Roo.get(document).un("click", this.slideInIf, this);
39619 // these names are backwards but not changed for compat
39620 slideOut : function(){
39621 if(this.isSlid || this.el.hasActiveFx()){
39624 this.isSlid = true;
39625 if(this.collapseBtn){
39626 this.collapseBtn.hide();
39628 this.closeBtnState = this.closeBtn.getStyle('display');
39629 this.closeBtn.hide();
39631 this.stickBtn.show();
39634 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39635 this.beforeSlide();
39636 this.el.setStyle("z-index", 10001);
39637 this.el.slideIn(this.getSlideAnchor(), {
39638 callback: function(){
39640 this.initAutoHide();
39641 Roo.get(document).on("click", this.slideInIf, this);
39642 this.fireEvent("slideshow", this);
39649 afterSlideIn : function(){
39650 this.clearAutoHide();
39651 this.isSlid = false;
39652 this.clearMonitor();
39653 this.el.setStyle("z-index", "");
39654 if(this.collapseBtn){
39655 this.collapseBtn.show();
39657 this.closeBtn.setStyle('display', this.closeBtnState);
39659 this.stickBtn.hide();
39661 this.fireEvent("slidehide", this);
39664 slideIn : function(cb){
39665 if(!this.isSlid || this.el.hasActiveFx()){
39669 this.isSlid = false;
39670 this.beforeSlide();
39671 this.el.slideOut(this.getSlideAnchor(), {
39672 callback: function(){
39673 this.el.setLeftTop(-10000, -10000);
39675 this.afterSlideIn();
39683 slideInIf : function(e){
39684 if(!e.within(this.el)){
39689 animateCollapse : function(){
39690 this.beforeSlide();
39691 this.el.setStyle("z-index", 20000);
39692 var anchor = this.getSlideAnchor();
39693 this.el.slideOut(anchor, {
39694 callback : function(){
39695 this.el.setStyle("z-index", "");
39696 this.collapsedEl.slideIn(anchor, {duration:.3});
39698 this.el.setLocation(-10000,-10000);
39700 this.fireEvent("collapsed", this);
39707 animateExpand : function(){
39708 this.beforeSlide();
39709 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39710 this.el.setStyle("z-index", 20000);
39711 this.collapsedEl.hide({
39714 this.el.slideIn(this.getSlideAnchor(), {
39715 callback : function(){
39716 this.el.setStyle("z-index", "");
39719 this.split.el.show();
39721 this.fireEvent("invalidated", this);
39722 this.fireEvent("expanded", this);
39750 getAnchor : function(){
39751 return this.anchors[this.position];
39754 getCollapseAnchor : function(){
39755 return this.canchors[this.position];
39758 getSlideAnchor : function(){
39759 return this.sanchors[this.position];
39762 getAlignAdj : function(){
39763 var cm = this.cmargins;
39764 switch(this.position){
39780 getExpandAdj : function(){
39781 var c = this.collapsedEl, cm = this.cmargins;
39782 switch(this.position){
39784 return [-(cm.right+c.getWidth()+cm.left), 0];
39787 return [cm.right+c.getWidth()+cm.left, 0];
39790 return [0, -(cm.top+cm.bottom+c.getHeight())];
39793 return [0, cm.top+cm.bottom+c.getHeight()];
39799 * Ext JS Library 1.1.1
39800 * Copyright(c) 2006-2007, Ext JS, LLC.
39802 * Originally Released Under LGPL - original licence link has changed is not relivant.
39805 * <script type="text/javascript">
39808 * These classes are private internal classes
39810 Roo.bootstrap.layout.Center = function(config){
39811 config.region = "center";
39812 Roo.bootstrap.layout.Region.call(this, config);
39813 this.visible = true;
39814 this.minWidth = config.minWidth || 20;
39815 this.minHeight = config.minHeight || 20;
39818 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39820 // center panel can't be hidden
39824 // center panel can't be hidden
39827 getMinWidth: function(){
39828 return this.minWidth;
39831 getMinHeight: function(){
39832 return this.minHeight;
39846 Roo.bootstrap.layout.North = function(config)
39848 config.region = 'north';
39849 config.cursor = 'n-resize';
39851 Roo.bootstrap.layout.Split.call(this, config);
39855 this.split.placement = Roo.bootstrap.SplitBar.TOP;
39856 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39857 this.split.el.addClass("roo-layout-split-v");
39859 //var size = config.initialSize || config.height;
39860 //if(this.el && typeof size != "undefined"){
39861 // this.el.setHeight(size);
39864 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39866 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39869 onRender : function(ctr, pos)
39871 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39872 var size = this.config.initialSize || this.config.height;
39873 if(this.el && typeof size != "undefined"){
39874 this.el.setHeight(size);
39879 getBox : function(){
39880 if(this.collapsed){
39881 return this.collapsedEl.getBox();
39883 var box = this.el.getBox();
39885 box.height += this.split.el.getHeight();
39890 updateBox : function(box){
39891 if(this.split && !this.collapsed){
39892 box.height -= this.split.el.getHeight();
39893 this.split.el.setLeft(box.x);
39894 this.split.el.setTop(box.y+box.height);
39895 this.split.el.setWidth(box.width);
39897 if(this.collapsed){
39898 this.updateBody(box.width, null);
39900 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39908 Roo.bootstrap.layout.South = function(config){
39909 config.region = 'south';
39910 config.cursor = 's-resize';
39911 Roo.bootstrap.layout.Split.call(this, config);
39913 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39914 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39915 this.split.el.addClass("roo-layout-split-v");
39920 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39921 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39923 onRender : function(ctr, pos)
39925 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39926 var size = this.config.initialSize || this.config.height;
39927 if(this.el && typeof size != "undefined"){
39928 this.el.setHeight(size);
39933 getBox : function(){
39934 if(this.collapsed){
39935 return this.collapsedEl.getBox();
39937 var box = this.el.getBox();
39939 var sh = this.split.el.getHeight();
39946 updateBox : function(box){
39947 if(this.split && !this.collapsed){
39948 var sh = this.split.el.getHeight();
39951 this.split.el.setLeft(box.x);
39952 this.split.el.setTop(box.y-sh);
39953 this.split.el.setWidth(box.width);
39955 if(this.collapsed){
39956 this.updateBody(box.width, null);
39958 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39962 Roo.bootstrap.layout.East = function(config){
39963 config.region = "east";
39964 config.cursor = "e-resize";
39965 Roo.bootstrap.layout.Split.call(this, config);
39967 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39968 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39969 this.split.el.addClass("roo-layout-split-h");
39973 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39974 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39976 onRender : function(ctr, pos)
39978 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39979 var size = this.config.initialSize || this.config.width;
39980 if(this.el && typeof size != "undefined"){
39981 this.el.setWidth(size);
39986 getBox : function(){
39987 if(this.collapsed){
39988 return this.collapsedEl.getBox();
39990 var box = this.el.getBox();
39992 var sw = this.split.el.getWidth();
39999 updateBox : function(box){
40000 if(this.split && !this.collapsed){
40001 var sw = this.split.el.getWidth();
40003 this.split.el.setLeft(box.x);
40004 this.split.el.setTop(box.y);
40005 this.split.el.setHeight(box.height);
40008 if(this.collapsed){
40009 this.updateBody(null, box.height);
40011 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40015 Roo.bootstrap.layout.West = function(config){
40016 config.region = "west";
40017 config.cursor = "w-resize";
40019 Roo.bootstrap.layout.Split.call(this, config);
40021 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40022 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40023 this.split.el.addClass("roo-layout-split-h");
40027 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40028 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40030 onRender: function(ctr, pos)
40032 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40033 var size = this.config.initialSize || this.config.width;
40034 if(typeof size != "undefined"){
40035 this.el.setWidth(size);
40039 getBox : function(){
40040 if(this.collapsed){
40041 return this.collapsedEl.getBox();
40043 var box = this.el.getBox();
40044 if (box.width == 0) {
40045 box.width = this.config.width; // kludge?
40048 box.width += this.split.el.getWidth();
40053 updateBox : function(box){
40054 if(this.split && !this.collapsed){
40055 var sw = this.split.el.getWidth();
40057 this.split.el.setLeft(box.x+box.width);
40058 this.split.el.setTop(box.y);
40059 this.split.el.setHeight(box.height);
40061 if(this.collapsed){
40062 this.updateBody(null, box.height);
40064 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40066 });Roo.namespace("Roo.bootstrap.panel");/*
40068 * Ext JS Library 1.1.1
40069 * Copyright(c) 2006-2007, Ext JS, LLC.
40071 * Originally Released Under LGPL - original licence link has changed is not relivant.
40074 * <script type="text/javascript">
40077 * @class Roo.ContentPanel
40078 * @extends Roo.util.Observable
40079 * A basic ContentPanel element.
40080 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
40081 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
40082 * @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
40083 * @cfg {Boolean} closable True if the panel can be closed/removed
40084 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
40085 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40086 * @cfg {Toolbar} toolbar A toolbar for this panel
40087 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
40088 * @cfg {String} title The title for this panel
40089 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40090 * @cfg {String} url Calls {@link #setUrl} with this value
40091 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40092 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
40093 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
40094 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
40095 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
40096 * @cfg {Boolean} badges render the badges
40097 * @cfg {String} cls extra classes to use
40098 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40101 * Create a new ContentPanel.
40102 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40103 * @param {String/Object} config A string to set only the title or a config object
40104 * @param {String} content (optional) Set the HTML content for this panel
40105 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40107 Roo.bootstrap.panel.Content = function( config){
40109 this.tpl = config.tpl || false;
40111 var el = config.el;
40112 var content = config.content;
40114 if(config.autoCreate){ // xtype is available if this is called from factory
40117 this.el = Roo.get(el);
40118 if(!this.el && config && config.autoCreate){
40119 if(typeof config.autoCreate == "object"){
40120 if(!config.autoCreate.id){
40121 config.autoCreate.id = config.id||el;
40123 this.el = Roo.DomHelper.append(document.body,
40124 config.autoCreate, true);
40128 cls: (config.cls || '') +
40129 (config.background ? ' bg-' + config.background : '') +
40130 " roo-layout-inactive-content",
40133 if (config.iframe) {
40137 style : 'border: 0px',
40138 src : 'about:blank'
40144 elcfg.html = config.html;
40148 this.el = Roo.DomHelper.append(document.body, elcfg , true);
40149 if (config.iframe) {
40150 this.iframeEl = this.el.select('iframe',true).first();
40155 this.closable = false;
40156 this.loaded = false;
40157 this.active = false;
40160 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40162 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40164 this.wrapEl = this.el; //this.el.wrap();
40166 if (config.toolbar.items) {
40167 ti = config.toolbar.items ;
40168 delete config.toolbar.items ;
40172 this.toolbar.render(this.wrapEl, 'before');
40173 for(var i =0;i < ti.length;i++) {
40174 // Roo.log(['add child', items[i]]);
40175 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40177 this.toolbar.items = nitems;
40178 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40179 delete config.toolbar;
40183 // xtype created footer. - not sure if will work as we normally have to render first..
40184 if (this.footer && !this.footer.el && this.footer.xtype) {
40185 if (!this.wrapEl) {
40186 this.wrapEl = this.el.wrap();
40189 this.footer.container = this.wrapEl.createChild();
40191 this.footer = Roo.factory(this.footer, Roo);
40196 if(typeof config == "string"){
40197 this.title = config;
40199 Roo.apply(this, config);
40203 this.resizeEl = Roo.get(this.resizeEl, true);
40205 this.resizeEl = this.el;
40207 // handle view.xtype
40215 * Fires when this panel is activated.
40216 * @param {Roo.ContentPanel} this
40220 * @event deactivate
40221 * Fires when this panel is activated.
40222 * @param {Roo.ContentPanel} this
40224 "deactivate" : true,
40228 * Fires when this panel is resized if fitToFrame is true.
40229 * @param {Roo.ContentPanel} this
40230 * @param {Number} width The width after any component adjustments
40231 * @param {Number} height The height after any component adjustments
40237 * Fires when this tab is created
40238 * @param {Roo.ContentPanel} this
40244 * Fires when this content is scrolled
40245 * @param {Roo.ContentPanel} this
40246 * @param {Event} scrollEvent
40257 if(this.autoScroll && !this.iframe){
40258 this.resizeEl.setStyle("overflow", "auto");
40259 this.resizeEl.on('scroll', this.onScroll, this);
40261 // fix randome scrolling
40262 //this.el.on('scroll', function() {
40263 // Roo.log('fix random scolling');
40264 // this.scrollTo('top',0);
40267 content = content || this.content;
40269 this.setContent(content);
40271 if(config && config.url){
40272 this.setUrl(this.url, this.params, this.loadOnce);
40277 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40279 if (this.view && typeof(this.view.xtype) != 'undefined') {
40280 this.view.el = this.el.appendChild(document.createElement("div"));
40281 this.view = Roo.factory(this.view);
40282 this.view.render && this.view.render(false, '');
40286 this.fireEvent('render', this);
40289 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40299 /* Resize Element - use this to work out scroll etc. */
40302 setRegion : function(region){
40303 this.region = region;
40304 this.setActiveClass(region && !this.background);
40308 setActiveClass: function(state)
40311 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40312 this.el.setStyle('position','relative');
40314 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40315 this.el.setStyle('position', 'absolute');
40320 * Returns the toolbar for this Panel if one was configured.
40321 * @return {Roo.Toolbar}
40323 getToolbar : function(){
40324 return this.toolbar;
40327 setActiveState : function(active)
40329 this.active = active;
40330 this.setActiveClass(active);
40332 if(this.fireEvent("deactivate", this) === false){
40337 this.fireEvent("activate", this);
40341 * Updates this panel's element (not for iframe)
40342 * @param {String} content The new content
40343 * @param {Boolean} loadScripts (optional) true to look for and process scripts
40345 setContent : function(content, loadScripts){
40350 this.el.update(content, loadScripts);
40353 ignoreResize : function(w, h){
40354 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40357 this.lastSize = {width: w, height: h};
40362 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40363 * @return {Roo.UpdateManager} The UpdateManager
40365 getUpdateManager : function(){
40369 return this.el.getUpdateManager();
40372 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40373 * Does not work with IFRAME contents
40374 * @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:
40377 url: "your-url.php",
40378 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40379 callback: yourFunction,
40380 scope: yourObject, //(optional scope)
40383 text: "Loading...",
40389 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40390 * 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.
40391 * @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}
40392 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40393 * @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.
40394 * @return {Roo.ContentPanel} this
40402 var um = this.el.getUpdateManager();
40403 um.update.apply(um, arguments);
40409 * 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.
40410 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40411 * @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)
40412 * @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)
40413 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40415 setUrl : function(url, params, loadOnce){
40417 this.iframeEl.dom.src = url;
40421 if(this.refreshDelegate){
40422 this.removeListener("activate", this.refreshDelegate);
40424 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40425 this.on("activate", this.refreshDelegate);
40426 return this.el.getUpdateManager();
40429 _handleRefresh : function(url, params, loadOnce){
40430 if(!loadOnce || !this.loaded){
40431 var updater = this.el.getUpdateManager();
40432 updater.update(url, params, this._setLoaded.createDelegate(this));
40436 _setLoaded : function(){
40437 this.loaded = true;
40441 * Returns this panel's id
40444 getId : function(){
40449 * Returns this panel's element - used by regiosn to add.
40450 * @return {Roo.Element}
40452 getEl : function(){
40453 return this.wrapEl || this.el;
40458 adjustForComponents : function(width, height)
40460 //Roo.log('adjustForComponents ');
40461 if(this.resizeEl != this.el){
40462 width -= this.el.getFrameWidth('lr');
40463 height -= this.el.getFrameWidth('tb');
40466 var te = this.toolbar.getEl();
40467 te.setWidth(width);
40468 height -= te.getHeight();
40471 var te = this.footer.getEl();
40472 te.setWidth(width);
40473 height -= te.getHeight();
40477 if(this.adjustments){
40478 width += this.adjustments[0];
40479 height += this.adjustments[1];
40481 return {"width": width, "height": height};
40484 setSize : function(width, height){
40485 if(this.fitToFrame && !this.ignoreResize(width, height)){
40486 if(this.fitContainer && this.resizeEl != this.el){
40487 this.el.setSize(width, height);
40489 var size = this.adjustForComponents(width, height);
40491 this.iframeEl.setSize(width,height);
40494 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40495 this.fireEvent('resize', this, size.width, size.height);
40502 * Returns this panel's title
40505 getTitle : function(){
40507 if (typeof(this.title) != 'object') {
40512 for (var k in this.title) {
40513 if (!this.title.hasOwnProperty(k)) {
40517 if (k.indexOf('-') >= 0) {
40518 var s = k.split('-');
40519 for (var i = 0; i<s.length; i++) {
40520 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40523 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40530 * Set this panel's title
40531 * @param {String} title
40533 setTitle : function(title){
40534 this.title = title;
40536 this.region.updatePanelTitle(this, title);
40541 * Returns true is this panel was configured to be closable
40542 * @return {Boolean}
40544 isClosable : function(){
40545 return this.closable;
40548 beforeSlide : function(){
40550 this.resizeEl.clip();
40553 afterSlide : function(){
40555 this.resizeEl.unclip();
40559 * Force a content refresh from the URL specified in the {@link #setUrl} method.
40560 * Will fail silently if the {@link #setUrl} method has not been called.
40561 * This does not activate the panel, just updates its content.
40563 refresh : function(){
40564 if(this.refreshDelegate){
40565 this.loaded = false;
40566 this.refreshDelegate();
40571 * Destroys this panel
40573 destroy : function(){
40574 this.el.removeAllListeners();
40575 var tempEl = document.createElement("span");
40576 tempEl.appendChild(this.el.dom);
40577 tempEl.innerHTML = "";
40583 * form - if the content panel contains a form - this is a reference to it.
40584 * @type {Roo.form.Form}
40588 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40589 * This contains a reference to it.
40595 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40605 * @param {Object} cfg Xtype definition of item to add.
40609 getChildContainer: function () {
40610 return this.getEl();
40614 onScroll : function(e)
40616 this.fireEvent('scroll', this, e);
40621 var ret = new Roo.factory(cfg);
40626 if (cfg.xtype.match(/^Form$/)) {
40629 //if (this.footer) {
40630 // el = this.footer.container.insertSibling(false, 'before');
40632 el = this.el.createChild();
40635 this.form = new Roo.form.Form(cfg);
40638 if ( this.form.allItems.length) {
40639 this.form.render(el.dom);
40643 // should only have one of theses..
40644 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40645 // views.. should not be just added - used named prop 'view''
40647 cfg.el = this.el.appendChild(document.createElement("div"));
40650 var ret = new Roo.factory(cfg);
40652 ret.render && ret.render(false, ''); // render blank..
40662 * @class Roo.bootstrap.panel.Grid
40663 * @extends Roo.bootstrap.panel.Content
40665 * Create a new GridPanel.
40666 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40667 * @param {Object} config A the config object
40673 Roo.bootstrap.panel.Grid = function(config)
40677 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40678 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40680 config.el = this.wrapper;
40681 //this.el = this.wrapper;
40683 if (config.container) {
40684 // ctor'ed from a Border/panel.grid
40687 this.wrapper.setStyle("overflow", "hidden");
40688 this.wrapper.addClass('roo-grid-container');
40693 if(config.toolbar){
40694 var tool_el = this.wrapper.createChild();
40695 this.toolbar = Roo.factory(config.toolbar);
40697 if (config.toolbar.items) {
40698 ti = config.toolbar.items ;
40699 delete config.toolbar.items ;
40703 this.toolbar.render(tool_el);
40704 for(var i =0;i < ti.length;i++) {
40705 // Roo.log(['add child', items[i]]);
40706 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40708 this.toolbar.items = nitems;
40710 delete config.toolbar;
40713 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40714 config.grid.scrollBody = true;;
40715 config.grid.monitorWindowResize = false; // turn off autosizing
40716 config.grid.autoHeight = false;
40717 config.grid.autoWidth = false;
40719 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40721 if (config.background) {
40722 // render grid on panel activation (if panel background)
40723 this.on('activate', function(gp) {
40724 if (!gp.grid.rendered) {
40725 gp.grid.render(this.wrapper);
40726 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40731 this.grid.render(this.wrapper);
40732 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40735 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40736 // ??? needed ??? config.el = this.wrapper;
40741 // xtype created footer. - not sure if will work as we normally have to render first..
40742 if (this.footer && !this.footer.el && this.footer.xtype) {
40744 var ctr = this.grid.getView().getFooterPanel(true);
40745 this.footer.dataSource = this.grid.dataSource;
40746 this.footer = Roo.factory(this.footer, Roo);
40747 this.footer.render(ctr);
40757 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40758 getId : function(){
40759 return this.grid.id;
40763 * Returns the grid for this panel
40764 * @return {Roo.bootstrap.Table}
40766 getGrid : function(){
40770 setSize : function(width, height){
40771 if(!this.ignoreResize(width, height)){
40772 var grid = this.grid;
40773 var size = this.adjustForComponents(width, height);
40774 // tfoot is not a footer?
40777 var gridel = grid.getGridEl();
40778 gridel.setSize(size.width, size.height);
40780 var tbd = grid.getGridEl().select('tbody', true).first();
40781 var thd = grid.getGridEl().select('thead',true).first();
40782 var tbf= grid.getGridEl().select('tfoot', true).first();
40785 size.height -= tbf.getHeight();
40788 size.height -= thd.getHeight();
40791 tbd.setSize(size.width, size.height );
40792 // this is for the account management tab -seems to work there.
40793 var thd = grid.getGridEl().select('thead',true).first();
40795 // tbd.setSize(size.width, size.height - thd.getHeight());
40804 beforeSlide : function(){
40805 this.grid.getView().scroller.clip();
40808 afterSlide : function(){
40809 this.grid.getView().scroller.unclip();
40812 destroy : function(){
40813 this.grid.destroy();
40815 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
40820 * @class Roo.bootstrap.panel.Nest
40821 * @extends Roo.bootstrap.panel.Content
40823 * Create a new Panel, that can contain a layout.Border.
40826 * @param {Roo.BorderLayout} layout The layout for this panel
40827 * @param {String/Object} config A string to set only the title or a config object
40829 Roo.bootstrap.panel.Nest = function(config)
40831 // construct with only one argument..
40832 /* FIXME - implement nicer consturctors
40833 if (layout.layout) {
40835 layout = config.layout;
40836 delete config.layout;
40838 if (layout.xtype && !layout.getEl) {
40839 // then layout needs constructing..
40840 layout = Roo.factory(layout, Roo);
40844 config.el = config.layout.getEl();
40846 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40848 config.layout.monitorWindowResize = false; // turn off autosizing
40849 this.layout = config.layout;
40850 this.layout.getEl().addClass("roo-layout-nested-layout");
40851 this.layout.parent = this;
40858 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40860 setSize : function(width, height){
40861 if(!this.ignoreResize(width, height)){
40862 var size = this.adjustForComponents(width, height);
40863 var el = this.layout.getEl();
40864 if (size.height < 1) {
40865 el.setWidth(size.width);
40867 el.setSize(size.width, size.height);
40869 var touch = el.dom.offsetWidth;
40870 this.layout.layout();
40871 // ie requires a double layout on the first pass
40872 if(Roo.isIE && !this.initialized){
40873 this.initialized = true;
40874 this.layout.layout();
40879 // activate all subpanels if not currently active..
40881 setActiveState : function(active){
40882 this.active = active;
40883 this.setActiveClass(active);
40886 this.fireEvent("deactivate", this);
40890 this.fireEvent("activate", this);
40891 // not sure if this should happen before or after..
40892 if (!this.layout) {
40893 return; // should not happen..
40896 for (var r in this.layout.regions) {
40897 reg = this.layout.getRegion(r);
40898 if (reg.getActivePanel()) {
40899 //reg.showPanel(reg.getActivePanel()); // force it to activate..
40900 reg.setActivePanel(reg.getActivePanel());
40903 if (!reg.panels.length) {
40906 reg.showPanel(reg.getPanel(0));
40915 * Returns the nested BorderLayout for this panel
40916 * @return {Roo.BorderLayout}
40918 getLayout : function(){
40919 return this.layout;
40923 * Adds a xtype elements to the layout of the nested panel
40927 xtype : 'ContentPanel',
40934 xtype : 'NestedLayoutPanel',
40940 items : [ ... list of content panels or nested layout panels.. ]
40944 * @param {Object} cfg Xtype definition of item to add.
40946 addxtype : function(cfg) {
40947 return this.layout.addxtype(cfg);
40952 * Ext JS Library 1.1.1
40953 * Copyright(c) 2006-2007, Ext JS, LLC.
40955 * Originally Released Under LGPL - original licence link has changed is not relivant.
40958 * <script type="text/javascript">
40961 * @class Roo.TabPanel
40962 * @extends Roo.util.Observable
40963 * A lightweight tab container.
40967 // basic tabs 1, built from existing content
40968 var tabs = new Roo.TabPanel("tabs1");
40969 tabs.addTab("script", "View Script");
40970 tabs.addTab("markup", "View Markup");
40971 tabs.activate("script");
40973 // more advanced tabs, built from javascript
40974 var jtabs = new Roo.TabPanel("jtabs");
40975 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40977 // set up the UpdateManager
40978 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40979 var updater = tab2.getUpdateManager();
40980 updater.setDefaultUrl("ajax1.htm");
40981 tab2.on('activate', updater.refresh, updater, true);
40983 // Use setUrl for Ajax loading
40984 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40985 tab3.setUrl("ajax2.htm", null, true);
40988 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40991 jtabs.activate("jtabs-1");
40994 * Create a new TabPanel.
40995 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40996 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40998 Roo.bootstrap.panel.Tabs = function(config){
41000 * The container element for this TabPanel.
41001 * @type Roo.Element
41003 this.el = Roo.get(config.el);
41006 if(typeof config == "boolean"){
41007 this.tabPosition = config ? "bottom" : "top";
41009 Roo.apply(this, config);
41013 if(this.tabPosition == "bottom"){
41014 // if tabs are at the bottom = create the body first.
41015 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41016 this.el.addClass("roo-tabs-bottom");
41018 // next create the tabs holders
41020 if (this.tabPosition == "west"){
41022 var reg = this.region; // fake it..
41024 if (!reg.mgr.parent) {
41027 reg = reg.mgr.parent.region;
41029 Roo.log("got nest?");
41031 if (reg.mgr.getRegion('west')) {
41032 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41033 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41034 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41035 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41036 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41044 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41045 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41046 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41047 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41052 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41055 // finally - if tabs are at the top, then create the body last..
41056 if(this.tabPosition != "bottom"){
41057 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41058 * @type Roo.Element
41060 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41061 this.el.addClass("roo-tabs-top");
41065 this.bodyEl.setStyle("position", "relative");
41067 this.active = null;
41068 this.activateDelegate = this.activate.createDelegate(this);
41073 * Fires when the active tab changes
41074 * @param {Roo.TabPanel} this
41075 * @param {Roo.TabPanelItem} activePanel The new active tab
41079 * @event beforetabchange
41080 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41081 * @param {Roo.TabPanel} this
41082 * @param {Object} e Set cancel to true on this object to cancel the tab change
41083 * @param {Roo.TabPanelItem} tab The tab being changed to
41085 "beforetabchange" : true
41088 Roo.EventManager.onWindowResize(this.onResize, this);
41089 this.cpad = this.el.getPadding("lr");
41090 this.hiddenCount = 0;
41093 // toolbar on the tabbar support...
41094 if (this.toolbar) {
41095 alert("no toolbar support yet");
41096 this.toolbar = false;
41098 var tcfg = this.toolbar;
41099 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
41100 this.toolbar = new Roo.Toolbar(tcfg);
41101 if (Roo.isSafari) {
41102 var tbl = tcfg.container.child('table', true);
41103 tbl.setAttribute('width', '100%');
41111 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41114 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41116 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41118 tabPosition : "top",
41120 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41122 currentTabWidth : 0,
41124 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41128 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41132 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41134 preferredTabWidth : 175,
41136 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41138 resizeTabs : false,
41140 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41142 monitorResize : true,
41144 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
41146 toolbar : false, // set by caller..
41148 region : false, /// set by caller
41150 disableTooltips : true, // not used yet...
41153 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41154 * @param {String} id The id of the div to use <b>or create</b>
41155 * @param {String} text The text for the tab
41156 * @param {String} content (optional) Content to put in the TabPanelItem body
41157 * @param {Boolean} closable (optional) True to create a close icon on the tab
41158 * @return {Roo.TabPanelItem} The created TabPanelItem
41160 addTab : function(id, text, content, closable, tpl)
41162 var item = new Roo.bootstrap.panel.TabItem({
41166 closable : closable,
41169 this.addTabItem(item);
41171 item.setContent(content);
41177 * Returns the {@link Roo.TabPanelItem} with the specified id/index
41178 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41179 * @return {Roo.TabPanelItem}
41181 getTab : function(id){
41182 return this.items[id];
41186 * Hides the {@link Roo.TabPanelItem} with the specified id/index
41187 * @param {String/Number} id The id or index of the TabPanelItem to hide.
41189 hideTab : function(id){
41190 var t = this.items[id];
41193 this.hiddenCount++;
41194 this.autoSizeTabs();
41199 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41200 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41202 unhideTab : function(id){
41203 var t = this.items[id];
41205 t.setHidden(false);
41206 this.hiddenCount--;
41207 this.autoSizeTabs();
41212 * Adds an existing {@link Roo.TabPanelItem}.
41213 * @param {Roo.TabPanelItem} item The TabPanelItem to add
41215 addTabItem : function(item)
41217 this.items[item.id] = item;
41218 this.items.push(item);
41219 this.autoSizeTabs();
41220 // if(this.resizeTabs){
41221 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41222 // this.autoSizeTabs();
41224 // item.autoSize();
41229 * Removes a {@link Roo.TabPanelItem}.
41230 * @param {String/Number} id The id or index of the TabPanelItem to remove.
41232 removeTab : function(id){
41233 var items = this.items;
41234 var tab = items[id];
41235 if(!tab) { return; }
41236 var index = items.indexOf(tab);
41237 if(this.active == tab && items.length > 1){
41238 var newTab = this.getNextAvailable(index);
41243 this.stripEl.dom.removeChild(tab.pnode.dom);
41244 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41245 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41247 items.splice(index, 1);
41248 delete this.items[tab.id];
41249 tab.fireEvent("close", tab);
41250 tab.purgeListeners();
41251 this.autoSizeTabs();
41254 getNextAvailable : function(start){
41255 var items = this.items;
41257 // look for a next tab that will slide over to
41258 // replace the one being removed
41259 while(index < items.length){
41260 var item = items[++index];
41261 if(item && !item.isHidden()){
41265 // if one isn't found select the previous tab (on the left)
41268 var item = items[--index];
41269 if(item && !item.isHidden()){
41277 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41278 * @param {String/Number} id The id or index of the TabPanelItem to disable.
41280 disableTab : function(id){
41281 var tab = this.items[id];
41282 if(tab && this.active != tab){
41288 * Enables a {@link Roo.TabPanelItem} that is disabled.
41289 * @param {String/Number} id The id or index of the TabPanelItem to enable.
41291 enableTab : function(id){
41292 var tab = this.items[id];
41297 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41298 * @param {String/Number} id The id or index of the TabPanelItem to activate.
41299 * @return {Roo.TabPanelItem} The TabPanelItem.
41301 activate : function(id)
41303 //Roo.log('activite:' + id);
41305 var tab = this.items[id];
41309 if(tab == this.active || tab.disabled){
41313 this.fireEvent("beforetabchange", this, e, tab);
41314 if(e.cancel !== true && !tab.disabled){
41316 this.active.hide();
41318 this.active = this.items[id];
41319 this.active.show();
41320 this.fireEvent("tabchange", this, this.active);
41326 * Gets the active {@link Roo.TabPanelItem}.
41327 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41329 getActiveTab : function(){
41330 return this.active;
41334 * Updates the tab body element to fit the height of the container element
41335 * for overflow scrolling
41336 * @param {Number} targetHeight (optional) Override the starting height from the elements height
41338 syncHeight : function(targetHeight){
41339 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41340 var bm = this.bodyEl.getMargins();
41341 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41342 this.bodyEl.setHeight(newHeight);
41346 onResize : function(){
41347 if(this.monitorResize){
41348 this.autoSizeTabs();
41353 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41355 beginUpdate : function(){
41356 this.updating = true;
41360 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41362 endUpdate : function(){
41363 this.updating = false;
41364 this.autoSizeTabs();
41368 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41370 autoSizeTabs : function()
41372 var count = this.items.length;
41373 var vcount = count - this.hiddenCount;
41376 this.stripEl.hide();
41378 this.stripEl.show();
41381 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41386 var w = Math.max(this.el.getWidth() - this.cpad, 10);
41387 var availWidth = Math.floor(w / vcount);
41388 var b = this.stripBody;
41389 if(b.getWidth() > w){
41390 var tabs = this.items;
41391 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41392 if(availWidth < this.minTabWidth){
41393 /*if(!this.sleft){ // incomplete scrolling code
41394 this.createScrollButtons();
41397 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41400 if(this.currentTabWidth < this.preferredTabWidth){
41401 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41407 * Returns the number of tabs in this TabPanel.
41410 getCount : function(){
41411 return this.items.length;
41415 * Resizes all the tabs to the passed width
41416 * @param {Number} The new width
41418 setTabWidth : function(width){
41419 this.currentTabWidth = width;
41420 for(var i = 0, len = this.items.length; i < len; i++) {
41421 if(!this.items[i].isHidden()) {
41422 this.items[i].setWidth(width);
41428 * Destroys this TabPanel
41429 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41431 destroy : function(removeEl){
41432 Roo.EventManager.removeResizeListener(this.onResize, this);
41433 for(var i = 0, len = this.items.length; i < len; i++){
41434 this.items[i].purgeListeners();
41436 if(removeEl === true){
41437 this.el.update("");
41442 createStrip : function(container)
41444 var strip = document.createElement("nav");
41445 strip.className = Roo.bootstrap.version == 4 ?
41446 "navbar-light bg-light" :
41447 "navbar navbar-default"; //"x-tabs-wrap";
41448 container.appendChild(strip);
41452 createStripList : function(strip)
41454 // div wrapper for retard IE
41455 // returns the "tr" element.
41456 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41457 //'<div class="x-tabs-strip-wrap">'+
41458 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41459 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41460 return strip.firstChild; //.firstChild.firstChild.firstChild;
41462 createBody : function(container)
41464 var body = document.createElement("div");
41465 Roo.id(body, "tab-body");
41466 //Roo.fly(body).addClass("x-tabs-body");
41467 Roo.fly(body).addClass("tab-content");
41468 container.appendChild(body);
41471 createItemBody :function(bodyEl, id){
41472 var body = Roo.getDom(id);
41474 body = document.createElement("div");
41477 //Roo.fly(body).addClass("x-tabs-item-body");
41478 Roo.fly(body).addClass("tab-pane");
41479 bodyEl.insertBefore(body, bodyEl.firstChild);
41483 createStripElements : function(stripEl, text, closable, tpl)
41485 var td = document.createElement("li"); // was td..
41486 td.className = 'nav-item';
41488 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41491 stripEl.appendChild(td);
41493 td.className = "x-tabs-closable";
41494 if(!this.closeTpl){
41495 this.closeTpl = new Roo.Template(
41496 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41497 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41498 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41501 var el = this.closeTpl.overwrite(td, {"text": text});
41502 var close = el.getElementsByTagName("div")[0];
41503 var inner = el.getElementsByTagName("em")[0];
41504 return {"el": el, "close": close, "inner": inner};
41507 // not sure what this is..
41508 // if(!this.tabTpl){
41509 //this.tabTpl = new Roo.Template(
41510 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41511 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41513 // this.tabTpl = new Roo.Template(
41514 // '<a href="#">' +
41515 // '<span unselectable="on"' +
41516 // (this.disableTooltips ? '' : ' title="{text}"') +
41517 // ' >{text}</span></a>'
41523 var template = tpl || this.tabTpl || false;
41526 template = new Roo.Template(
41527 Roo.bootstrap.version == 4 ?
41529 '<a class="nav-link" href="#" unselectable="on"' +
41530 (this.disableTooltips ? '' : ' title="{text}"') +
41533 '<a class="nav-link" href="#">' +
41534 '<span unselectable="on"' +
41535 (this.disableTooltips ? '' : ' title="{text}"') +
41536 ' >{text}</span></a>'
41541 switch (typeof(template)) {
41545 template = new Roo.Template(template);
41551 var el = template.overwrite(td, {"text": text});
41553 var inner = el.getElementsByTagName("span")[0];
41555 return {"el": el, "inner": inner};
41563 * @class Roo.TabPanelItem
41564 * @extends Roo.util.Observable
41565 * Represents an individual item (tab plus body) in a TabPanel.
41566 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41567 * @param {String} id The id of this TabPanelItem
41568 * @param {String} text The text for the tab of this TabPanelItem
41569 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41571 Roo.bootstrap.panel.TabItem = function(config){
41573 * The {@link Roo.TabPanel} this TabPanelItem belongs to
41574 * @type Roo.TabPanel
41576 this.tabPanel = config.panel;
41578 * The id for this TabPanelItem
41581 this.id = config.id;
41583 this.disabled = false;
41585 this.text = config.text;
41587 this.loaded = false;
41588 this.closable = config.closable;
41591 * The body element for this TabPanelItem.
41592 * @type Roo.Element
41594 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41595 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41596 this.bodyEl.setStyle("display", "block");
41597 this.bodyEl.setStyle("zoom", "1");
41598 //this.hideAction();
41600 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41602 this.el = Roo.get(els.el);
41603 this.inner = Roo.get(els.inner, true);
41604 this.textEl = Roo.bootstrap.version == 4 ?
41605 this.el : Roo.get(this.el.dom.firstChild, true);
41607 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41608 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41611 // this.el.on("mousedown", this.onTabMouseDown, this);
41612 this.el.on("click", this.onTabClick, this);
41614 if(config.closable){
41615 var c = Roo.get(els.close, true);
41616 c.dom.title = this.closeText;
41617 c.addClassOnOver("close-over");
41618 c.on("click", this.closeClick, this);
41624 * Fires when this tab becomes the active tab.
41625 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41626 * @param {Roo.TabPanelItem} this
41630 * @event beforeclose
41631 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41632 * @param {Roo.TabPanelItem} this
41633 * @param {Object} e Set cancel to true on this object to cancel the close.
41635 "beforeclose": true,
41638 * Fires when this tab is closed.
41639 * @param {Roo.TabPanelItem} this
41643 * @event deactivate
41644 * Fires when this tab is no longer the active tab.
41645 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41646 * @param {Roo.TabPanelItem} this
41648 "deactivate" : true
41650 this.hidden = false;
41652 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41655 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41657 purgeListeners : function(){
41658 Roo.util.Observable.prototype.purgeListeners.call(this);
41659 this.el.removeAllListeners();
41662 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41665 this.status_node.addClass("active");
41668 this.tabPanel.stripWrap.repaint();
41670 this.fireEvent("activate", this.tabPanel, this);
41674 * Returns true if this tab is the active tab.
41675 * @return {Boolean}
41677 isActive : function(){
41678 return this.tabPanel.getActiveTab() == this;
41682 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41685 this.status_node.removeClass("active");
41687 this.fireEvent("deactivate", this.tabPanel, this);
41690 hideAction : function(){
41691 this.bodyEl.hide();
41692 this.bodyEl.setStyle("position", "absolute");
41693 this.bodyEl.setLeft("-20000px");
41694 this.bodyEl.setTop("-20000px");
41697 showAction : function(){
41698 this.bodyEl.setStyle("position", "relative");
41699 this.bodyEl.setTop("");
41700 this.bodyEl.setLeft("");
41701 this.bodyEl.show();
41705 * Set the tooltip for the tab.
41706 * @param {String} tooltip The tab's tooltip
41708 setTooltip : function(text){
41709 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41710 this.textEl.dom.qtip = text;
41711 this.textEl.dom.removeAttribute('title');
41713 this.textEl.dom.title = text;
41717 onTabClick : function(e){
41718 e.preventDefault();
41719 this.tabPanel.activate(this.id);
41722 onTabMouseDown : function(e){
41723 e.preventDefault();
41724 this.tabPanel.activate(this.id);
41727 getWidth : function(){
41728 return this.inner.getWidth();
41731 setWidth : function(width){
41732 var iwidth = width - this.linode.getPadding("lr");
41733 this.inner.setWidth(iwidth);
41734 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41735 this.linode.setWidth(width);
41739 * Show or hide the tab
41740 * @param {Boolean} hidden True to hide or false to show.
41742 setHidden : function(hidden){
41743 this.hidden = hidden;
41744 this.linode.setStyle("display", hidden ? "none" : "");
41748 * Returns true if this tab is "hidden"
41749 * @return {Boolean}
41751 isHidden : function(){
41752 return this.hidden;
41756 * Returns the text for this tab
41759 getText : function(){
41763 autoSize : function(){
41764 //this.el.beginMeasure();
41765 this.textEl.setWidth(1);
41767 * #2804 [new] Tabs in Roojs
41768 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41770 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41771 //this.el.endMeasure();
41775 * Sets the text for the tab (Note: this also sets the tooltip text)
41776 * @param {String} text The tab's text and tooltip
41778 setText : function(text){
41780 this.textEl.update(text);
41781 this.setTooltip(text);
41782 //if(!this.tabPanel.resizeTabs){
41783 // this.autoSize();
41787 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41789 activate : function(){
41790 this.tabPanel.activate(this.id);
41794 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41796 disable : function(){
41797 if(this.tabPanel.active != this){
41798 this.disabled = true;
41799 this.status_node.addClass("disabled");
41804 * Enables this TabPanelItem if it was previously disabled.
41806 enable : function(){
41807 this.disabled = false;
41808 this.status_node.removeClass("disabled");
41812 * Sets the content for this TabPanelItem.
41813 * @param {String} content The content
41814 * @param {Boolean} loadScripts true to look for and load scripts
41816 setContent : function(content, loadScripts){
41817 this.bodyEl.update(content, loadScripts);
41821 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41822 * @return {Roo.UpdateManager} The UpdateManager
41824 getUpdateManager : function(){
41825 return this.bodyEl.getUpdateManager();
41829 * Set a URL to be used to load the content for this TabPanelItem.
41830 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41831 * @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)
41832 * @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)
41833 * @return {Roo.UpdateManager} The UpdateManager
41835 setUrl : function(url, params, loadOnce){
41836 if(this.refreshDelegate){
41837 this.un('activate', this.refreshDelegate);
41839 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41840 this.on("activate", this.refreshDelegate);
41841 return this.bodyEl.getUpdateManager();
41845 _handleRefresh : function(url, params, loadOnce){
41846 if(!loadOnce || !this.loaded){
41847 var updater = this.bodyEl.getUpdateManager();
41848 updater.update(url, params, this._setLoaded.createDelegate(this));
41853 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
41854 * Will fail silently if the setUrl method has not been called.
41855 * This does not activate the panel, just updates its content.
41857 refresh : function(){
41858 if(this.refreshDelegate){
41859 this.loaded = false;
41860 this.refreshDelegate();
41865 _setLoaded : function(){
41866 this.loaded = true;
41870 closeClick : function(e){
41873 this.fireEvent("beforeclose", this, o);
41874 if(o.cancel !== true){
41875 this.tabPanel.removeTab(this.id);
41879 * The text displayed in the tooltip for the close icon.
41882 closeText : "Close this tab"
41885 * This script refer to:
41886 * Title: International Telephone Input
41887 * Author: Jack O'Connor
41888 * Code version: v12.1.12
41889 * Availability: https://github.com/jackocnr/intl-tel-input.git
41892 Roo.bootstrap.PhoneInputData = function() {
41895 "Afghanistan (افغانستان)",
41900 "Albania (Shqipëri)",
41905 "Algeria (الجزائر)",
41930 "Antigua and Barbuda",
41940 "Armenia (Հայաստան)",
41956 "Austria (Österreich)",
41961 "Azerbaijan (Azərbaycan)",
41971 "Bahrain (البحرين)",
41976 "Bangladesh (বাংলাদেশ)",
41986 "Belarus (Беларусь)",
41991 "Belgium (België)",
42021 "Bosnia and Herzegovina (Босна и Херцеговина)",
42036 "British Indian Ocean Territory",
42041 "British Virgin Islands",
42051 "Bulgaria (България)",
42061 "Burundi (Uburundi)",
42066 "Cambodia (កម្ពុជា)",
42071 "Cameroon (Cameroun)",
42080 ["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"]
42083 "Cape Verde (Kabu Verdi)",
42088 "Caribbean Netherlands",
42099 "Central African Republic (République centrafricaine)",
42119 "Christmas Island",
42125 "Cocos (Keeling) Islands",
42136 "Comoros (جزر القمر)",
42141 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42146 "Congo (Republic) (Congo-Brazzaville)",
42166 "Croatia (Hrvatska)",
42187 "Czech Republic (Česká republika)",
42192 "Denmark (Danmark)",
42207 "Dominican Republic (República Dominicana)",
42211 ["809", "829", "849"]
42229 "Equatorial Guinea (Guinea Ecuatorial)",
42249 "Falkland Islands (Islas Malvinas)",
42254 "Faroe Islands (Føroyar)",
42275 "French Guiana (Guyane française)",
42280 "French Polynesia (Polynésie française)",
42295 "Georgia (საქართველო)",
42300 "Germany (Deutschland)",
42320 "Greenland (Kalaallit Nunaat)",
42357 "Guinea-Bissau (Guiné Bissau)",
42382 "Hungary (Magyarország)",
42387 "Iceland (Ísland)",
42407 "Iraq (العراق)",
42423 "Israel (ישראל)",
42450 "Jordan (الأردن)",
42455 "Kazakhstan (Казахстан)",
42476 "Kuwait (الكويت)",
42481 "Kyrgyzstan (Кыргызстан)",
42491 "Latvia (Latvija)",
42496 "Lebanon (لبنان)",
42511 "Libya (ليبيا)",
42521 "Lithuania (Lietuva)",
42536 "Macedonia (FYROM) (Македонија)",
42541 "Madagascar (Madagasikara)",
42571 "Marshall Islands",
42581 "Mauritania (موريتانيا)",
42586 "Mauritius (Moris)",
42607 "Moldova (Republica Moldova)",
42617 "Mongolia (Монгол)",
42622 "Montenegro (Crna Gora)",
42632 "Morocco (المغرب)",
42638 "Mozambique (Moçambique)",
42643 "Myanmar (Burma) (မြန်မာ)",
42648 "Namibia (Namibië)",
42663 "Netherlands (Nederland)",
42668 "New Caledonia (Nouvelle-Calédonie)",
42703 "North Korea (조선 민주주의 인민 공화국)",
42708 "Northern Mariana Islands",
42724 "Pakistan (پاکستان)",
42734 "Palestine (فلسطين)",
42744 "Papua New Guinea",
42786 "Réunion (La Réunion)",
42792 "Romania (România)",
42808 "Saint Barthélemy",
42819 "Saint Kitts and Nevis",
42829 "Saint Martin (Saint-Martin (partie française))",
42835 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42840 "Saint Vincent and the Grenadines",
42855 "São Tomé and Príncipe (São Tomé e Príncipe)",
42860 "Saudi Arabia (المملكة العربية السعودية)",
42865 "Senegal (Sénégal)",
42895 "Slovakia (Slovensko)",
42900 "Slovenia (Slovenija)",
42910 "Somalia (Soomaaliya)",
42920 "South Korea (대한민국)",
42925 "South Sudan (جنوب السودان)",
42935 "Sri Lanka (ශ්රී ලංකාව)",
42940 "Sudan (السودان)",
42950 "Svalbard and Jan Mayen",
42961 "Sweden (Sverige)",
42966 "Switzerland (Schweiz)",
42971 "Syria (سوريا)",
43016 "Trinidad and Tobago",
43021 "Tunisia (تونس)",
43026 "Turkey (Türkiye)",
43036 "Turks and Caicos Islands",
43046 "U.S. Virgin Islands",
43056 "Ukraine (Україна)",
43061 "United Arab Emirates (الإمارات العربية المتحدة)",
43083 "Uzbekistan (Oʻzbekiston)",
43093 "Vatican City (Città del Vaticano)",
43104 "Vietnam (Việt Nam)",
43109 "Wallis and Futuna (Wallis-et-Futuna)",
43114 "Western Sahara (الصحراء الغربية)",
43120 "Yemen (اليمن)",
43144 * This script refer to:
43145 * Title: International Telephone Input
43146 * Author: Jack O'Connor
43147 * Code version: v12.1.12
43148 * Availability: https://github.com/jackocnr/intl-tel-input.git
43152 * @class Roo.bootstrap.PhoneInput
43153 * @extends Roo.bootstrap.TriggerField
43154 * An input with International dial-code selection
43156 * @cfg {String} defaultDialCode default '+852'
43157 * @cfg {Array} preferedCountries default []
43160 * Create a new PhoneInput.
43161 * @param {Object} config Configuration options
43164 Roo.bootstrap.PhoneInput = function(config) {
43165 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43168 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43170 listWidth: undefined,
43172 selectedClass: 'active',
43174 invalidClass : "has-warning",
43176 validClass: 'has-success',
43178 allowed: '0123456789',
43183 * @cfg {String} defaultDialCode The default dial code when initializing the input
43185 defaultDialCode: '+852',
43188 * @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
43190 preferedCountries: false,
43192 getAutoCreate : function()
43194 var data = Roo.bootstrap.PhoneInputData();
43195 var align = this.labelAlign || this.parentLabelAlign();
43198 this.allCountries = [];
43199 this.dialCodeMapping = [];
43201 for (var i = 0; i < data.length; i++) {
43203 this.allCountries[i] = {
43207 priority: c[3] || 0,
43208 areaCodes: c[4] || null
43210 this.dialCodeMapping[c[2]] = {
43213 priority: c[3] || 0,
43214 areaCodes: c[4] || null
43226 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43227 maxlength: this.max_length,
43228 cls : 'form-control tel-input',
43229 autocomplete: 'new-password'
43232 var hiddenInput = {
43235 cls: 'hidden-tel-input'
43239 hiddenInput.name = this.name;
43242 if (this.disabled) {
43243 input.disabled = true;
43246 var flag_container = {
43263 cls: this.hasFeedback ? 'has-feedback' : '',
43269 cls: 'dial-code-holder',
43276 cls: 'roo-select2-container input-group',
43283 if (this.fieldLabel.length) {
43286 tooltip: 'This field is required'
43292 cls: 'control-label',
43298 html: this.fieldLabel
43301 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43307 if(this.indicatorpos == 'right') {
43308 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43315 if(align == 'left') {
43323 if(this.labelWidth > 12){
43324 label.style = "width: " + this.labelWidth + 'px';
43326 if(this.labelWidth < 13 && this.labelmd == 0){
43327 this.labelmd = this.labelWidth;
43329 if(this.labellg > 0){
43330 label.cls += ' col-lg-' + this.labellg;
43331 input.cls += ' col-lg-' + (12 - this.labellg);
43333 if(this.labelmd > 0){
43334 label.cls += ' col-md-' + this.labelmd;
43335 container.cls += ' col-md-' + (12 - this.labelmd);
43337 if(this.labelsm > 0){
43338 label.cls += ' col-sm-' + this.labelsm;
43339 container.cls += ' col-sm-' + (12 - this.labelsm);
43341 if(this.labelxs > 0){
43342 label.cls += ' col-xs-' + this.labelxs;
43343 container.cls += ' col-xs-' + (12 - this.labelxs);
43353 var settings = this;
43355 ['xs','sm','md','lg'].map(function(size){
43356 if (settings[size]) {
43357 cfg.cls += ' col-' + size + '-' + settings[size];
43361 this.store = new Roo.data.Store({
43362 proxy : new Roo.data.MemoryProxy({}),
43363 reader : new Roo.data.JsonReader({
43374 'name' : 'dialCode',
43378 'name' : 'priority',
43382 'name' : 'areaCodes',
43389 if(!this.preferedCountries) {
43390 this.preferedCountries = [
43397 var p = this.preferedCountries.reverse();
43400 for (var i = 0; i < p.length; i++) {
43401 for (var j = 0; j < this.allCountries.length; j++) {
43402 if(this.allCountries[j].iso2 == p[i]) {
43403 var t = this.allCountries[j];
43404 this.allCountries.splice(j,1);
43405 this.allCountries.unshift(t);
43411 this.store.proxy.data = {
43413 data: this.allCountries
43419 initEvents : function()
43422 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43424 this.indicator = this.indicatorEl();
43425 this.flag = this.flagEl();
43426 this.dialCodeHolder = this.dialCodeHolderEl();
43428 this.trigger = this.el.select('div.flag-box',true).first();
43429 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43434 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43435 _this.list.setWidth(lw);
43438 this.list.on('mouseover', this.onViewOver, this);
43439 this.list.on('mousemove', this.onViewMove, this);
43440 this.inputEl().on("keyup", this.onKeyUp, this);
43441 this.inputEl().on("keypress", this.onKeyPress, this);
43443 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43445 this.view = new Roo.View(this.list, this.tpl, {
43446 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43449 this.view.on('click', this.onViewClick, this);
43450 this.setValue(this.defaultDialCode);
43453 onTriggerClick : function(e)
43455 Roo.log('trigger click');
43460 if(this.isExpanded()){
43462 this.hasFocus = false;
43464 this.store.load({});
43465 this.hasFocus = true;
43470 isExpanded : function()
43472 return this.list.isVisible();
43475 collapse : function()
43477 if(!this.isExpanded()){
43481 Roo.get(document).un('mousedown', this.collapseIf, this);
43482 Roo.get(document).un('mousewheel', this.collapseIf, this);
43483 this.fireEvent('collapse', this);
43487 expand : function()
43491 if(this.isExpanded() || !this.hasFocus){
43495 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43496 this.list.setWidth(lw);
43499 this.restrictHeight();
43501 Roo.get(document).on('mousedown', this.collapseIf, this);
43502 Roo.get(document).on('mousewheel', this.collapseIf, this);
43504 this.fireEvent('expand', this);
43507 restrictHeight : function()
43509 this.list.alignTo(this.inputEl(), this.listAlign);
43510 this.list.alignTo(this.inputEl(), this.listAlign);
43513 onViewOver : function(e, t)
43515 if(this.inKeyMode){
43518 var item = this.view.findItemFromChild(t);
43521 var index = this.view.indexOf(item);
43522 this.select(index, false);
43527 onViewClick : function(view, doFocus, el, e)
43529 var index = this.view.getSelectedIndexes()[0];
43531 var r = this.store.getAt(index);
43534 this.onSelect(r, index);
43536 if(doFocus !== false && !this.blockFocus){
43537 this.inputEl().focus();
43541 onViewMove : function(e, t)
43543 this.inKeyMode = false;
43546 select : function(index, scrollIntoView)
43548 this.selectedIndex = index;
43549 this.view.select(index);
43550 if(scrollIntoView !== false){
43551 var el = this.view.getNode(index);
43553 this.list.scrollChildIntoView(el, false);
43558 createList : function()
43560 this.list = Roo.get(document.body).createChild({
43562 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43563 style: 'display:none'
43566 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43569 collapseIf : function(e)
43571 var in_combo = e.within(this.el);
43572 var in_list = e.within(this.list);
43573 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43575 if (in_combo || in_list || is_list) {
43581 onSelect : function(record, index)
43583 if(this.fireEvent('beforeselect', this, record, index) !== false){
43585 this.setFlagClass(record.data.iso2);
43586 this.setDialCode(record.data.dialCode);
43587 this.hasFocus = false;
43589 this.fireEvent('select', this, record, index);
43593 flagEl : function()
43595 var flag = this.el.select('div.flag',true).first();
43602 dialCodeHolderEl : function()
43604 var d = this.el.select('input.dial-code-holder',true).first();
43611 setDialCode : function(v)
43613 this.dialCodeHolder.dom.value = '+'+v;
43616 setFlagClass : function(n)
43618 this.flag.dom.className = 'flag '+n;
43621 getValue : function()
43623 var v = this.inputEl().getValue();
43624 if(this.dialCodeHolder) {
43625 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43630 setValue : function(v)
43632 var d = this.getDialCode(v);
43634 //invalid dial code
43635 if(v.length == 0 || !d || d.length == 0) {
43637 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43638 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43644 this.setFlagClass(this.dialCodeMapping[d].iso2);
43645 this.setDialCode(d);
43646 this.inputEl().dom.value = v.replace('+'+d,'');
43647 this.hiddenEl().dom.value = this.getValue();
43652 getDialCode : function(v)
43656 if (v.length == 0) {
43657 return this.dialCodeHolder.dom.value;
43661 if (v.charAt(0) != "+") {
43664 var numericChars = "";
43665 for (var i = 1; i < v.length; i++) {
43666 var c = v.charAt(i);
43669 if (this.dialCodeMapping[numericChars]) {
43670 dialCode = v.substr(1, i);
43672 if (numericChars.length == 4) {
43682 this.setValue(this.defaultDialCode);
43686 hiddenEl : function()
43688 return this.el.select('input.hidden-tel-input',true).first();
43691 // after setting val
43692 onKeyUp : function(e){
43693 this.setValue(this.getValue());
43696 onKeyPress : function(e){
43697 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43704 * @class Roo.bootstrap.MoneyField
43705 * @extends Roo.bootstrap.ComboBox
43706 * Bootstrap MoneyField class
43709 * Create a new MoneyField.
43710 * @param {Object} config Configuration options
43713 Roo.bootstrap.MoneyField = function(config) {
43715 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43719 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43722 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43724 allowDecimals : true,
43726 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43728 decimalSeparator : ".",
43730 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43732 decimalPrecision : 0,
43734 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43736 allowNegative : true,
43738 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43742 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43744 minValue : Number.NEGATIVE_INFINITY,
43746 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43748 maxValue : Number.MAX_VALUE,
43750 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43752 minText : "The minimum value for this field is {0}",
43754 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43756 maxText : "The maximum value for this field is {0}",
43758 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
43759 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43761 nanText : "{0} is not a valid number",
43763 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43767 * @cfg {String} defaults currency of the MoneyField
43768 * value should be in lkey
43770 defaultCurrency : false,
43772 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43774 thousandsDelimiter : false,
43776 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43787 getAutoCreate : function()
43789 var align = this.labelAlign || this.parentLabelAlign();
43801 cls : 'form-control roo-money-amount-input',
43802 autocomplete: 'new-password'
43805 var hiddenInput = {
43809 cls: 'hidden-number-input'
43812 if(this.max_length) {
43813 input.maxlength = this.max_length;
43817 hiddenInput.name = this.name;
43820 if (this.disabled) {
43821 input.disabled = true;
43824 var clg = 12 - this.inputlg;
43825 var cmd = 12 - this.inputmd;
43826 var csm = 12 - this.inputsm;
43827 var cxs = 12 - this.inputxs;
43831 cls : 'row roo-money-field',
43835 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43839 cls: 'roo-select2-container input-group',
43843 cls : 'form-control roo-money-currency-input',
43844 autocomplete: 'new-password',
43846 name : this.currencyName
43850 cls : 'input-group-addon',
43864 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43868 cls: this.hasFeedback ? 'has-feedback' : '',
43879 if (this.fieldLabel.length) {
43882 tooltip: 'This field is required'
43888 cls: 'control-label',
43894 html: this.fieldLabel
43897 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43903 if(this.indicatorpos == 'right') {
43904 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43911 if(align == 'left') {
43919 if(this.labelWidth > 12){
43920 label.style = "width: " + this.labelWidth + 'px';
43922 if(this.labelWidth < 13 && this.labelmd == 0){
43923 this.labelmd = this.labelWidth;
43925 if(this.labellg > 0){
43926 label.cls += ' col-lg-' + this.labellg;
43927 input.cls += ' col-lg-' + (12 - this.labellg);
43929 if(this.labelmd > 0){
43930 label.cls += ' col-md-' + this.labelmd;
43931 container.cls += ' col-md-' + (12 - this.labelmd);
43933 if(this.labelsm > 0){
43934 label.cls += ' col-sm-' + this.labelsm;
43935 container.cls += ' col-sm-' + (12 - this.labelsm);
43937 if(this.labelxs > 0){
43938 label.cls += ' col-xs-' + this.labelxs;
43939 container.cls += ' col-xs-' + (12 - this.labelxs);
43950 var settings = this;
43952 ['xs','sm','md','lg'].map(function(size){
43953 if (settings[size]) {
43954 cfg.cls += ' col-' + size + '-' + settings[size];
43961 initEvents : function()
43963 this.indicator = this.indicatorEl();
43965 this.initCurrencyEvent();
43967 this.initNumberEvent();
43970 initCurrencyEvent : function()
43973 throw "can not find store for combo";
43976 this.store = Roo.factory(this.store, Roo.data);
43977 this.store.parent = this;
43981 this.triggerEl = this.el.select('.input-group-addon', true).first();
43983 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43988 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43989 _this.list.setWidth(lw);
43992 this.list.on('mouseover', this.onViewOver, this);
43993 this.list.on('mousemove', this.onViewMove, this);
43994 this.list.on('scroll', this.onViewScroll, this);
43997 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44000 this.view = new Roo.View(this.list, this.tpl, {
44001 singleSelect:true, store: this.store, selectedClass: this.selectedClass
44004 this.view.on('click', this.onViewClick, this);
44006 this.store.on('beforeload', this.onBeforeLoad, this);
44007 this.store.on('load', this.onLoad, this);
44008 this.store.on('loadexception', this.onLoadException, this);
44010 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44011 "up" : function(e){
44012 this.inKeyMode = true;
44016 "down" : function(e){
44017 if(!this.isExpanded()){
44018 this.onTriggerClick();
44020 this.inKeyMode = true;
44025 "enter" : function(e){
44028 if(this.fireEvent("specialkey", this, e)){
44029 this.onViewClick(false);
44035 "esc" : function(e){
44039 "tab" : function(e){
44042 if(this.fireEvent("specialkey", this, e)){
44043 this.onViewClick(false);
44051 doRelay : function(foo, bar, hname){
44052 if(hname == 'down' || this.scope.isExpanded()){
44053 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44061 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44065 initNumberEvent : function(e)
44067 this.inputEl().on("keydown" , this.fireKey, this);
44068 this.inputEl().on("focus", this.onFocus, this);
44069 this.inputEl().on("blur", this.onBlur, this);
44071 this.inputEl().relayEvent('keyup', this);
44073 if(this.indicator){
44074 this.indicator.addClass('invisible');
44077 this.originalValue = this.getValue();
44079 if(this.validationEvent == 'keyup'){
44080 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44081 this.inputEl().on('keyup', this.filterValidation, this);
44083 else if(this.validationEvent !== false){
44084 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44087 if(this.selectOnFocus){
44088 this.on("focus", this.preFocus, this);
44091 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44092 this.inputEl().on("keypress", this.filterKeys, this);
44094 this.inputEl().relayEvent('keypress', this);
44097 var allowed = "0123456789";
44099 if(this.allowDecimals){
44100 allowed += this.decimalSeparator;
44103 if(this.allowNegative){
44107 if(this.thousandsDelimiter) {
44111 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44113 var keyPress = function(e){
44115 var k = e.getKey();
44117 var c = e.getCharCode();
44120 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44121 allowed.indexOf(String.fromCharCode(c)) === -1
44127 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44131 if(allowed.indexOf(String.fromCharCode(c)) === -1){
44136 this.inputEl().on("keypress", keyPress, this);
44140 onTriggerClick : function(e)
44147 this.loadNext = false;
44149 if(this.isExpanded()){
44154 this.hasFocus = true;
44156 if(this.triggerAction == 'all') {
44157 this.doQuery(this.allQuery, true);
44161 this.doQuery(this.getRawValue());
44164 getCurrency : function()
44166 var v = this.currencyEl().getValue();
44171 restrictHeight : function()
44173 this.list.alignTo(this.currencyEl(), this.listAlign);
44174 this.list.alignTo(this.currencyEl(), this.listAlign);
44177 onViewClick : function(view, doFocus, el, e)
44179 var index = this.view.getSelectedIndexes()[0];
44181 var r = this.store.getAt(index);
44184 this.onSelect(r, index);
44188 onSelect : function(record, index){
44190 if(this.fireEvent('beforeselect', this, record, index) !== false){
44192 this.setFromCurrencyData(index > -1 ? record.data : false);
44196 this.fireEvent('select', this, record, index);
44200 setFromCurrencyData : function(o)
44204 this.lastCurrency = o;
44206 if (this.currencyField) {
44207 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44209 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
44212 this.lastSelectionText = currency;
44214 //setting default currency
44215 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44216 this.setCurrency(this.defaultCurrency);
44220 this.setCurrency(currency);
44223 setFromData : function(o)
44227 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44229 this.setFromCurrencyData(c);
44234 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44236 Roo.log('no value set for '+ (this.name ? this.name : this.id));
44239 this.setValue(value);
44243 setCurrency : function(v)
44245 this.currencyValue = v;
44248 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44253 setValue : function(v)
44255 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44261 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44263 this.inputEl().dom.value = (v == '') ? '' :
44264 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44266 if(!this.allowZero && v === '0') {
44267 this.hiddenEl().dom.value = '';
44268 this.inputEl().dom.value = '';
44275 getRawValue : function()
44277 var v = this.inputEl().getValue();
44282 getValue : function()
44284 return this.fixPrecision(this.parseValue(this.getRawValue()));
44287 parseValue : function(value)
44289 if(this.thousandsDelimiter) {
44291 r = new RegExp(",", "g");
44292 value = value.replace(r, "");
44295 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44296 return isNaN(value) ? '' : value;
44300 fixPrecision : function(value)
44302 if(this.thousandsDelimiter) {
44304 r = new RegExp(",", "g");
44305 value = value.replace(r, "");
44308 var nan = isNaN(value);
44310 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44311 return nan ? '' : value;
44313 return parseFloat(value).toFixed(this.decimalPrecision);
44316 decimalPrecisionFcn : function(v)
44318 return Math.floor(v);
44321 validateValue : function(value)
44323 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44327 var num = this.parseValue(value);
44330 this.markInvalid(String.format(this.nanText, value));
44334 if(num < this.minValue){
44335 this.markInvalid(String.format(this.minText, this.minValue));
44339 if(num > this.maxValue){
44340 this.markInvalid(String.format(this.maxText, this.maxValue));
44347 validate : function()
44349 if(this.disabled || this.allowBlank){
44354 var currency = this.getCurrency();
44356 if(this.validateValue(this.getRawValue()) && currency.length){
44361 this.markInvalid();
44365 getName: function()
44370 beforeBlur : function()
44376 var v = this.parseValue(this.getRawValue());
44383 onBlur : function()
44387 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44388 //this.el.removeClass(this.focusClass);
44391 this.hasFocus = false;
44393 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44397 var v = this.getValue();
44399 if(String(v) !== String(this.startValue)){
44400 this.fireEvent('change', this, v, this.startValue);
44403 this.fireEvent("blur", this);
44406 inputEl : function()
44408 return this.el.select('.roo-money-amount-input', true).first();
44411 currencyEl : function()
44413 return this.el.select('.roo-money-currency-input', true).first();
44416 hiddenEl : function()
44418 return this.el.select('input.hidden-number-input',true).first();
44422 * @class Roo.bootstrap.BezierSignature
44423 * @extends Roo.bootstrap.Component
44424 * Bootstrap BezierSignature class
44425 * This script refer to:
44426 * Title: Signature Pad
44428 * Availability: https://github.com/szimek/signature_pad
44431 * Create a new BezierSignature
44432 * @param {Object} config The config object
44435 Roo.bootstrap.BezierSignature = function(config){
44436 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44442 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44449 mouse_btn_down: true,
44452 * @cfg {int} canvas height
44454 canvas_height: '200px',
44457 * @cfg {float|function} Radius of a single dot.
44462 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44467 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44472 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44477 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44482 * @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.
44484 bg_color: 'rgba(0, 0, 0, 0)',
44487 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44489 dot_color: 'black',
44492 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44494 velocity_filter_weight: 0.7,
44497 * @cfg {function} Callback when stroke begin.
44502 * @cfg {function} Callback when stroke end.
44506 getAutoCreate : function()
44508 var cls = 'roo-signature column';
44511 cls += ' ' + this.cls;
44521 for(var i = 0; i < col_sizes.length; i++) {
44522 if(this[col_sizes[i]]) {
44523 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44533 cls: 'roo-signature-body',
44537 cls: 'roo-signature-body-canvas',
44538 height: this.canvas_height,
44539 width: this.canvas_width
44546 style: 'display: none'
44554 initEvents: function()
44556 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44558 var canvas = this.canvasEl();
44560 // mouse && touch event swapping...
44561 canvas.dom.style.touchAction = 'none';
44562 canvas.dom.style.msTouchAction = 'none';
44564 this.mouse_btn_down = false;
44565 canvas.on('mousedown', this._handleMouseDown, this);
44566 canvas.on('mousemove', this._handleMouseMove, this);
44567 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44569 if (window.PointerEvent) {
44570 canvas.on('pointerdown', this._handleMouseDown, this);
44571 canvas.on('pointermove', this._handleMouseMove, this);
44572 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44575 if ('ontouchstart' in window) {
44576 canvas.on('touchstart', this._handleTouchStart, this);
44577 canvas.on('touchmove', this._handleTouchMove, this);
44578 canvas.on('touchend', this._handleTouchEnd, this);
44581 Roo.EventManager.onWindowResize(this.resize, this, true);
44583 // file input event
44584 this.fileEl().on('change', this.uploadImage, this);
44591 resize: function(){
44593 var canvas = this.canvasEl().dom;
44594 var ctx = this.canvasElCtx();
44595 var img_data = false;
44597 if(canvas.width > 0) {
44598 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44600 // setting canvas width will clean img data
44603 var style = window.getComputedStyle ?
44604 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44606 var padding_left = parseInt(style.paddingLeft) || 0;
44607 var padding_right = parseInt(style.paddingRight) || 0;
44609 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44612 ctx.putImageData(img_data, 0, 0);
44616 _handleMouseDown: function(e)
44618 if (e.browserEvent.which === 1) {
44619 this.mouse_btn_down = true;
44620 this.strokeBegin(e);
44624 _handleMouseMove: function (e)
44626 if (this.mouse_btn_down) {
44627 this.strokeMoveUpdate(e);
44631 _handleMouseUp: function (e)
44633 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44634 this.mouse_btn_down = false;
44639 _handleTouchStart: function (e) {
44641 e.preventDefault();
44642 if (e.browserEvent.targetTouches.length === 1) {
44643 // var touch = e.browserEvent.changedTouches[0];
44644 // this.strokeBegin(touch);
44646 this.strokeBegin(e); // assume e catching the correct xy...
44650 _handleTouchMove: function (e) {
44651 e.preventDefault();
44652 // var touch = event.targetTouches[0];
44653 // _this._strokeMoveUpdate(touch);
44654 this.strokeMoveUpdate(e);
44657 _handleTouchEnd: function (e) {
44658 var wasCanvasTouched = e.target === this.canvasEl().dom;
44659 if (wasCanvasTouched) {
44660 e.preventDefault();
44661 // var touch = event.changedTouches[0];
44662 // _this._strokeEnd(touch);
44667 reset: function () {
44668 this._lastPoints = [];
44669 this._lastVelocity = 0;
44670 this._lastWidth = (this.min_width + this.max_width) / 2;
44671 this.canvasElCtx().fillStyle = this.dot_color;
44674 strokeMoveUpdate: function(e)
44676 this.strokeUpdate(e);
44678 if (this.throttle) {
44679 this.throttleStroke(this.strokeUpdate, this.throttle);
44682 this.strokeUpdate(e);
44686 strokeBegin: function(e)
44688 var newPointGroup = {
44689 color: this.dot_color,
44693 if (typeof this.onBegin === 'function') {
44697 this.curve_data.push(newPointGroup);
44699 this.strokeUpdate(e);
44702 strokeUpdate: function(e)
44704 var rect = this.canvasEl().dom.getBoundingClientRect();
44705 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44706 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44707 var lastPoints = lastPointGroup.points;
44708 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44709 var isLastPointTooClose = lastPoint
44710 ? point.distanceTo(lastPoint) <= this.min_distance
44712 var color = lastPointGroup.color;
44713 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44714 var curve = this.addPoint(point);
44716 this.drawDot({color: color, point: point});
44719 this.drawCurve({color: color, curve: curve});
44729 strokeEnd: function(e)
44731 this.strokeUpdate(e);
44732 if (typeof this.onEnd === 'function') {
44737 addPoint: function (point) {
44738 var _lastPoints = this._lastPoints;
44739 _lastPoints.push(point);
44740 if (_lastPoints.length > 2) {
44741 if (_lastPoints.length === 3) {
44742 _lastPoints.unshift(_lastPoints[0]);
44744 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44745 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44746 _lastPoints.shift();
44752 calculateCurveWidths: function (startPoint, endPoint) {
44753 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44754 (1 - this.velocity_filter_weight) * this._lastVelocity;
44756 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44759 start: this._lastWidth
44762 this._lastVelocity = velocity;
44763 this._lastWidth = newWidth;
44767 drawDot: function (_a) {
44768 var color = _a.color, point = _a.point;
44769 var ctx = this.canvasElCtx();
44770 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44772 this.drawCurveSegment(point.x, point.y, width);
44774 ctx.fillStyle = color;
44778 drawCurve: function (_a) {
44779 var color = _a.color, curve = _a.curve;
44780 var ctx = this.canvasElCtx();
44781 var widthDelta = curve.endWidth - curve.startWidth;
44782 var drawSteps = Math.floor(curve.length()) * 2;
44784 ctx.fillStyle = color;
44785 for (var i = 0; i < drawSteps; i += 1) {
44786 var t = i / drawSteps;
44792 var x = uuu * curve.startPoint.x;
44793 x += 3 * uu * t * curve.control1.x;
44794 x += 3 * u * tt * curve.control2.x;
44795 x += ttt * curve.endPoint.x;
44796 var y = uuu * curve.startPoint.y;
44797 y += 3 * uu * t * curve.control1.y;
44798 y += 3 * u * tt * curve.control2.y;
44799 y += ttt * curve.endPoint.y;
44800 var width = curve.startWidth + ttt * widthDelta;
44801 this.drawCurveSegment(x, y, width);
44807 drawCurveSegment: function (x, y, width) {
44808 var ctx = this.canvasElCtx();
44810 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44811 this.is_empty = false;
44816 var ctx = this.canvasElCtx();
44817 var canvas = this.canvasEl().dom;
44818 ctx.fillStyle = this.bg_color;
44819 ctx.clearRect(0, 0, canvas.width, canvas.height);
44820 ctx.fillRect(0, 0, canvas.width, canvas.height);
44821 this.curve_data = [];
44823 this.is_empty = true;
44828 return this.el.select('input',true).first();
44831 canvasEl: function()
44833 return this.el.select('canvas',true).first();
44836 canvasElCtx: function()
44838 return this.el.select('canvas',true).first().dom.getContext('2d');
44841 getImage: function(type)
44843 if(this.is_empty) {
44848 return this.canvasEl().dom.toDataURL('image/'+type, 1);
44851 drawFromImage: function(img_src)
44853 var img = new Image();
44855 img.onload = function(){
44856 this.canvasElCtx().drawImage(img, 0, 0);
44861 this.is_empty = false;
44864 selectImage: function()
44866 this.fileEl().dom.click();
44869 uploadImage: function(e)
44871 var reader = new FileReader();
44873 reader.onload = function(e){
44874 var img = new Image();
44875 img.onload = function(){
44877 this.canvasElCtx().drawImage(img, 0, 0);
44879 img.src = e.target.result;
44882 reader.readAsDataURL(e.target.files[0]);
44885 // Bezier Point Constructor
44886 Point: (function () {
44887 function Point(x, y, time) {
44890 this.time = time || Date.now();
44892 Point.prototype.distanceTo = function (start) {
44893 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44895 Point.prototype.equals = function (other) {
44896 return this.x === other.x && this.y === other.y && this.time === other.time;
44898 Point.prototype.velocityFrom = function (start) {
44899 return this.time !== start.time
44900 ? this.distanceTo(start) / (this.time - start.time)
44907 // Bezier Constructor
44908 Bezier: (function () {
44909 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44910 this.startPoint = startPoint;
44911 this.control2 = control2;
44912 this.control1 = control1;
44913 this.endPoint = endPoint;
44914 this.startWidth = startWidth;
44915 this.endWidth = endWidth;
44917 Bezier.fromPoints = function (points, widths, scope) {
44918 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44919 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44920 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44922 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44923 var dx1 = s1.x - s2.x;
44924 var dy1 = s1.y - s2.y;
44925 var dx2 = s2.x - s3.x;
44926 var dy2 = s2.y - s3.y;
44927 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44928 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44929 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44930 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44931 var dxm = m1.x - m2.x;
44932 var dym = m1.y - m2.y;
44933 var k = l2 / (l1 + l2);
44934 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44935 var tx = s2.x - cm.x;
44936 var ty = s2.y - cm.y;
44938 c1: new scope.Point(m1.x + tx, m1.y + ty),
44939 c2: new scope.Point(m2.x + tx, m2.y + ty)
44942 Bezier.prototype.length = function () {
44947 for (var i = 0; i <= steps; i += 1) {
44949 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44950 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44952 var xdiff = cx - px;
44953 var ydiff = cy - py;
44954 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44961 Bezier.prototype.point = function (t, start, c1, c2, end) {
44962 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44963 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44964 + (3.0 * c2 * (1.0 - t) * t * t)
44965 + (end * t * t * t);
44970 throttleStroke: function(fn, wait) {
44971 if (wait === void 0) { wait = 250; }
44973 var timeout = null;
44977 var later = function () {
44978 previous = Date.now();
44980 result = fn.apply(storedContext, storedArgs);
44982 storedContext = null;
44986 return function wrapper() {
44988 for (var _i = 0; _i < arguments.length; _i++) {
44989 args[_i] = arguments[_i];
44991 var now = Date.now();
44992 var remaining = wait - (now - previous);
44993 storedContext = this;
44995 if (remaining <= 0 || remaining > wait) {
44997 clearTimeout(timeout);
45001 result = fn.apply(storedContext, storedArgs);
45003 storedContext = null;
45007 else if (!timeout) {
45008 timeout = window.setTimeout(later, remaining);