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">
7313 * @class Roo.grid.ColumnModel
7314 * @extends Roo.util.Observable
7315 * This is the default implementation of a ColumnModel used by the Grid. It defines
7316 * the columns in the grid.
7319 var colModel = new Roo.grid.ColumnModel([
7320 {header: "Ticker", width: 60, sortable: true, locked: true},
7321 {header: "Company Name", width: 150, sortable: true},
7322 {header: "Market Cap.", width: 100, sortable: true},
7323 {header: "$ Sales", width: 100, sortable: true, renderer: money},
7324 {header: "Employees", width: 100, sortable: true, resizable: false}
7329 * The config options listed for this class are options which may appear in each
7330 * individual column definition.
7331 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7333 * @param {Object} config An Array of column config objects. See this class's
7334 * config objects for details.
7336 Roo.grid.ColumnModel = function(config){
7338 * The config passed into the constructor
7340 this.config = []; //config;
7343 // if no id, create one
7344 // if the column does not have a dataIndex mapping,
7345 // map it to the order it is in the config
7346 for(var i = 0, len = config.length; i < len; i++){
7347 this.addColumn(config[i]);
7352 * The width of columns which have no width specified (defaults to 100)
7355 this.defaultWidth = 100;
7358 * Default sortable of columns which have no sortable specified (defaults to false)
7361 this.defaultSortable = false;
7365 * @event widthchange
7366 * Fires when the width of a column changes.
7367 * @param {ColumnModel} this
7368 * @param {Number} columnIndex The column index
7369 * @param {Number} newWidth The new width
7371 "widthchange": true,
7373 * @event headerchange
7374 * Fires when the text of a header changes.
7375 * @param {ColumnModel} this
7376 * @param {Number} columnIndex The column index
7377 * @param {Number} newText The new header text
7379 "headerchange": true,
7381 * @event hiddenchange
7382 * Fires when a column is hidden or "unhidden".
7383 * @param {ColumnModel} this
7384 * @param {Number} columnIndex The column index
7385 * @param {Boolean} hidden true if hidden, false otherwise
7387 "hiddenchange": true,
7389 * @event columnmoved
7390 * Fires when a column is moved.
7391 * @param {ColumnModel} this
7392 * @param {Number} oldIndex
7393 * @param {Number} newIndex
7395 "columnmoved" : true,
7397 * @event columlockchange
7398 * Fires when a column's locked state is changed
7399 * @param {ColumnModel} this
7400 * @param {Number} colIndex
7401 * @param {Boolean} locked true if locked
7403 "columnlockchange" : true
7405 Roo.grid.ColumnModel.superclass.constructor.call(this);
7407 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7409 * @cfg {String} header The header text to display in the Grid view.
7412 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7413 * {@link Roo.data.Record} definition from which to draw the column's value. If not
7414 * specified, the column's index is used as an index into the Record's data Array.
7417 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7418 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7421 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7422 * Defaults to the value of the {@link #defaultSortable} property.
7423 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7426 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
7429 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
7432 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7435 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7438 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7439 * given the cell's data value. See {@link #setRenderer}. If not specified, the
7440 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7441 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7444 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
7447 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
7450 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
7453 * @cfg {String} cursor (Optional)
7456 * @cfg {String} tooltip (Optional)
7459 * @cfg {Number} xs (Optional)
7462 * @cfg {Number} sm (Optional)
7465 * @cfg {Number} md (Optional)
7468 * @cfg {Number} lg (Optional)
7471 * Returns the id of the column at the specified index.
7472 * @param {Number} index The column index
7473 * @return {String} the id
7475 getColumnId : function(index){
7476 return this.config[index].id;
7480 * Returns the column for a specified id.
7481 * @param {String} id The column id
7482 * @return {Object} the column
7484 getColumnById : function(id){
7485 return this.lookup[id];
7490 * Returns the column Object for a specified dataIndex.
7491 * @param {String} dataIndex The column dataIndex
7492 * @return {Object|Boolean} the column or false if not found
7494 getColumnByDataIndex: function(dataIndex){
7495 var index = this.findColumnIndex(dataIndex);
7496 return index > -1 ? this.config[index] : false;
7500 * Returns the index for a specified column id.
7501 * @param {String} id The column id
7502 * @return {Number} the index, or -1 if not found
7504 getIndexById : function(id){
7505 for(var i = 0, len = this.config.length; i < len; i++){
7506 if(this.config[i].id == id){
7514 * Returns the index for a specified column dataIndex.
7515 * @param {String} dataIndex The column dataIndex
7516 * @return {Number} the index, or -1 if not found
7519 findColumnIndex : function(dataIndex){
7520 for(var i = 0, len = this.config.length; i < len; i++){
7521 if(this.config[i].dataIndex == dataIndex){
7529 moveColumn : function(oldIndex, newIndex){
7530 var c = this.config[oldIndex];
7531 this.config.splice(oldIndex, 1);
7532 this.config.splice(newIndex, 0, c);
7533 this.dataMap = null;
7534 this.fireEvent("columnmoved", this, oldIndex, newIndex);
7537 isLocked : function(colIndex){
7538 return this.config[colIndex].locked === true;
7541 setLocked : function(colIndex, value, suppressEvent){
7542 if(this.isLocked(colIndex) == value){
7545 this.config[colIndex].locked = value;
7547 this.fireEvent("columnlockchange", this, colIndex, value);
7551 getTotalLockedWidth : function(){
7553 for(var i = 0; i < this.config.length; i++){
7554 if(this.isLocked(i) && !this.isHidden(i)){
7555 this.totalWidth += this.getColumnWidth(i);
7561 getLockedCount : function(){
7562 for(var i = 0, len = this.config.length; i < len; i++){
7563 if(!this.isLocked(i)){
7568 return this.config.length;
7572 * Returns the number of columns.
7575 getColumnCount : function(visibleOnly){
7576 if(visibleOnly === true){
7578 for(var i = 0, len = this.config.length; i < len; i++){
7579 if(!this.isHidden(i)){
7585 return this.config.length;
7589 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7590 * @param {Function} fn
7591 * @param {Object} scope (optional)
7592 * @return {Array} result
7594 getColumnsBy : function(fn, scope){
7596 for(var i = 0, len = this.config.length; i < len; i++){
7597 var c = this.config[i];
7598 if(fn.call(scope||this, c, i) === true){
7606 * Returns true if the specified column is sortable.
7607 * @param {Number} col The column index
7610 isSortable : function(col){
7611 if(typeof this.config[col].sortable == "undefined"){
7612 return this.defaultSortable;
7614 return this.config[col].sortable;
7618 * Returns the rendering (formatting) function defined for the column.
7619 * @param {Number} col The column index.
7620 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7622 getRenderer : function(col){
7623 if(!this.config[col].renderer){
7624 return Roo.grid.ColumnModel.defaultRenderer;
7626 return this.config[col].renderer;
7630 * Sets the rendering (formatting) function for a column.
7631 * @param {Number} col The column index
7632 * @param {Function} fn The function to use to process the cell's raw data
7633 * to return HTML markup for the grid view. The render function is called with
7634 * the following parameters:<ul>
7635 * <li>Data value.</li>
7636 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7637 * <li>css A CSS style string to apply to the table cell.</li>
7638 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7639 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7640 * <li>Row index</li>
7641 * <li>Column index</li>
7642 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7644 setRenderer : function(col, fn){
7645 this.config[col].renderer = fn;
7649 * Returns the width for the specified column.
7650 * @param {Number} col The column index
7653 getColumnWidth : function(col){
7654 return this.config[col].width * 1 || this.defaultWidth;
7658 * Sets the width for a column.
7659 * @param {Number} col The column index
7660 * @param {Number} width The new width
7662 setColumnWidth : function(col, width, suppressEvent){
7663 this.config[col].width = width;
7664 this.totalWidth = null;
7666 this.fireEvent("widthchange", this, col, width);
7671 * Returns the total width of all columns.
7672 * @param {Boolean} includeHidden True to include hidden column widths
7675 getTotalWidth : function(includeHidden){
7676 if(!this.totalWidth){
7677 this.totalWidth = 0;
7678 for(var i = 0, len = this.config.length; i < len; i++){
7679 if(includeHidden || !this.isHidden(i)){
7680 this.totalWidth += this.getColumnWidth(i);
7684 return this.totalWidth;
7688 * Returns the header for the specified column.
7689 * @param {Number} col The column index
7692 getColumnHeader : function(col){
7693 return this.config[col].header;
7697 * Sets the header for a column.
7698 * @param {Number} col The column index
7699 * @param {String} header The new header
7701 setColumnHeader : function(col, header){
7702 this.config[col].header = header;
7703 this.fireEvent("headerchange", this, col, header);
7707 * Returns the tooltip for the specified column.
7708 * @param {Number} col The column index
7711 getColumnTooltip : function(col){
7712 return this.config[col].tooltip;
7715 * Sets the tooltip for a column.
7716 * @param {Number} col The column index
7717 * @param {String} tooltip The new tooltip
7719 setColumnTooltip : function(col, tooltip){
7720 this.config[col].tooltip = tooltip;
7724 * Returns the dataIndex for the specified column.
7725 * @param {Number} col The column index
7728 getDataIndex : function(col){
7729 return this.config[col].dataIndex;
7733 * Sets the dataIndex for a column.
7734 * @param {Number} col The column index
7735 * @param {Number} dataIndex The new dataIndex
7737 setDataIndex : function(col, dataIndex){
7738 this.config[col].dataIndex = dataIndex;
7744 * Returns true if the cell is editable.
7745 * @param {Number} colIndex The column index
7746 * @param {Number} rowIndex The row index - this is nto actually used..?
7749 isCellEditable : function(colIndex, rowIndex){
7750 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7754 * Returns the editor defined for the cell/column.
7755 * return false or null to disable editing.
7756 * @param {Number} colIndex The column index
7757 * @param {Number} rowIndex The row index
7760 getCellEditor : function(colIndex, rowIndex){
7761 return this.config[colIndex].editor;
7765 * Sets if a column is editable.
7766 * @param {Number} col The column index
7767 * @param {Boolean} editable True if the column is editable
7769 setEditable : function(col, editable){
7770 this.config[col].editable = editable;
7775 * Returns true if the column is hidden.
7776 * @param {Number} colIndex The column index
7779 isHidden : function(colIndex){
7780 return this.config[colIndex].hidden;
7785 * Returns true if the column width cannot be changed
7787 isFixed : function(colIndex){
7788 return this.config[colIndex].fixed;
7792 * Returns true if the column can be resized
7795 isResizable : function(colIndex){
7796 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7799 * Sets if a column is hidden.
7800 * @param {Number} colIndex The column index
7801 * @param {Boolean} hidden True if the column is hidden
7803 setHidden : function(colIndex, hidden){
7804 this.config[colIndex].hidden = hidden;
7805 this.totalWidth = null;
7806 this.fireEvent("hiddenchange", this, colIndex, hidden);
7810 * Sets the editor for a column.
7811 * @param {Number} col The column index
7812 * @param {Object} editor The editor object
7814 setEditor : function(col, editor){
7815 this.config[col].editor = editor;
7818 * Add a column (experimental...) - defaults to adding to the end..
7819 * @param {Object} config
7821 addColumn : function(c)
7824 var i = this.config.length;
7827 if(typeof c.dataIndex == "undefined"){
7830 if(typeof c.renderer == "string"){
7831 c.renderer = Roo.util.Format[c.renderer];
7833 if(typeof c.id == "undefined"){
7836 if(c.editor && c.editor.xtype){
7837 c.editor = Roo.factory(c.editor, Roo.grid);
7839 if(c.editor && c.editor.isFormField){
7840 c.editor = new Roo.grid.GridEditor(c.editor);
7842 this.lookup[c.id] = c;
7847 Roo.grid.ColumnModel.defaultRenderer = function(value)
7849 if(typeof value == "object") {
7852 if(typeof value == "string" && value.length < 1){
7856 return String.format("{0}", value);
7859 // Alias for backwards compatibility
7860 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7863 * Ext JS Library 1.1.1
7864 * Copyright(c) 2006-2007, Ext JS, LLC.
7866 * Originally Released Under LGPL - original licence link has changed is not relivant.
7869 * <script type="text/javascript">
7873 * @class Roo.LoadMask
7874 * A simple utility class for generically masking elements while loading data. If the element being masked has
7875 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7876 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
7877 * element's UpdateManager load indicator and will be destroyed after the initial load.
7879 * Create a new LoadMask
7880 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7881 * @param {Object} config The config object
7883 Roo.LoadMask = function(el, config){
7884 this.el = Roo.get(el);
7885 Roo.apply(this, config);
7887 this.store.on('beforeload', this.onBeforeLoad, this);
7888 this.store.on('load', this.onLoad, this);
7889 this.store.on('loadexception', this.onLoadException, this);
7890 this.removeMask = false;
7892 var um = this.el.getUpdateManager();
7893 um.showLoadIndicator = false; // disable the default indicator
7894 um.on('beforeupdate', this.onBeforeLoad, this);
7895 um.on('update', this.onLoad, this);
7896 um.on('failure', this.onLoad, this);
7897 this.removeMask = true;
7901 Roo.LoadMask.prototype = {
7903 * @cfg {Boolean} removeMask
7904 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7905 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
7909 * The text to display in a centered loading message box (defaults to 'Loading...')
7913 * @cfg {String} msgCls
7914 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7916 msgCls : 'x-mask-loading',
7919 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7925 * Disables the mask to prevent it from being displayed
7927 disable : function(){
7928 this.disabled = true;
7932 * Enables the mask so that it can be displayed
7934 enable : function(){
7935 this.disabled = false;
7938 onLoadException : function()
7942 if (typeof(arguments[3]) != 'undefined') {
7943 Roo.MessageBox.alert("Error loading",arguments[3]);
7947 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7948 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7955 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7960 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7964 onBeforeLoad : function(){
7966 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7971 destroy : function(){
7973 this.store.un('beforeload', this.onBeforeLoad, this);
7974 this.store.un('load', this.onLoad, this);
7975 this.store.un('loadexception', this.onLoadException, this);
7977 var um = this.el.getUpdateManager();
7978 um.un('beforeupdate', this.onBeforeLoad, this);
7979 um.un('update', this.onLoad, this);
7980 um.un('failure', this.onLoad, this);
7991 * @class Roo.bootstrap.Table
7992 * @extends Roo.bootstrap.Component
7993 * Bootstrap Table class. This class represents the primary interface of a component based grid control.
7994 * Similar to Roo.grid.Grid
7996 var table = Roo.factory({
7998 xns : Roo.bootstrap,
7999 autoSizeColumns: true,
8006 sortInfo : { direction : 'ASC', field: 'name' },
8008 xtype : 'HttpProxy',
8011 url : 'https://example.com/some.data.url.json'
8014 xtype : 'JsonReader',
8016 fields : [ 'id', 'name', whatever' ],
8023 xtype : 'ColumnModel',
8027 dataIndex : 'is_in_group',
8030 renderer : function(v, x , r) {
8032 return String.format("{0}", v)
8038 xtype : 'RowSelectionModel',
8039 xns : Roo.bootstrap.Table
8040 // you can add listeners to catch selection change here....
8046 grid.render(Roo.get("some-div"));
8048 <b>A Dialog should always be a direct child of the body element.</b>
8050 * @cfg {Roo.bootstrap.Table.RowSelectionModel|Roo.bootstrap.Table.CellSelectionModel} sm The selection model to use (cell selection is not supported yet)
8051 * @cfg {Roo.data.Store|Roo.data.SimpleStore} store The data store to use
8052 * @cfg {Roo.grid.ColumnModel} cm[] A column for th grid.
8054 * @cfg {String} cls table class
8055 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
8056 * @cfg {String} bgcolor Specifies the background color for a table
8057 * @cfg {Number} border Specifies whether the table cells should have borders or not
8058 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
8059 * @cfg {Number} cellspacing Specifies the space between cells
8060 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
8061 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
8062 * @cfg {String} sortable Specifies that the table should be sortable
8063 * @cfg {String} summary Specifies a summary of the content of a table
8064 * @cfg {Number} width Specifies the width of a table
8065 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
8067 * @cfg {boolean} striped Should the rows be alternative striped
8068 * @cfg {boolean} bordered Add borders to the table
8069 * @cfg {boolean} hover Add hover highlighting
8070 * @cfg {boolean} condensed Format condensed
8071 * @cfg {boolean} responsive Format condensed
8072 * @cfg {Boolean} loadMask (true|false) default false
8073 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
8074 * @cfg {Boolean} headerShow (true|false) generate thead, default true
8075 * @cfg {Boolean} rowSelection (true|false) default false
8076 * @cfg {Boolean} cellSelection (true|false) default false
8077 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
8078 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
8079 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
8080 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
8084 * Create a new Table
8085 * @param {Object} config The config object
8088 Roo.bootstrap.Table = function(config){
8089 Roo.bootstrap.Table.superclass.constructor.call(this, config);
8092 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
8093 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
8094 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
8095 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
8097 this.sm = this.sm || {xtype: 'RowSelectionModel'};
8099 this.sm.grid = this;
8100 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
8101 this.sm = this.selModel;
8102 this.sm.xmodule = this.xmodule || false;
8105 if (this.cm && typeof(this.cm.config) == 'undefined') {
8106 this.colModel = new Roo.grid.ColumnModel(this.cm);
8107 this.cm = this.colModel;
8108 this.cm.xmodule = this.xmodule || false;
8111 this.store= Roo.factory(this.store, Roo.data);
8112 this.ds = this.store;
8113 this.ds.xmodule = this.xmodule || false;
8116 if (this.footer && this.store) {
8117 this.footer.dataSource = this.ds;
8118 this.footer = Roo.factory(this.footer);
8125 * Fires when a cell is clicked
8126 * @param {Roo.bootstrap.Table} this
8127 * @param {Roo.Element} el
8128 * @param {Number} rowIndex
8129 * @param {Number} columnIndex
8130 * @param {Roo.EventObject} e
8134 * @event celldblclick
8135 * Fires when a cell is double clicked
8136 * @param {Roo.bootstrap.Table} this
8137 * @param {Roo.Element} el
8138 * @param {Number} rowIndex
8139 * @param {Number} columnIndex
8140 * @param {Roo.EventObject} e
8142 "celldblclick" : true,
8145 * Fires when a row is clicked
8146 * @param {Roo.bootstrap.Table} this
8147 * @param {Roo.Element} el
8148 * @param {Number} rowIndex
8149 * @param {Roo.EventObject} e
8153 * @event rowdblclick
8154 * Fires when a row is double clicked
8155 * @param {Roo.bootstrap.Table} this
8156 * @param {Roo.Element} el
8157 * @param {Number} rowIndex
8158 * @param {Roo.EventObject} e
8160 "rowdblclick" : true,
8163 * Fires when a mouseover occur
8164 * @param {Roo.bootstrap.Table} this
8165 * @param {Roo.Element} el
8166 * @param {Number} rowIndex
8167 * @param {Number} columnIndex
8168 * @param {Roo.EventObject} e
8173 * Fires when a mouseout occur
8174 * @param {Roo.bootstrap.Table} this
8175 * @param {Roo.Element} el
8176 * @param {Number} rowIndex
8177 * @param {Number} columnIndex
8178 * @param {Roo.EventObject} e
8183 * Fires when a row is rendered, so you can change add a style to it.
8184 * @param {Roo.bootstrap.Table} this
8185 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
8189 * @event rowsrendered
8190 * Fires when all the rows have been rendered
8191 * @param {Roo.bootstrap.Table} this
8193 'rowsrendered' : true,
8195 * @event contextmenu
8196 * The raw contextmenu event for the entire grid.
8197 * @param {Roo.EventObject} e
8199 "contextmenu" : true,
8201 * @event rowcontextmenu
8202 * Fires when a row is right clicked
8203 * @param {Roo.bootstrap.Table} this
8204 * @param {Number} rowIndex
8205 * @param {Roo.EventObject} e
8207 "rowcontextmenu" : true,
8209 * @event cellcontextmenu
8210 * Fires when a cell is right clicked
8211 * @param {Roo.bootstrap.Table} this
8212 * @param {Number} rowIndex
8213 * @param {Number} cellIndex
8214 * @param {Roo.EventObject} e
8216 "cellcontextmenu" : true,
8218 * @event headercontextmenu
8219 * Fires when a header is right clicked
8220 * @param {Roo.bootstrap.Table} this
8221 * @param {Number} columnIndex
8222 * @param {Roo.EventObject} e
8224 "headercontextmenu" : true
8228 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
8254 rowSelection : false,
8255 cellSelection : false,
8258 // Roo.Element - the tbody
8260 // Roo.Element - thead element
8263 container: false, // used by gridpanel...
8269 auto_hide_footer : false,
8271 getAutoCreate : function()
8273 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8280 if (this.scrollBody) {
8281 cfg.cls += ' table-body-fixed';
8284 cfg.cls += ' table-striped';
8288 cfg.cls += ' table-hover';
8290 if (this.bordered) {
8291 cfg.cls += ' table-bordered';
8293 if (this.condensed) {
8294 cfg.cls += ' table-condensed';
8296 if (this.responsive) {
8297 cfg.cls += ' table-responsive';
8301 cfg.cls+= ' ' +this.cls;
8304 // this lot should be simplifed...
8317 ].forEach(function(k) {
8325 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8328 if(this.store || this.cm){
8329 if(this.headerShow){
8330 cfg.cn.push(this.renderHeader());
8333 cfg.cn.push(this.renderBody());
8335 if(this.footerShow){
8336 cfg.cn.push(this.renderFooter());
8338 // where does this come from?
8339 //cfg.cls+= ' TableGrid';
8342 return { cn : [ cfg ] };
8345 initEvents : function()
8347 if(!this.store || !this.cm){
8350 if (this.selModel) {
8351 this.selModel.initEvents();
8355 //Roo.log('initEvents with ds!!!!');
8357 this.mainBody = this.el.select('tbody', true).first();
8358 this.mainHead = this.el.select('thead', true).first();
8359 this.mainFoot = this.el.select('tfoot', true).first();
8364 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8365 e.on('click', this.sort, this);
8368 this.mainBody.on("click", this.onClick, this);
8369 this.mainBody.on("dblclick", this.onDblClick, this);
8371 // why is this done????? = it breaks dialogs??
8372 //this.parent().el.setStyle('position', 'relative');
8376 this.footer.parentId = this.id;
8377 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8380 this.el.select('tfoot tr td').first().addClass('hide');
8385 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8388 this.store.on('load', this.onLoad, this);
8389 this.store.on('beforeload', this.onBeforeLoad, this);
8390 this.store.on('update', this.onUpdate, this);
8391 this.store.on('add', this.onAdd, this);
8392 this.store.on("clear", this.clear, this);
8394 this.el.on("contextmenu", this.onContextMenu, this);
8396 this.mainBody.on('scroll', this.onBodyScroll, this);
8398 this.cm.on("headerchange", this.onHeaderChange, this);
8400 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8404 onContextMenu : function(e, t)
8406 this.processEvent("contextmenu", e);
8409 processEvent : function(name, e)
8411 if (name != 'touchstart' ) {
8412 this.fireEvent(name, e);
8415 var t = e.getTarget();
8417 var cell = Roo.get(t);
8423 if(cell.findParent('tfoot', false, true)){
8427 if(cell.findParent('thead', false, true)){
8429 if(e.getTarget().nodeName.toLowerCase() != 'th'){
8430 cell = Roo.get(t).findParent('th', false, true);
8432 Roo.log("failed to find th in thead?");
8433 Roo.log(e.getTarget());
8438 var cellIndex = cell.dom.cellIndex;
8440 var ename = name == 'touchstart' ? 'click' : name;
8441 this.fireEvent("header" + ename, this, cellIndex, e);
8446 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8447 cell = Roo.get(t).findParent('td', false, true);
8449 Roo.log("failed to find th in tbody?");
8450 Roo.log(e.getTarget());
8455 var row = cell.findParent('tr', false, true);
8456 var cellIndex = cell.dom.cellIndex;
8457 var rowIndex = row.dom.rowIndex - 1;
8461 this.fireEvent("row" + name, this, rowIndex, e);
8465 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8471 onMouseover : function(e, el)
8473 var cell = Roo.get(el);
8479 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8480 cell = cell.findParent('td', false, true);
8483 var row = cell.findParent('tr', false, true);
8484 var cellIndex = cell.dom.cellIndex;
8485 var rowIndex = row.dom.rowIndex - 1; // start from 0
8487 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8491 onMouseout : function(e, el)
8493 var cell = Roo.get(el);
8499 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8500 cell = cell.findParent('td', false, true);
8503 var row = cell.findParent('tr', false, true);
8504 var cellIndex = cell.dom.cellIndex;
8505 var rowIndex = row.dom.rowIndex - 1; // start from 0
8507 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8511 onClick : function(e, el)
8513 var cell = Roo.get(el);
8515 if(!cell || (!this.cellSelection && !this.rowSelection)){
8519 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8520 cell = cell.findParent('td', false, true);
8523 if(!cell || typeof(cell) == 'undefined'){
8527 var row = cell.findParent('tr', false, true);
8529 if(!row || typeof(row) == 'undefined'){
8533 var cellIndex = cell.dom.cellIndex;
8534 var rowIndex = this.getRowIndex(row);
8536 // why??? - should these not be based on SelectionModel?
8537 //if(this.cellSelection){
8538 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8541 //if(this.rowSelection){
8542 this.fireEvent('rowclick', this, row, rowIndex, e);
8547 onDblClick : function(e,el)
8549 var cell = Roo.get(el);
8551 if(!cell || (!this.cellSelection && !this.rowSelection)){
8555 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8556 cell = cell.findParent('td', false, true);
8559 if(!cell || typeof(cell) == 'undefined'){
8563 var row = cell.findParent('tr', false, true);
8565 if(!row || typeof(row) == 'undefined'){
8569 var cellIndex = cell.dom.cellIndex;
8570 var rowIndex = this.getRowIndex(row);
8572 if(this.cellSelection){
8573 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8576 if(this.rowSelection){
8577 this.fireEvent('rowdblclick', this, row, rowIndex, e);
8581 sort : function(e,el)
8583 var col = Roo.get(el);
8585 if(!col.hasClass('sortable')){
8589 var sort = col.attr('sort');
8592 if(col.select('i', true).first().hasClass('fa-arrow-up')){
8596 this.store.sortInfo = {field : sort, direction : dir};
8599 Roo.log("calling footer first");
8600 this.footer.onClick('first');
8603 this.store.load({ params : { start : 0 } });
8607 renderHeader : function()
8615 this.totalWidth = 0;
8617 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8619 var config = cm.config[i];
8623 cls : 'x-hcol-' + i,
8626 html: cm.getColumnHeader(i)
8629 var tooltip = cm.getColumnTooltip(i);
8631 c.tooltip = tooltip;
8637 if(typeof(config.sortable) != 'undefined' && config.sortable){
8639 c.html = '<i class="fa"></i>' + c.html;
8642 // could use BS4 hidden-..-down
8644 if(typeof(config.lgHeader) != 'undefined'){
8645 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8648 if(typeof(config.mdHeader) != 'undefined'){
8649 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8652 if(typeof(config.smHeader) != 'undefined'){
8653 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8656 if(typeof(config.xsHeader) != 'undefined'){
8657 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8664 if(typeof(config.tooltip) != 'undefined'){
8665 c.tooltip = config.tooltip;
8668 if(typeof(config.colspan) != 'undefined'){
8669 c.colspan = config.colspan;
8672 if(typeof(config.hidden) != 'undefined' && config.hidden){
8673 c.style += ' display:none;';
8676 if(typeof(config.dataIndex) != 'undefined'){
8677 c.sort = config.dataIndex;
8682 if(typeof(config.align) != 'undefined' && config.align.length){
8683 c.style += ' text-align:' + config.align + ';';
8686 if(typeof(config.width) != 'undefined'){
8687 c.style += ' width:' + config.width + 'px;';
8688 this.totalWidth += config.width;
8690 this.totalWidth += 100; // assume minimum of 100 per column?
8693 if(typeof(config.cls) != 'undefined'){
8694 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8697 ['xs','sm','md','lg'].map(function(size){
8699 if(typeof(config[size]) == 'undefined'){
8703 if (!config[size]) { // 0 = hidden
8704 // BS 4 '0' is treated as hide that column and below.
8705 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8709 c.cls += ' col-' + size + '-' + config[size] + (
8710 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8722 renderBody : function()
8732 colspan : this.cm.getColumnCount()
8742 renderFooter : function()
8752 colspan : this.cm.getColumnCount()
8766 // Roo.log('ds onload');
8771 var ds = this.store;
8773 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8774 e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
8775 if (_this.store.sortInfo) {
8777 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8778 e.select('i', true).addClass(['fa-arrow-up']);
8781 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8782 e.select('i', true).addClass(['fa-arrow-down']);
8787 var tbody = this.mainBody;
8789 if(ds.getCount() > 0){
8790 ds.data.each(function(d,rowIndex){
8791 var row = this.renderRow(cm, ds, rowIndex);
8793 tbody.createChild(row);
8797 if(row.cellObjects.length){
8798 Roo.each(row.cellObjects, function(r){
8799 _this.renderCellObject(r);
8806 var tfoot = this.el.select('tfoot', true).first();
8808 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8810 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8812 var total = this.ds.getTotalCount();
8814 if(this.footer.pageSize < total){
8815 this.mainFoot.show();
8819 Roo.each(this.el.select('tbody td', true).elements, function(e){
8820 e.on('mouseover', _this.onMouseover, _this);
8823 Roo.each(this.el.select('tbody td', true).elements, function(e){
8824 e.on('mouseout', _this.onMouseout, _this);
8826 this.fireEvent('rowsrendered', this);
8832 onUpdate : function(ds,record)
8834 this.refreshRow(record);
8838 onRemove : function(ds, record, index, isUpdate){
8839 if(isUpdate !== true){
8840 this.fireEvent("beforerowremoved", this, index, record);
8842 var bt = this.mainBody.dom;
8844 var rows = this.el.select('tbody > tr', true).elements;
8846 if(typeof(rows[index]) != 'undefined'){
8847 bt.removeChild(rows[index].dom);
8850 // if(bt.rows[index]){
8851 // bt.removeChild(bt.rows[index]);
8854 if(isUpdate !== true){
8855 //this.stripeRows(index);
8856 //this.syncRowHeights(index, index);
8858 this.fireEvent("rowremoved", this, index, record);
8862 onAdd : function(ds, records, rowIndex)
8864 //Roo.log('on Add called');
8865 // - note this does not handle multiple adding very well..
8866 var bt = this.mainBody.dom;
8867 for (var i =0 ; i < records.length;i++) {
8868 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8869 //Roo.log(records[i]);
8870 //Roo.log(this.store.getAt(rowIndex+i));
8871 this.insertRow(this.store, rowIndex + i, false);
8878 refreshRow : function(record){
8879 var ds = this.store, index;
8880 if(typeof record == 'number'){
8882 record = ds.getAt(index);
8884 index = ds.indexOf(record);
8886 return; // should not happen - but seems to
8889 this.insertRow(ds, index, true);
8891 this.onRemove(ds, record, index+1, true);
8893 //this.syncRowHeights(index, index);
8895 this.fireEvent("rowupdated", this, index, record);
8898 insertRow : function(dm, rowIndex, isUpdate){
8901 this.fireEvent("beforerowsinserted", this, rowIndex);
8903 //var s = this.getScrollState();
8904 var row = this.renderRow(this.cm, this.store, rowIndex);
8905 // insert before rowIndex..
8906 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8910 if(row.cellObjects.length){
8911 Roo.each(row.cellObjects, function(r){
8912 _this.renderCellObject(r);
8917 this.fireEvent("rowsinserted", this, rowIndex);
8918 //this.syncRowHeights(firstRow, lastRow);
8919 //this.stripeRows(firstRow);
8926 getRowDom : function(rowIndex)
8928 var rows = this.el.select('tbody > tr', true).elements;
8930 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8933 // returns the object tree for a tr..
8936 renderRow : function(cm, ds, rowIndex)
8938 var d = ds.getAt(rowIndex);
8942 cls : 'x-row-' + rowIndex,
8946 var cellObjects = [];
8948 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8949 var config = cm.config[i];
8951 var renderer = cm.getRenderer(i);
8955 if(typeof(renderer) !== 'undefined'){
8956 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8958 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8959 // and are rendered into the cells after the row is rendered - using the id for the element.
8961 if(typeof(value) === 'object'){
8971 rowIndex : rowIndex,
8976 this.fireEvent('rowclass', this, rowcfg);
8980 // this might end up displaying HTML?
8981 // this is too messy... - better to only do it on columsn you know are going to be too long
8982 //tooltip : (typeof(value) === 'object') ? '' : value,
8983 cls : rowcfg.rowClass + ' x-col-' + i,
8985 html: (typeof(value) === 'object') ? '' : value
8992 if(typeof(config.colspan) != 'undefined'){
8993 td.colspan = config.colspan;
8996 if(typeof(config.hidden) != 'undefined' && config.hidden){
8997 td.style += ' display:none;';
9000 if(typeof(config.align) != 'undefined' && config.align.length){
9001 td.style += ' text-align:' + config.align + ';';
9003 if(typeof(config.valign) != 'undefined' && config.valign.length){
9004 td.style += ' vertical-align:' + config.valign + ';';
9007 if(typeof(config.width) != 'undefined'){
9008 td.style += ' width:' + config.width + 'px;';
9011 if(typeof(config.cursor) != 'undefined'){
9012 td.style += ' cursor:' + config.cursor + ';';
9015 if(typeof(config.cls) != 'undefined'){
9016 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
9019 ['xs','sm','md','lg'].map(function(size){
9021 if(typeof(config[size]) == 'undefined'){
9027 if (!config[size]) { // 0 = hidden
9028 // BS 4 '0' is treated as hide that column and below.
9029 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
9033 td.cls += ' col-' + size + '-' + config[size] + (
9034 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9044 row.cellObjects = cellObjects;
9052 onBeforeLoad : function()
9061 this.el.select('tbody', true).first().dom.innerHTML = '';
9064 * Show or hide a row.
9065 * @param {Number} rowIndex to show or hide
9066 * @param {Boolean} state hide
9068 setRowVisibility : function(rowIndex, state)
9070 var bt = this.mainBody.dom;
9072 var rows = this.el.select('tbody > tr', true).elements;
9074 if(typeof(rows[rowIndex]) == 'undefined'){
9077 rows[rowIndex].dom.style.display = state ? '' : 'none';
9081 getSelectionModel : function(){
9083 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
9085 return this.selModel;
9088 * Render the Roo.bootstrap object from renderder
9090 renderCellObject : function(r)
9094 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
9096 var t = r.cfg.render(r.container);
9099 Roo.each(r.cfg.cn, function(c){
9101 container: t.getChildContainer(),
9104 _this.renderCellObject(child);
9109 getRowIndex : function(row)
9113 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
9124 * Returns the grid's underlying element = used by panel.Grid
9125 * @return {Element} The element
9127 getGridEl : function(){
9131 * Forces a resize - used by panel.Grid
9132 * @return {Element} The element
9134 autoSize : function()
9136 //var ctr = Roo.get(this.container.dom.parentElement);
9137 var ctr = Roo.get(this.el.dom);
9139 var thd = this.getGridEl().select('thead',true).first();
9140 var tbd = this.getGridEl().select('tbody', true).first();
9141 var tfd = this.getGridEl().select('tfoot', true).first();
9143 var cw = ctr.getWidth();
9144 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
9148 tbd.setWidth(ctr.getWidth());
9149 // if the body has a max height - and then scrolls - we should perhaps set up the height here
9150 // this needs fixing for various usage - currently only hydra job advers I think..
9152 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
9154 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
9157 cw = Math.max(cw, this.totalWidth);
9158 this.getGridEl().select('tbody tr',true).setWidth(cw);
9160 // resize 'expandable coloumn?
9162 return; // we doe not have a view in this design..
9165 onBodyScroll: function()
9167 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
9169 this.mainHead.setStyle({
9170 'position' : 'relative',
9171 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
9177 var scrollHeight = this.mainBody.dom.scrollHeight;
9179 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
9181 var height = this.mainBody.getHeight();
9183 if(scrollHeight - height == scrollTop) {
9185 var total = this.ds.getTotalCount();
9187 if(this.footer.cursor + this.footer.pageSize < total){
9189 this.footer.ds.load({
9191 start : this.footer.cursor + this.footer.pageSize,
9192 limit : this.footer.pageSize
9202 onHeaderChange : function()
9204 var header = this.renderHeader();
9205 var table = this.el.select('table', true).first();
9207 this.mainHead.remove();
9208 this.mainHead = table.createChild(header, this.mainBody, false);
9210 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9211 e.on('click', this.sort, this);
9217 onHiddenChange : function(colModel, colIndex, hidden)
9219 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
9220 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
9222 this.CSS.updateRule(thSelector, "display", "");
9223 this.CSS.updateRule(tdSelector, "display", "");
9226 this.CSS.updateRule(thSelector, "display", "none");
9227 this.CSS.updateRule(tdSelector, "display", "none");
9230 this.onHeaderChange();
9234 setColumnWidth: function(col_index, width)
9236 // width = "md-2 xs-2..."
9237 if(!this.colModel.config[col_index]) {
9241 var w = width.split(" ");
9243 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
9245 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
9248 for(var j = 0; j < w.length; j++) {
9254 var size_cls = w[j].split("-");
9256 if(!Number.isInteger(size_cls[1] * 1)) {
9260 if(!this.colModel.config[col_index][size_cls[0]]) {
9264 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9268 h_row[0].classList.replace(
9269 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9270 "col-"+size_cls[0]+"-"+size_cls[1]
9273 for(var i = 0; i < rows.length; i++) {
9275 var size_cls = w[j].split("-");
9277 if(!Number.isInteger(size_cls[1] * 1)) {
9281 if(!this.colModel.config[col_index][size_cls[0]]) {
9285 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9289 rows[i].classList.replace(
9290 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9291 "col-"+size_cls[0]+"-"+size_cls[1]
9295 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
9310 * @class Roo.bootstrap.TableCell
9311 * @extends Roo.bootstrap.Component
9312 * Bootstrap TableCell class
9313 * @cfg {String} html cell contain text
9314 * @cfg {String} cls cell class
9315 * @cfg {String} tag cell tag (td|th) default td
9316 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
9317 * @cfg {String} align Aligns the content in a cell
9318 * @cfg {String} axis Categorizes cells
9319 * @cfg {String} bgcolor Specifies the background color of a cell
9320 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9321 * @cfg {Number} colspan Specifies the number of columns a cell should span
9322 * @cfg {String} headers Specifies one or more header cells a cell is related to
9323 * @cfg {Number} height Sets the height of a cell
9324 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
9325 * @cfg {Number} rowspan Sets the number of rows a cell should span
9326 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
9327 * @cfg {String} valign Vertical aligns the content in a cell
9328 * @cfg {Number} width Specifies the width of a cell
9331 * Create a new TableCell
9332 * @param {Object} config The config object
9335 Roo.bootstrap.TableCell = function(config){
9336 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
9339 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
9359 getAutoCreate : function(){
9360 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
9380 cfg.align=this.align
9386 cfg.bgcolor=this.bgcolor
9389 cfg.charoff=this.charoff
9392 cfg.colspan=this.colspan
9395 cfg.headers=this.headers
9398 cfg.height=this.height
9401 cfg.nowrap=this.nowrap
9404 cfg.rowspan=this.rowspan
9407 cfg.scope=this.scope
9410 cfg.valign=this.valign
9413 cfg.width=this.width
9432 * @class Roo.bootstrap.TableRow
9433 * @extends Roo.bootstrap.Component
9434 * Bootstrap TableRow class
9435 * @cfg {String} cls row class
9436 * @cfg {String} align Aligns the content in a table row
9437 * @cfg {String} bgcolor Specifies a background color for a table row
9438 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9439 * @cfg {String} valign Vertical aligns the content in a table row
9442 * Create a new TableRow
9443 * @param {Object} config The config object
9446 Roo.bootstrap.TableRow = function(config){
9447 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9450 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
9458 getAutoCreate : function(){
9459 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9469 cfg.align = this.align;
9472 cfg.bgcolor = this.bgcolor;
9475 cfg.charoff = this.charoff;
9478 cfg.valign = this.valign;
9496 * @class Roo.bootstrap.TableBody
9497 * @extends Roo.bootstrap.Component
9498 * Bootstrap TableBody class
9499 * @cfg {String} cls element class
9500 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9501 * @cfg {String} align Aligns the content inside the element
9502 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9503 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9506 * Create a new TableBody
9507 * @param {Object} config The config object
9510 Roo.bootstrap.TableBody = function(config){
9511 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9514 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
9522 getAutoCreate : function(){
9523 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9537 cfg.align = this.align;
9540 cfg.charoff = this.charoff;
9543 cfg.valign = this.valign;
9550 // initEvents : function()
9557 // this.store = Roo.factory(this.store, Roo.data);
9558 // this.store.on('load', this.onLoad, this);
9560 // this.store.load();
9564 // onLoad: function ()
9566 // this.fireEvent('load', this);
9576 * Ext JS Library 1.1.1
9577 * Copyright(c) 2006-2007, Ext JS, LLC.
9579 * Originally Released Under LGPL - original licence link has changed is not relivant.
9582 * <script type="text/javascript">
9585 // as we use this in bootstrap.
9586 Roo.namespace('Roo.form');
9588 * @class Roo.form.Action
9589 * Internal Class used to handle form actions
9591 * @param {Roo.form.BasicForm} el The form element or its id
9592 * @param {Object} config Configuration options
9597 // define the action interface
9598 Roo.form.Action = function(form, options){
9600 this.options = options || {};
9603 * Client Validation Failed
9606 Roo.form.Action.CLIENT_INVALID = 'client';
9608 * Server Validation Failed
9611 Roo.form.Action.SERVER_INVALID = 'server';
9613 * Connect to Server Failed
9616 Roo.form.Action.CONNECT_FAILURE = 'connect';
9618 * Reading Data from Server Failed
9621 Roo.form.Action.LOAD_FAILURE = 'load';
9623 Roo.form.Action.prototype = {
9625 failureType : undefined,
9626 response : undefined,
9630 run : function(options){
9635 success : function(response){
9640 handleResponse : function(response){
9644 // default connection failure
9645 failure : function(response){
9647 this.response = response;
9648 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9649 this.form.afterAction(this, false);
9652 processResponse : function(response){
9653 this.response = response;
9654 if(!response.responseText){
9657 this.result = this.handleResponse(response);
9661 // utility functions used internally
9662 getUrl : function(appendParams){
9663 var url = this.options.url || this.form.url || this.form.el.dom.action;
9665 var p = this.getParams();
9667 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9673 getMethod : function(){
9674 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9677 getParams : function(){
9678 var bp = this.form.baseParams;
9679 var p = this.options.params;
9681 if(typeof p == "object"){
9682 p = Roo.urlEncode(Roo.applyIf(p, bp));
9683 }else if(typeof p == 'string' && bp){
9684 p += '&' + Roo.urlEncode(bp);
9687 p = Roo.urlEncode(bp);
9692 createCallback : function(){
9694 success: this.success,
9695 failure: this.failure,
9697 timeout: (this.form.timeout*1000),
9698 upload: this.form.fileUpload ? this.success : undefined
9703 Roo.form.Action.Submit = function(form, options){
9704 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9707 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9710 haveProgress : false,
9711 uploadComplete : false,
9713 // uploadProgress indicator.
9714 uploadProgress : function()
9716 if (!this.form.progressUrl) {
9720 if (!this.haveProgress) {
9721 Roo.MessageBox.progress("Uploading", "Uploading");
9723 if (this.uploadComplete) {
9724 Roo.MessageBox.hide();
9728 this.haveProgress = true;
9730 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9732 var c = new Roo.data.Connection();
9734 url : this.form.progressUrl,
9739 success : function(req){
9740 //console.log(data);
9744 rdata = Roo.decode(req.responseText)
9746 Roo.log("Invalid data from server..");
9750 if (!rdata || !rdata.success) {
9752 Roo.MessageBox.alert(Roo.encode(rdata));
9755 var data = rdata.data;
9757 if (this.uploadComplete) {
9758 Roo.MessageBox.hide();
9763 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9764 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9767 this.uploadProgress.defer(2000,this);
9770 failure: function(data) {
9771 Roo.log('progress url failed ');
9782 // run get Values on the form, so it syncs any secondary forms.
9783 this.form.getValues();
9785 var o = this.options;
9786 var method = this.getMethod();
9787 var isPost = method == 'POST';
9788 if(o.clientValidation === false || this.form.isValid()){
9790 if (this.form.progressUrl) {
9791 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9792 (new Date() * 1) + '' + Math.random());
9797 Roo.Ajax.request(Roo.apply(this.createCallback(), {
9798 form:this.form.el.dom,
9799 url:this.getUrl(!isPost),
9801 params:isPost ? this.getParams() : null,
9802 isUpload: this.form.fileUpload,
9803 formData : this.form.formData
9806 this.uploadProgress();
9808 }else if (o.clientValidation !== false){ // client validation failed
9809 this.failureType = Roo.form.Action.CLIENT_INVALID;
9810 this.form.afterAction(this, false);
9814 success : function(response)
9816 this.uploadComplete= true;
9817 if (this.haveProgress) {
9818 Roo.MessageBox.hide();
9822 var result = this.processResponse(response);
9823 if(result === true || result.success){
9824 this.form.afterAction(this, true);
9828 this.form.markInvalid(result.errors);
9829 this.failureType = Roo.form.Action.SERVER_INVALID;
9831 this.form.afterAction(this, false);
9833 failure : function(response)
9835 this.uploadComplete= true;
9836 if (this.haveProgress) {
9837 Roo.MessageBox.hide();
9840 this.response = response;
9841 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9842 this.form.afterAction(this, false);
9845 handleResponse : function(response){
9846 if(this.form.errorReader){
9847 var rs = this.form.errorReader.read(response);
9850 for(var i = 0, len = rs.records.length; i < len; i++) {
9851 var r = rs.records[i];
9855 if(errors.length < 1){
9859 success : rs.success,
9865 ret = Roo.decode(response.responseText);
9869 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9879 Roo.form.Action.Load = function(form, options){
9880 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9881 this.reader = this.form.reader;
9884 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9889 Roo.Ajax.request(Roo.apply(
9890 this.createCallback(), {
9891 method:this.getMethod(),
9892 url:this.getUrl(false),
9893 params:this.getParams()
9897 success : function(response){
9899 var result = this.processResponse(response);
9900 if(result === true || !result.success || !result.data){
9901 this.failureType = Roo.form.Action.LOAD_FAILURE;
9902 this.form.afterAction(this, false);
9905 this.form.clearInvalid();
9906 this.form.setValues(result.data);
9907 this.form.afterAction(this, true);
9910 handleResponse : function(response){
9911 if(this.form.reader){
9912 var rs = this.form.reader.read(response);
9913 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9915 success : rs.success,
9919 return Roo.decode(response.responseText);
9923 Roo.form.Action.ACTION_TYPES = {
9924 'load' : Roo.form.Action.Load,
9925 'submit' : Roo.form.Action.Submit
9934 * @class Roo.bootstrap.Form
9935 * @extends Roo.bootstrap.Component
9936 * Bootstrap Form class
9937 * @cfg {String} method GET | POST (default POST)
9938 * @cfg {String} labelAlign top | left (default top)
9939 * @cfg {String} align left | right - for navbars
9940 * @cfg {Boolean} loadMask load mask when submit (default true)
9945 * @param {Object} config The config object
9949 Roo.bootstrap.Form = function(config){
9951 Roo.bootstrap.Form.superclass.constructor.call(this, config);
9953 Roo.bootstrap.Form.popover.apply();
9957 * @event clientvalidation
9958 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9959 * @param {Form} this
9960 * @param {Boolean} valid true if the form has passed client-side validation
9962 clientvalidation: true,
9964 * @event beforeaction
9965 * Fires before any action is performed. Return false to cancel the action.
9966 * @param {Form} this
9967 * @param {Action} action The action to be performed
9971 * @event actionfailed
9972 * Fires when an action fails.
9973 * @param {Form} this
9974 * @param {Action} action The action that failed
9976 actionfailed : true,
9978 * @event actioncomplete
9979 * Fires when an action is completed.
9980 * @param {Form} this
9981 * @param {Action} action The action that completed
9983 actioncomplete : true
9987 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
9990 * @cfg {String} method
9991 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9996 * The URL to use for form actions if one isn't supplied in the action options.
9999 * @cfg {Boolean} fileUpload
10000 * Set to true if this form is a file upload.
10004 * @cfg {Object} baseParams
10005 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
10009 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
10013 * @cfg {Sting} align (left|right) for navbar forms
10018 activeAction : null,
10021 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
10022 * element by passing it or its id or mask the form itself by passing in true.
10025 waitMsgTarget : false,
10030 * @cfg {Boolean} errorMask (true|false) default false
10035 * @cfg {Number} maskOffset Default 100
10040 * @cfg {Boolean} maskBody
10044 getAutoCreate : function(){
10048 method : this.method || 'POST',
10049 id : this.id || Roo.id(),
10052 if (this.parent().xtype.match(/^Nav/)) {
10053 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
10057 if (this.labelAlign == 'left' ) {
10058 cfg.cls += ' form-horizontal';
10064 initEvents : function()
10066 this.el.on('submit', this.onSubmit, this);
10067 // this was added as random key presses on the form where triggering form submit.
10068 this.el.on('keypress', function(e) {
10069 if (e.getCharCode() != 13) {
10072 // we might need to allow it for textareas.. and some other items.
10073 // check e.getTarget().
10075 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
10079 Roo.log("keypress blocked");
10081 e.preventDefault();
10087 onSubmit : function(e){
10092 * Returns true if client-side validation on the form is successful.
10095 isValid : function(){
10096 var items = this.getItems();
10098 var target = false;
10100 items.each(function(f){
10106 Roo.log('invalid field: ' + f.name);
10110 if(!target && f.el.isVisible(true)){
10116 if(this.errorMask && !valid){
10117 Roo.bootstrap.Form.popover.mask(this, target);
10124 * Returns true if any fields in this form have changed since their original load.
10127 isDirty : function(){
10129 var items = this.getItems();
10130 items.each(function(f){
10140 * Performs a predefined action (submit or load) or custom actions you define on this form.
10141 * @param {String} actionName The name of the action type
10142 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
10143 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
10144 * accept other config options):
10146 Property Type Description
10147 ---------------- --------------- ----------------------------------------------------------------------------------
10148 url String The url for the action (defaults to the form's url)
10149 method String The form method to use (defaults to the form's method, or POST if not defined)
10150 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
10151 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
10152 validate the form on the client (defaults to false)
10154 * @return {BasicForm} this
10156 doAction : function(action, options){
10157 if(typeof action == 'string'){
10158 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
10160 if(this.fireEvent('beforeaction', this, action) !== false){
10161 this.beforeAction(action);
10162 action.run.defer(100, action);
10168 beforeAction : function(action){
10169 var o = action.options;
10174 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
10176 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10179 // not really supported yet.. ??
10181 //if(this.waitMsgTarget === true){
10182 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10183 //}else if(this.waitMsgTarget){
10184 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
10185 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
10187 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
10193 afterAction : function(action, success){
10194 this.activeAction = null;
10195 var o = action.options;
10200 Roo.get(document.body).unmask();
10206 //if(this.waitMsgTarget === true){
10207 // this.el.unmask();
10208 //}else if(this.waitMsgTarget){
10209 // this.waitMsgTarget.unmask();
10211 // Roo.MessageBox.updateProgress(1);
10212 // Roo.MessageBox.hide();
10219 Roo.callback(o.success, o.scope, [this, action]);
10220 this.fireEvent('actioncomplete', this, action);
10224 // failure condition..
10225 // we have a scenario where updates need confirming.
10226 // eg. if a locking scenario exists..
10227 // we look for { errors : { needs_confirm : true }} in the response.
10229 (typeof(action.result) != 'undefined') &&
10230 (typeof(action.result.errors) != 'undefined') &&
10231 (typeof(action.result.errors.needs_confirm) != 'undefined')
10234 Roo.log("not supported yet");
10237 Roo.MessageBox.confirm(
10238 "Change requires confirmation",
10239 action.result.errorMsg,
10244 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
10254 Roo.callback(o.failure, o.scope, [this, action]);
10255 // show an error message if no failed handler is set..
10256 if (!this.hasListener('actionfailed')) {
10257 Roo.log("need to add dialog support");
10259 Roo.MessageBox.alert("Error",
10260 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
10261 action.result.errorMsg :
10262 "Saving Failed, please check your entries or try again"
10267 this.fireEvent('actionfailed', this, action);
10272 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
10273 * @param {String} id The value to search for
10276 findField : function(id){
10277 var items = this.getItems();
10278 var field = items.get(id);
10280 items.each(function(f){
10281 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
10288 return field || null;
10291 * Mark fields in this form invalid in bulk.
10292 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
10293 * @return {BasicForm} this
10295 markInvalid : function(errors){
10296 if(errors instanceof Array){
10297 for(var i = 0, len = errors.length; i < len; i++){
10298 var fieldError = errors[i];
10299 var f = this.findField(fieldError.id);
10301 f.markInvalid(fieldError.msg);
10307 if(typeof errors[id] != 'function' && (field = this.findField(id))){
10308 field.markInvalid(errors[id]);
10312 //Roo.each(this.childForms || [], function (f) {
10313 // f.markInvalid(errors);
10320 * Set values for fields in this form in bulk.
10321 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
10322 * @return {BasicForm} this
10324 setValues : function(values){
10325 if(values instanceof Array){ // array of objects
10326 for(var i = 0, len = values.length; i < len; i++){
10328 var f = this.findField(v.id);
10330 f.setValue(v.value);
10331 if(this.trackResetOnLoad){
10332 f.originalValue = f.getValue();
10336 }else{ // object hash
10339 if(typeof values[id] != 'function' && (field = this.findField(id))){
10341 if (field.setFromData &&
10342 field.valueField &&
10343 field.displayField &&
10344 // combos' with local stores can
10345 // be queried via setValue()
10346 // to set their value..
10347 (field.store && !field.store.isLocal)
10351 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
10352 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
10353 field.setFromData(sd);
10355 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
10357 field.setFromData(values);
10360 field.setValue(values[id]);
10364 if(this.trackResetOnLoad){
10365 field.originalValue = field.getValue();
10371 //Roo.each(this.childForms || [], function (f) {
10372 // f.setValues(values);
10379 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
10380 * they are returned as an array.
10381 * @param {Boolean} asString
10384 getValues : function(asString){
10385 //if (this.childForms) {
10386 // copy values from the child forms
10387 // Roo.each(this.childForms, function (f) {
10388 // this.setValues(f.getValues());
10394 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10395 if(asString === true){
10398 return Roo.urlDecode(fs);
10402 * Returns the fields in this form as an object with key/value pairs.
10403 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10406 getFieldValues : function(with_hidden)
10408 var items = this.getItems();
10410 items.each(function(f){
10412 if (!f.getName()) {
10416 var v = f.getValue();
10418 if (f.inputType =='radio') {
10419 if (typeof(ret[f.getName()]) == 'undefined') {
10420 ret[f.getName()] = ''; // empty..
10423 if (!f.el.dom.checked) {
10427 v = f.el.dom.value;
10431 if(f.xtype == 'MoneyField'){
10432 ret[f.currencyName] = f.getCurrency();
10435 // not sure if this supported any more..
10436 if ((typeof(v) == 'object') && f.getRawValue) {
10437 v = f.getRawValue() ; // dates..
10439 // combo boxes where name != hiddenName...
10440 if (f.name !== false && f.name != '' && f.name != f.getName()) {
10441 ret[f.name] = f.getRawValue();
10443 ret[f.getName()] = v;
10450 * Clears all invalid messages in this form.
10451 * @return {BasicForm} this
10453 clearInvalid : function(){
10454 var items = this.getItems();
10456 items.each(function(f){
10464 * Resets this form.
10465 * @return {BasicForm} this
10467 reset : function(){
10468 var items = this.getItems();
10469 items.each(function(f){
10473 Roo.each(this.childForms || [], function (f) {
10481 getItems : function()
10483 var r=new Roo.util.MixedCollection(false, function(o){
10484 return o.id || (o.id = Roo.id());
10486 var iter = function(el) {
10493 Roo.each(el.items,function(e) {
10502 hideFields : function(items)
10504 Roo.each(items, function(i){
10506 var f = this.findField(i);
10517 showFields : function(items)
10519 Roo.each(items, function(i){
10521 var f = this.findField(i);
10534 Roo.apply(Roo.bootstrap.Form, {
10550 intervalID : false,
10556 if(this.isApplied){
10561 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10562 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10563 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10564 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10567 this.maskEl.top.enableDisplayMode("block");
10568 this.maskEl.left.enableDisplayMode("block");
10569 this.maskEl.bottom.enableDisplayMode("block");
10570 this.maskEl.right.enableDisplayMode("block");
10572 this.toolTip = new Roo.bootstrap.Tooltip({
10573 cls : 'roo-form-error-popover',
10575 'left' : ['r-l', [-2,0], 'right'],
10576 'right' : ['l-r', [2,0], 'left'],
10577 'bottom' : ['tl-bl', [0,2], 'top'],
10578 'top' : [ 'bl-tl', [0,-2], 'bottom']
10582 this.toolTip.render(Roo.get(document.body));
10584 this.toolTip.el.enableDisplayMode("block");
10586 Roo.get(document.body).on('click', function(){
10590 Roo.get(document.body).on('touchstart', function(){
10594 this.isApplied = true
10597 mask : function(form, target)
10601 this.target = target;
10603 if(!this.form.errorMask || !target.el){
10607 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10609 Roo.log(scrollable);
10611 var ot = this.target.el.calcOffsetsTo(scrollable);
10613 var scrollTo = ot[1] - this.form.maskOffset;
10615 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10617 scrollable.scrollTo('top', scrollTo);
10619 var box = this.target.el.getBox();
10621 var zIndex = Roo.bootstrap.Modal.zIndex++;
10624 this.maskEl.top.setStyle('position', 'absolute');
10625 this.maskEl.top.setStyle('z-index', zIndex);
10626 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10627 this.maskEl.top.setLeft(0);
10628 this.maskEl.top.setTop(0);
10629 this.maskEl.top.show();
10631 this.maskEl.left.setStyle('position', 'absolute');
10632 this.maskEl.left.setStyle('z-index', zIndex);
10633 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10634 this.maskEl.left.setLeft(0);
10635 this.maskEl.left.setTop(box.y - this.padding);
10636 this.maskEl.left.show();
10638 this.maskEl.bottom.setStyle('position', 'absolute');
10639 this.maskEl.bottom.setStyle('z-index', zIndex);
10640 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10641 this.maskEl.bottom.setLeft(0);
10642 this.maskEl.bottom.setTop(box.bottom + this.padding);
10643 this.maskEl.bottom.show();
10645 this.maskEl.right.setStyle('position', 'absolute');
10646 this.maskEl.right.setStyle('z-index', zIndex);
10647 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10648 this.maskEl.right.setLeft(box.right + this.padding);
10649 this.maskEl.right.setTop(box.y - this.padding);
10650 this.maskEl.right.show();
10652 this.toolTip.bindEl = this.target.el;
10654 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10656 var tip = this.target.blankText;
10658 if(this.target.getValue() !== '' ) {
10660 if (this.target.invalidText.length) {
10661 tip = this.target.invalidText;
10662 } else if (this.target.regexText.length){
10663 tip = this.target.regexText;
10667 this.toolTip.show(tip);
10669 this.intervalID = window.setInterval(function() {
10670 Roo.bootstrap.Form.popover.unmask();
10673 window.onwheel = function(){ return false;};
10675 (function(){ this.isMasked = true; }).defer(500, this);
10679 unmask : function()
10681 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10685 this.maskEl.top.setStyle('position', 'absolute');
10686 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10687 this.maskEl.top.hide();
10689 this.maskEl.left.setStyle('position', 'absolute');
10690 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10691 this.maskEl.left.hide();
10693 this.maskEl.bottom.setStyle('position', 'absolute');
10694 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10695 this.maskEl.bottom.hide();
10697 this.maskEl.right.setStyle('position', 'absolute');
10698 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10699 this.maskEl.right.hide();
10701 this.toolTip.hide();
10703 this.toolTip.el.hide();
10705 window.onwheel = function(){ return true;};
10707 if(this.intervalID){
10708 window.clearInterval(this.intervalID);
10709 this.intervalID = false;
10712 this.isMasked = false;
10722 * Ext JS Library 1.1.1
10723 * Copyright(c) 2006-2007, Ext JS, LLC.
10725 * Originally Released Under LGPL - original licence link has changed is not relivant.
10728 * <script type="text/javascript">
10731 * @class Roo.form.VTypes
10732 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10735 Roo.form.VTypes = function(){
10736 // closure these in so they are only created once.
10737 var alpha = /^[a-zA-Z_]+$/;
10738 var alphanum = /^[a-zA-Z0-9_]+$/;
10739 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10740 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10742 // All these messages and functions are configurable
10745 * The function used to validate email addresses
10746 * @param {String} value The email address
10748 'email' : function(v){
10749 return email.test(v);
10752 * The error text to display when the email validation function returns false
10755 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10757 * The keystroke filter mask to be applied on email input
10760 'emailMask' : /[a-z0-9_\.\-@]/i,
10763 * The function used to validate URLs
10764 * @param {String} value The URL
10766 'url' : function(v){
10767 return url.test(v);
10770 * The error text to display when the url validation function returns false
10773 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10776 * The function used to validate alpha values
10777 * @param {String} value The value
10779 'alpha' : function(v){
10780 return alpha.test(v);
10783 * The error text to display when the alpha validation function returns false
10786 'alphaText' : 'This field should only contain letters and _',
10788 * The keystroke filter mask to be applied on alpha input
10791 'alphaMask' : /[a-z_]/i,
10794 * The function used to validate alphanumeric values
10795 * @param {String} value The value
10797 'alphanum' : function(v){
10798 return alphanum.test(v);
10801 * The error text to display when the alphanumeric validation function returns false
10804 'alphanumText' : 'This field should only contain letters, numbers and _',
10806 * The keystroke filter mask to be applied on alphanumeric input
10809 'alphanumMask' : /[a-z0-9_]/i
10819 * @class Roo.bootstrap.Input
10820 * @extends Roo.bootstrap.Component
10821 * Bootstrap Input class
10822 * @cfg {Boolean} disabled is it disabled
10823 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
10824 * @cfg {String} name name of the input
10825 * @cfg {string} fieldLabel - the label associated
10826 * @cfg {string} placeholder - placeholder to put in text.
10827 * @cfg {string} before - input group add on before
10828 * @cfg {string} after - input group add on after
10829 * @cfg {string} size - (lg|sm) or leave empty..
10830 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10831 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10832 * @cfg {Number} md colspan out of 12 for computer-sized screens
10833 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10834 * @cfg {string} value default value of the input
10835 * @cfg {Number} labelWidth set the width of label
10836 * @cfg {Number} labellg set the width of label (1-12)
10837 * @cfg {Number} labelmd set the width of label (1-12)
10838 * @cfg {Number} labelsm set the width of label (1-12)
10839 * @cfg {Number} labelxs set the width of label (1-12)
10840 * @cfg {String} labelAlign (top|left)
10841 * @cfg {Boolean} readOnly Specifies that the field should be read-only
10842 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10843 * @cfg {String} indicatorpos (left|right) default left
10844 * @cfg {String} capture (user|camera) use for file input only. (default empty)
10845 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10846 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10848 * @cfg {String} align (left|center|right) Default left
10849 * @cfg {Boolean} forceFeedback (true|false) Default false
10852 * Create a new Input
10853 * @param {Object} config The config object
10856 Roo.bootstrap.Input = function(config){
10858 Roo.bootstrap.Input.superclass.constructor.call(this, config);
10863 * Fires when this field receives input focus.
10864 * @param {Roo.form.Field} this
10869 * Fires when this field loses input focus.
10870 * @param {Roo.form.Field} this
10874 * @event specialkey
10875 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
10876 * {@link Roo.EventObject#getKey} to determine which key was pressed.
10877 * @param {Roo.form.Field} this
10878 * @param {Roo.EventObject} e The event object
10883 * Fires just before the field blurs if the field value has changed.
10884 * @param {Roo.form.Field} this
10885 * @param {Mixed} newValue The new value
10886 * @param {Mixed} oldValue The original value
10891 * Fires after the field has been marked as invalid.
10892 * @param {Roo.form.Field} this
10893 * @param {String} msg The validation message
10898 * Fires after the field has been validated with no errors.
10899 * @param {Roo.form.Field} this
10904 * Fires after the key up
10905 * @param {Roo.form.Field} this
10906 * @param {Roo.EventObject} e The event Object
10911 * Fires after the user pastes into input
10912 * @param {Roo.form.Field} this
10913 * @param {Roo.EventObject} e The event Object
10919 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
10921 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10922 automatic validation (defaults to "keyup").
10924 validationEvent : "keyup",
10926 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10928 validateOnBlur : true,
10930 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10932 validationDelay : 250,
10934 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10936 focusClass : "x-form-focus", // not needed???
10940 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10942 invalidClass : "has-warning",
10945 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10947 validClass : "has-success",
10950 * @cfg {Boolean} hasFeedback (true|false) default true
10952 hasFeedback : true,
10955 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10957 invalidFeedbackClass : "glyphicon-warning-sign",
10960 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10962 validFeedbackClass : "glyphicon-ok",
10965 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10967 selectOnFocus : false,
10970 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10974 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10979 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10981 disableKeyFilter : false,
10984 * @cfg {Boolean} disabled True to disable the field (defaults to false).
10988 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10992 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10994 blankText : "Please complete this mandatory field",
10997 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
11001 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
11003 maxLength : Number.MAX_VALUE,
11005 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
11007 minLengthText : "The minimum length for this field is {0}",
11009 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
11011 maxLengthText : "The maximum length for this field is {0}",
11015 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
11016 * If available, this function will be called only after the basic validators all return true, and will be passed the
11017 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
11021 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
11022 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
11023 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
11027 * @cfg {String} regexText -- Depricated - use Invalid Text
11032 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
11038 autocomplete: false,
11042 inputType : 'text',
11045 placeholder: false,
11050 preventMark: false,
11051 isFormField : true,
11054 labelAlign : false,
11057 formatedValue : false,
11058 forceFeedback : false,
11060 indicatorpos : 'left',
11070 parentLabelAlign : function()
11073 while (parent.parent()) {
11074 parent = parent.parent();
11075 if (typeof(parent.labelAlign) !='undefined') {
11076 return parent.labelAlign;
11083 getAutoCreate : function()
11085 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11091 if(this.inputType != 'hidden'){
11092 cfg.cls = 'form-group' //input-group
11098 type : this.inputType,
11099 value : this.value,
11100 cls : 'form-control',
11101 placeholder : this.placeholder || '',
11102 autocomplete : this.autocomplete || 'new-password'
11104 if (this.inputType == 'file') {
11105 input.style = 'overflow:hidden'; // why not in CSS?
11108 if(this.capture.length){
11109 input.capture = this.capture;
11112 if(this.accept.length){
11113 input.accept = this.accept + "/*";
11117 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
11120 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11121 input.maxLength = this.maxLength;
11124 if (this.disabled) {
11125 input.disabled=true;
11128 if (this.readOnly) {
11129 input.readonly=true;
11133 input.name = this.name;
11137 input.cls += ' input-' + this.size;
11141 ['xs','sm','md','lg'].map(function(size){
11142 if (settings[size]) {
11143 cfg.cls += ' col-' + size + '-' + settings[size];
11147 var inputblock = input;
11151 cls: 'glyphicon form-control-feedback'
11154 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11157 cls : 'has-feedback',
11165 if (this.before || this.after) {
11168 cls : 'input-group',
11172 if (this.before && typeof(this.before) == 'string') {
11174 inputblock.cn.push({
11176 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
11180 if (this.before && typeof(this.before) == 'object') {
11181 this.before = Roo.factory(this.before);
11183 inputblock.cn.push({
11185 cls : 'roo-input-before input-group-prepend input-group-' +
11186 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
11190 inputblock.cn.push(input);
11192 if (this.after && typeof(this.after) == 'string') {
11193 inputblock.cn.push({
11195 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
11199 if (this.after && typeof(this.after) == 'object') {
11200 this.after = Roo.factory(this.after);
11202 inputblock.cn.push({
11204 cls : 'roo-input-after input-group-append input-group-' +
11205 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
11209 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11210 inputblock.cls += ' has-feedback';
11211 inputblock.cn.push(feedback);
11216 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11217 tooltip : 'This field is required'
11219 if (this.allowBlank ) {
11220 indicator.style = this.allowBlank ? ' display:none' : '';
11222 if (align ==='left' && this.fieldLabel.length) {
11224 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
11231 cls : 'control-label col-form-label',
11232 html : this.fieldLabel
11243 var labelCfg = cfg.cn[1];
11244 var contentCfg = cfg.cn[2];
11246 if(this.indicatorpos == 'right'){
11251 cls : 'control-label col-form-label',
11255 html : this.fieldLabel
11269 labelCfg = cfg.cn[0];
11270 contentCfg = cfg.cn[1];
11274 if(this.labelWidth > 12){
11275 labelCfg.style = "width: " + this.labelWidth + 'px';
11278 if(this.labelWidth < 13 && this.labelmd == 0){
11279 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
11282 if(this.labellg > 0){
11283 labelCfg.cls += ' col-lg-' + this.labellg;
11284 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11287 if(this.labelmd > 0){
11288 labelCfg.cls += ' col-md-' + this.labelmd;
11289 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11292 if(this.labelsm > 0){
11293 labelCfg.cls += ' col-sm-' + this.labelsm;
11294 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11297 if(this.labelxs > 0){
11298 labelCfg.cls += ' col-xs-' + this.labelxs;
11299 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11303 } else if ( this.fieldLabel.length) {
11310 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
11311 tooltip : 'This field is required',
11312 style : this.allowBlank ? ' display:none' : ''
11316 //cls : 'input-group-addon',
11317 html : this.fieldLabel
11325 if(this.indicatorpos == 'right'){
11330 //cls : 'input-group-addon',
11331 html : this.fieldLabel
11336 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
11337 tooltip : 'This field is required',
11338 style : this.allowBlank ? ' display:none' : ''
11358 if (this.parentType === 'Navbar' && this.parent().bar) {
11359 cfg.cls += ' navbar-form';
11362 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
11363 // on BS4 we do this only if not form
11364 cfg.cls += ' navbar-form';
11372 * return the real input element.
11374 inputEl: function ()
11376 return this.el.select('input.form-control',true).first();
11379 tooltipEl : function()
11381 return this.inputEl();
11384 indicatorEl : function()
11386 if (Roo.bootstrap.version == 4) {
11387 return false; // not enabled in v4 yet.
11390 var indicator = this.el.select('i.roo-required-indicator',true).first();
11400 setDisabled : function(v)
11402 var i = this.inputEl().dom;
11404 i.removeAttribute('disabled');
11408 i.setAttribute('disabled','true');
11410 initEvents : function()
11413 this.inputEl().on("keydown" , this.fireKey, this);
11414 this.inputEl().on("focus", this.onFocus, this);
11415 this.inputEl().on("blur", this.onBlur, this);
11417 this.inputEl().relayEvent('keyup', this);
11418 this.inputEl().relayEvent('paste', this);
11420 this.indicator = this.indicatorEl();
11422 if(this.indicator){
11423 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
11426 // reference to original value for reset
11427 this.originalValue = this.getValue();
11428 //Roo.form.TextField.superclass.initEvents.call(this);
11429 if(this.validationEvent == 'keyup'){
11430 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11431 this.inputEl().on('keyup', this.filterValidation, this);
11433 else if(this.validationEvent !== false){
11434 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11437 if(this.selectOnFocus){
11438 this.on("focus", this.preFocus, this);
11441 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11442 this.inputEl().on("keypress", this.filterKeys, this);
11444 this.inputEl().relayEvent('keypress', this);
11447 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
11448 this.el.on("click", this.autoSize, this);
11451 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11452 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11455 if (typeof(this.before) == 'object') {
11456 this.before.render(this.el.select('.roo-input-before',true).first());
11458 if (typeof(this.after) == 'object') {
11459 this.after.render(this.el.select('.roo-input-after',true).first());
11462 this.inputEl().on('change', this.onChange, this);
11465 filterValidation : function(e){
11466 if(!e.isNavKeyPress()){
11467 this.validationTask.delay(this.validationDelay);
11471 * Validates the field value
11472 * @return {Boolean} True if the value is valid, else false
11474 validate : function(){
11475 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11476 if(this.disabled || this.validateValue(this.getRawValue())){
11481 this.markInvalid();
11487 * Validates a value according to the field's validation rules and marks the field as invalid
11488 * if the validation fails
11489 * @param {Mixed} value The value to validate
11490 * @return {Boolean} True if the value is valid, else false
11492 validateValue : function(value)
11494 if(this.getVisibilityEl().hasClass('hidden')){
11498 if(value.length < 1) { // if it's blank
11499 if(this.allowBlank){
11505 if(value.length < this.minLength){
11508 if(value.length > this.maxLength){
11512 var vt = Roo.form.VTypes;
11513 if(!vt[this.vtype](value, this)){
11517 if(typeof this.validator == "function"){
11518 var msg = this.validator(value);
11522 if (typeof(msg) == 'string') {
11523 this.invalidText = msg;
11527 if(this.regex && !this.regex.test(value)){
11535 fireKey : function(e){
11536 //Roo.log('field ' + e.getKey());
11537 if(e.isNavKeyPress()){
11538 this.fireEvent("specialkey", this, e);
11541 focus : function (selectText){
11543 this.inputEl().focus();
11544 if(selectText === true){
11545 this.inputEl().dom.select();
11551 onFocus : function(){
11552 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11553 // this.el.addClass(this.focusClass);
11555 if(!this.hasFocus){
11556 this.hasFocus = true;
11557 this.startValue = this.getValue();
11558 this.fireEvent("focus", this);
11562 beforeBlur : Roo.emptyFn,
11566 onBlur : function(){
11568 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11569 //this.el.removeClass(this.focusClass);
11571 this.hasFocus = false;
11572 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11575 var v = this.getValue();
11576 if(String(v) !== String(this.startValue)){
11577 this.fireEvent('change', this, v, this.startValue);
11579 this.fireEvent("blur", this);
11582 onChange : function(e)
11584 var v = this.getValue();
11585 if(String(v) !== String(this.startValue)){
11586 this.fireEvent('change', this, v, this.startValue);
11592 * Resets the current field value to the originally loaded value and clears any validation messages
11594 reset : function(){
11595 this.setValue(this.originalValue);
11599 * Returns the name of the field
11600 * @return {Mixed} name The name field
11602 getName: function(){
11606 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
11607 * @return {Mixed} value The field value
11609 getValue : function(){
11611 var v = this.inputEl().getValue();
11616 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
11617 * @return {Mixed} value The field value
11619 getRawValue : function(){
11620 var v = this.inputEl().getValue();
11626 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
11627 * @param {Mixed} value The value to set
11629 setRawValue : function(v){
11630 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11633 selectText : function(start, end){
11634 var v = this.getRawValue();
11636 start = start === undefined ? 0 : start;
11637 end = end === undefined ? v.length : end;
11638 var d = this.inputEl().dom;
11639 if(d.setSelectionRange){
11640 d.setSelectionRange(start, end);
11641 }else if(d.createTextRange){
11642 var range = d.createTextRange();
11643 range.moveStart("character", start);
11644 range.moveEnd("character", v.length-end);
11651 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
11652 * @param {Mixed} value The value to set
11654 setValue : function(v){
11657 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11663 processValue : function(value){
11664 if(this.stripCharsRe){
11665 var newValue = value.replace(this.stripCharsRe, '');
11666 if(newValue !== value){
11667 this.setRawValue(newValue);
11674 preFocus : function(){
11676 if(this.selectOnFocus){
11677 this.inputEl().dom.select();
11680 filterKeys : function(e){
11681 var k = e.getKey();
11682 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11685 var c = e.getCharCode(), cc = String.fromCharCode(c);
11686 if(Roo.isIE && (e.isSpecialKey() || !cc)){
11689 if(!this.maskRe.test(cc)){
11694 * Clear any invalid styles/messages for this field
11696 clearInvalid : function(){
11698 if(!this.el || this.preventMark){ // not rendered
11703 this.el.removeClass([this.invalidClass, 'is-invalid']);
11705 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11707 var feedback = this.el.select('.form-control-feedback', true).first();
11710 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11715 if(this.indicator){
11716 this.indicator.removeClass('visible');
11717 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11720 this.fireEvent('valid', this);
11724 * Mark this field as valid
11726 markValid : function()
11728 if(!this.el || this.preventMark){ // not rendered...
11732 this.el.removeClass([this.invalidClass, this.validClass]);
11733 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11735 var feedback = this.el.select('.form-control-feedback', true).first();
11738 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11741 if(this.indicator){
11742 this.indicator.removeClass('visible');
11743 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11751 if(this.allowBlank && !this.getRawValue().length){
11754 if (Roo.bootstrap.version == 3) {
11755 this.el.addClass(this.validClass);
11757 this.inputEl().addClass('is-valid');
11760 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11762 var feedback = this.el.select('.form-control-feedback', true).first();
11765 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11766 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11771 this.fireEvent('valid', this);
11775 * Mark this field as invalid
11776 * @param {String} msg The validation message
11778 markInvalid : function(msg)
11780 if(!this.el || this.preventMark){ // not rendered
11784 this.el.removeClass([this.invalidClass, this.validClass]);
11785 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11787 var feedback = this.el.select('.form-control-feedback', true).first();
11790 this.el.select('.form-control-feedback', true).first().removeClass(
11791 [this.invalidFeedbackClass, this.validFeedbackClass]);
11798 if(this.allowBlank && !this.getRawValue().length){
11802 if(this.indicator){
11803 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11804 this.indicator.addClass('visible');
11806 if (Roo.bootstrap.version == 3) {
11807 this.el.addClass(this.invalidClass);
11809 this.inputEl().addClass('is-invalid');
11814 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11816 var feedback = this.el.select('.form-control-feedback', true).first();
11819 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11821 if(this.getValue().length || this.forceFeedback){
11822 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11829 this.fireEvent('invalid', this, msg);
11832 SafariOnKeyDown : function(event)
11834 // this is a workaround for a password hang bug on chrome/ webkit.
11835 if (this.inputEl().dom.type != 'password') {
11839 var isSelectAll = false;
11841 if(this.inputEl().dom.selectionEnd > 0){
11842 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11844 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11845 event.preventDefault();
11850 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11852 event.preventDefault();
11853 // this is very hacky as keydown always get's upper case.
11855 var cc = String.fromCharCode(event.getCharCode());
11856 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
11860 adjustWidth : function(tag, w){
11861 tag = tag.toLowerCase();
11862 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11863 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11864 if(tag == 'input'){
11867 if(tag == 'textarea'){
11870 }else if(Roo.isOpera){
11871 if(tag == 'input'){
11874 if(tag == 'textarea'){
11882 setFieldLabel : function(v)
11884 if(!this.rendered){
11888 if(this.indicatorEl()){
11889 var ar = this.el.select('label > span',true);
11891 if (ar.elements.length) {
11892 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11893 this.fieldLabel = v;
11897 var br = this.el.select('label',true);
11899 if(br.elements.length) {
11900 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11901 this.fieldLabel = v;
11905 Roo.log('Cannot Found any of label > span || label in input');
11909 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11910 this.fieldLabel = v;
11925 * @class Roo.bootstrap.TextArea
11926 * @extends Roo.bootstrap.Input
11927 * Bootstrap TextArea class
11928 * @cfg {Number} cols Specifies the visible width of a text area
11929 * @cfg {Number} rows Specifies the visible number of lines in a text area
11930 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11931 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11932 * @cfg {string} html text
11935 * Create a new TextArea
11936 * @param {Object} config The config object
11939 Roo.bootstrap.TextArea = function(config){
11940 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11944 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
11954 getAutoCreate : function(){
11956 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11962 if(this.inputType != 'hidden'){
11963 cfg.cls = 'form-group' //input-group
11971 value : this.value || '',
11972 html: this.html || '',
11973 cls : 'form-control',
11974 placeholder : this.placeholder || ''
11978 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11979 input.maxLength = this.maxLength;
11983 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11987 input.cols = this.cols;
11990 if (this.readOnly) {
11991 input.readonly = true;
11995 input.name = this.name;
11999 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
12003 ['xs','sm','md','lg'].map(function(size){
12004 if (settings[size]) {
12005 cfg.cls += ' col-' + size + '-' + settings[size];
12009 var inputblock = input;
12011 if(this.hasFeedback && !this.allowBlank){
12015 cls: 'glyphicon form-control-feedback'
12019 cls : 'has-feedback',
12028 if (this.before || this.after) {
12031 cls : 'input-group',
12035 inputblock.cn.push({
12037 cls : 'input-group-addon',
12042 inputblock.cn.push(input);
12044 if(this.hasFeedback && !this.allowBlank){
12045 inputblock.cls += ' has-feedback';
12046 inputblock.cn.push(feedback);
12050 inputblock.cn.push({
12052 cls : 'input-group-addon',
12059 if (align ==='left' && this.fieldLabel.length) {
12064 cls : 'control-label',
12065 html : this.fieldLabel
12076 if(this.labelWidth > 12){
12077 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
12080 if(this.labelWidth < 13 && this.labelmd == 0){
12081 this.labelmd = this.labelWidth;
12084 if(this.labellg > 0){
12085 cfg.cn[0].cls += ' col-lg-' + this.labellg;
12086 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
12089 if(this.labelmd > 0){
12090 cfg.cn[0].cls += ' col-md-' + this.labelmd;
12091 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
12094 if(this.labelsm > 0){
12095 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
12096 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
12099 if(this.labelxs > 0){
12100 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
12101 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
12104 } else if ( this.fieldLabel.length) {
12109 //cls : 'input-group-addon',
12110 html : this.fieldLabel
12128 if (this.disabled) {
12129 input.disabled=true;
12136 * return the real textarea element.
12138 inputEl: function ()
12140 return this.el.select('textarea.form-control',true).first();
12144 * Clear any invalid styles/messages for this field
12146 clearInvalid : function()
12149 if(!this.el || this.preventMark){ // not rendered
12153 var label = this.el.select('label', true).first();
12154 var icon = this.el.select('i.fa-star', true).first();
12159 this.el.removeClass( this.validClass);
12160 this.inputEl().removeClass('is-invalid');
12162 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12164 var feedback = this.el.select('.form-control-feedback', true).first();
12167 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12172 this.fireEvent('valid', this);
12176 * Mark this field as valid
12178 markValid : function()
12180 if(!this.el || this.preventMark){ // not rendered
12184 this.el.removeClass([this.invalidClass, this.validClass]);
12185 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12187 var feedback = this.el.select('.form-control-feedback', true).first();
12190 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12193 if(this.disabled || this.allowBlank){
12197 var label = this.el.select('label', true).first();
12198 var icon = this.el.select('i.fa-star', true).first();
12203 if (Roo.bootstrap.version == 3) {
12204 this.el.addClass(this.validClass);
12206 this.inputEl().addClass('is-valid');
12210 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12212 var feedback = this.el.select('.form-control-feedback', true).first();
12215 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12216 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12221 this.fireEvent('valid', this);
12225 * Mark this field as invalid
12226 * @param {String} msg The validation message
12228 markInvalid : function(msg)
12230 if(!this.el || this.preventMark){ // not rendered
12234 this.el.removeClass([this.invalidClass, this.validClass]);
12235 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12237 var feedback = this.el.select('.form-control-feedback', true).first();
12240 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12243 if(this.disabled || this.allowBlank){
12247 var label = this.el.select('label', true).first();
12248 var icon = this.el.select('i.fa-star', true).first();
12250 if(!this.getValue().length && label && !icon){
12251 this.el.createChild({
12253 cls : 'text-danger fa fa-lg fa-star',
12254 tooltip : 'This field is required',
12255 style : 'margin-right:5px;'
12259 if (Roo.bootstrap.version == 3) {
12260 this.el.addClass(this.invalidClass);
12262 this.inputEl().addClass('is-invalid');
12265 // fixme ... this may be depricated need to test..
12266 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12268 var feedback = this.el.select('.form-control-feedback', true).first();
12271 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12273 if(this.getValue().length || this.forceFeedback){
12274 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12281 this.fireEvent('invalid', this, msg);
12289 * trigger field - base class for combo..
12294 * @class Roo.bootstrap.TriggerField
12295 * @extends Roo.bootstrap.Input
12296 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
12297 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
12298 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
12299 * for which you can provide a custom implementation. For example:
12301 var trigger = new Roo.bootstrap.TriggerField();
12302 trigger.onTriggerClick = myTriggerFn;
12303 trigger.applyTo('my-field');
12306 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
12307 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
12308 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
12309 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
12310 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
12313 * Create a new TriggerField.
12314 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
12315 * to the base TextField)
12317 Roo.bootstrap.TriggerField = function(config){
12318 this.mimicing = false;
12319 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
12322 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
12324 * @cfg {String} triggerClass A CSS class to apply to the trigger
12327 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
12332 * @cfg {Boolean} removable (true|false) special filter default false
12336 /** @cfg {Boolean} grow @hide */
12337 /** @cfg {Number} growMin @hide */
12338 /** @cfg {Number} growMax @hide */
12344 autoSize: Roo.emptyFn,
12348 deferHeight : true,
12351 actionMode : 'wrap',
12356 getAutoCreate : function(){
12358 var align = this.labelAlign || this.parentLabelAlign();
12363 cls: 'form-group' //input-group
12370 type : this.inputType,
12371 cls : 'form-control',
12372 autocomplete: 'new-password',
12373 placeholder : this.placeholder || ''
12377 input.name = this.name;
12380 input.cls += ' input-' + this.size;
12383 if (this.disabled) {
12384 input.disabled=true;
12387 var inputblock = input;
12389 if(this.hasFeedback && !this.allowBlank){
12393 cls: 'glyphicon form-control-feedback'
12396 if(this.removable && !this.editable ){
12398 cls : 'has-feedback',
12404 cls : 'roo-combo-removable-btn close'
12411 cls : 'has-feedback',
12420 if(this.removable && !this.editable ){
12422 cls : 'roo-removable',
12428 cls : 'roo-combo-removable-btn close'
12435 if (this.before || this.after) {
12438 cls : 'input-group',
12442 inputblock.cn.push({
12444 cls : 'input-group-addon input-group-prepend input-group-text',
12449 inputblock.cn.push(input);
12451 if(this.hasFeedback && !this.allowBlank){
12452 inputblock.cls += ' has-feedback';
12453 inputblock.cn.push(feedback);
12457 inputblock.cn.push({
12459 cls : 'input-group-addon input-group-append input-group-text',
12468 var ibwrap = inputblock;
12473 cls: 'roo-select2-choices',
12477 cls: 'roo-select2-search-field',
12489 cls: 'roo-select2-container input-group',
12494 cls: 'form-hidden-field'
12500 if(!this.multiple && this.showToggleBtn){
12506 if (this.caret != false) {
12509 cls: 'fa fa-' + this.caret
12516 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12518 Roo.bootstrap.version == 3 ? caret : '',
12521 cls: 'combobox-clear',
12535 combobox.cls += ' roo-select2-container-multi';
12539 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12540 tooltip : 'This field is required'
12542 if (Roo.bootstrap.version == 4) {
12545 style : 'display:none'
12550 if (align ==='left' && this.fieldLabel.length) {
12552 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12559 cls : 'control-label',
12560 html : this.fieldLabel
12572 var labelCfg = cfg.cn[1];
12573 var contentCfg = cfg.cn[2];
12575 if(this.indicatorpos == 'right'){
12580 cls : 'control-label',
12584 html : this.fieldLabel
12598 labelCfg = cfg.cn[0];
12599 contentCfg = cfg.cn[1];
12602 if(this.labelWidth > 12){
12603 labelCfg.style = "width: " + this.labelWidth + 'px';
12606 if(this.labelWidth < 13 && this.labelmd == 0){
12607 this.labelmd = this.labelWidth;
12610 if(this.labellg > 0){
12611 labelCfg.cls += ' col-lg-' + this.labellg;
12612 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12615 if(this.labelmd > 0){
12616 labelCfg.cls += ' col-md-' + this.labelmd;
12617 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12620 if(this.labelsm > 0){
12621 labelCfg.cls += ' col-sm-' + this.labelsm;
12622 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12625 if(this.labelxs > 0){
12626 labelCfg.cls += ' col-xs-' + this.labelxs;
12627 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12630 } else if ( this.fieldLabel.length) {
12631 // Roo.log(" label");
12636 //cls : 'input-group-addon',
12637 html : this.fieldLabel
12645 if(this.indicatorpos == 'right'){
12653 html : this.fieldLabel
12667 // Roo.log(" no label && no align");
12674 ['xs','sm','md','lg'].map(function(size){
12675 if (settings[size]) {
12676 cfg.cls += ' col-' + size + '-' + settings[size];
12687 onResize : function(w, h){
12688 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12689 // if(typeof w == 'number'){
12690 // var x = w - this.trigger.getWidth();
12691 // this.inputEl().setWidth(this.adjustWidth('input', x));
12692 // this.trigger.setStyle('left', x+'px');
12697 adjustSize : Roo.BoxComponent.prototype.adjustSize,
12700 getResizeEl : function(){
12701 return this.inputEl();
12705 getPositionEl : function(){
12706 return this.inputEl();
12710 alignErrorIcon : function(){
12711 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12715 initEvents : function(){
12719 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12720 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12721 if(!this.multiple && this.showToggleBtn){
12722 this.trigger = this.el.select('span.dropdown-toggle',true).first();
12723 if(this.hideTrigger){
12724 this.trigger.setDisplayed(false);
12726 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12730 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12733 if(this.removable && !this.editable && !this.tickable){
12734 var close = this.closeTriggerEl();
12737 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12738 close.on('click', this.removeBtnClick, this, close);
12742 //this.trigger.addClassOnOver('x-form-trigger-over');
12743 //this.trigger.addClassOnClick('x-form-trigger-click');
12746 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12750 closeTriggerEl : function()
12752 var close = this.el.select('.roo-combo-removable-btn', true).first();
12753 return close ? close : false;
12756 removeBtnClick : function(e, h, el)
12758 e.preventDefault();
12760 if(this.fireEvent("remove", this) !== false){
12762 this.fireEvent("afterremove", this)
12766 createList : function()
12768 this.list = Roo.get(document.body).createChild({
12769 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12770 cls: 'typeahead typeahead-long dropdown-menu shadow',
12771 style: 'display:none'
12774 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12779 initTrigger : function(){
12784 onDestroy : function(){
12786 this.trigger.removeAllListeners();
12787 // this.trigger.remove();
12790 // this.wrap.remove();
12792 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12796 onFocus : function(){
12797 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12799 if(!this.mimicing){
12800 this.wrap.addClass('x-trigger-wrap-focus');
12801 this.mimicing = true;
12802 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12803 if(this.monitorTab){
12804 this.el.on("keydown", this.checkTab, this);
12811 checkTab : function(e){
12812 if(e.getKey() == e.TAB){
12813 this.triggerBlur();
12818 onBlur : function(){
12823 mimicBlur : function(e, t){
12825 if(!this.wrap.contains(t) && this.validateBlur()){
12826 this.triggerBlur();
12832 triggerBlur : function(){
12833 this.mimicing = false;
12834 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12835 if(this.monitorTab){
12836 this.el.un("keydown", this.checkTab, this);
12838 //this.wrap.removeClass('x-trigger-wrap-focus');
12839 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12843 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12844 validateBlur : function(e, t){
12849 onDisable : function(){
12850 this.inputEl().dom.disabled = true;
12851 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12853 // this.wrap.addClass('x-item-disabled');
12858 onEnable : function(){
12859 this.inputEl().dom.disabled = false;
12860 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12862 // this.el.removeClass('x-item-disabled');
12867 onShow : function(){
12868 var ae = this.getActionEl();
12871 ae.dom.style.display = '';
12872 ae.dom.style.visibility = 'visible';
12878 onHide : function(){
12879 var ae = this.getActionEl();
12880 ae.dom.style.display = 'none';
12884 * The function that should handle the trigger's click event. This method does nothing by default until overridden
12885 * by an implementing function.
12887 * @param {EventObject} e
12889 onTriggerClick : Roo.emptyFn
12897 * @class Roo.bootstrap.CardUploader
12898 * @extends Roo.bootstrap.Button
12899 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12900 * @cfg {Number} errorTimeout default 3000
12901 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
12902 * @cfg {Array} html The button text.
12906 * Create a new CardUploader
12907 * @param {Object} config The config object
12910 Roo.bootstrap.CardUploader = function(config){
12914 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12917 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
12925 * When a image is clicked on - and needs to display a slideshow or similar..
12926 * @param {Roo.bootstrap.Card} this
12927 * @param {Object} The image information data
12933 * When a the download link is clicked
12934 * @param {Roo.bootstrap.Card} this
12935 * @param {Object} The image information data contains
12942 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
12945 errorTimeout : 3000,
12949 fileCollection : false,
12952 getAutoCreate : function()
12956 cls :'form-group' ,
12961 //cls : 'input-group-addon',
12962 html : this.fieldLabel
12970 value : this.value,
12971 cls : 'd-none form-control'
12976 multiple : 'multiple',
12978 cls : 'd-none roo-card-upload-selector'
12982 cls : 'roo-card-uploader-button-container w-100 mb-2'
12985 cls : 'card-columns roo-card-uploader-container'
12995 getChildContainer : function() /// what children are added to.
12997 return this.containerEl;
13000 getButtonContainer : function() /// what children are added to.
13002 return this.el.select(".roo-card-uploader-button-container").first();
13005 initEvents : function()
13008 Roo.bootstrap.Input.prototype.initEvents.call(this);
13012 xns: Roo.bootstrap,
13015 container_method : 'getButtonContainer' ,
13016 html : this.html, // fix changable?
13019 'click' : function(btn, e) {
13028 this.urlAPI = (window.createObjectURL && window) ||
13029 (window.URL && URL.revokeObjectURL && URL) ||
13030 (window.webkitURL && webkitURL);
13035 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
13037 this.selectorEl.on('change', this.onFileSelected, this);
13040 this.images.forEach(function(img) {
13043 this.images = false;
13045 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
13051 onClick : function(e)
13053 e.preventDefault();
13055 this.selectorEl.dom.click();
13059 onFileSelected : function(e)
13061 e.preventDefault();
13063 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
13067 Roo.each(this.selectorEl.dom.files, function(file){
13068 this.addFile(file);
13077 addFile : function(file)
13080 if(typeof(file) === 'string'){
13081 throw "Add file by name?"; // should not happen
13085 if(!file || !this.urlAPI){
13095 var url = _this.urlAPI.createObjectURL( file);
13098 id : Roo.bootstrap.CardUploader.ID--,
13099 is_uploaded : false,
13103 mimetype : file.type,
13111 * addCard - add an Attachment to the uploader
13112 * @param data - the data about the image to upload
13116 title : "Title of file",
13117 is_uploaded : false,
13118 src : "http://.....",
13119 srcfile : { the File upload object },
13120 mimetype : file.type,
13123 .. any other data...
13129 addCard : function (data)
13131 // hidden input element?
13132 // if the file is not an image...
13133 //then we need to use something other that and header_image
13138 xns : Roo.bootstrap,
13139 xtype : 'CardFooter',
13142 xns : Roo.bootstrap,
13148 xns : Roo.bootstrap,
13150 html : String.format("<small>{0}</small>", data.title),
13151 cls : 'col-10 text-left',
13156 click : function() {
13158 t.fireEvent( "download", t, data );
13164 xns : Roo.bootstrap,
13166 style: 'max-height: 28px; ',
13172 click : function() {
13173 t.removeCard(data.id)
13185 var cn = this.addxtype(
13188 xns : Roo.bootstrap,
13191 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
13192 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
13193 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
13198 initEvents : function() {
13199 Roo.bootstrap.Card.prototype.initEvents.call(this);
13201 this.imgEl = this.el.select('.card-img-top').first();
13203 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
13204 this.imgEl.set({ 'pointer' : 'cursor' });
13207 this.getCardFooter().addClass('p-1');
13214 // dont' really need ot update items.
13215 // this.items.push(cn);
13216 this.fileCollection.add(cn);
13218 if (!data.srcfile) {
13219 this.updateInput();
13224 var reader = new FileReader();
13225 reader.addEventListener("load", function() {
13226 data.srcdata = reader.result;
13229 reader.readAsDataURL(data.srcfile);
13234 removeCard : function(id)
13237 var card = this.fileCollection.get(id);
13238 card.data.is_deleted = 1;
13239 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
13240 //this.fileCollection.remove(card);
13241 //this.items = this.items.filter(function(e) { return e != card });
13242 // dont' really need ot update items.
13243 card.el.dom.parentNode.removeChild(card.el.dom);
13244 this.updateInput();
13250 this.fileCollection.each(function(card) {
13251 if (card.el.dom && card.el.dom.parentNode) {
13252 card.el.dom.parentNode.removeChild(card.el.dom);
13255 this.fileCollection.clear();
13256 this.updateInput();
13259 updateInput : function()
13262 this.fileCollection.each(function(e) {
13266 this.inputEl().dom.value = JSON.stringify(data);
13276 Roo.bootstrap.CardUploader.ID = -1;/*
13278 * Ext JS Library 1.1.1
13279 * Copyright(c) 2006-2007, Ext JS, LLC.
13281 * Originally Released Under LGPL - original licence link has changed is not relivant.
13284 * <script type="text/javascript">
13289 * @class Roo.data.SortTypes
13291 * Defines the default sorting (casting?) comparison functions used when sorting data.
13293 Roo.data.SortTypes = {
13295 * Default sort that does nothing
13296 * @param {Mixed} s The value being converted
13297 * @return {Mixed} The comparison value
13299 none : function(s){
13304 * The regular expression used to strip tags
13308 stripTagsRE : /<\/?[^>]+>/gi,
13311 * Strips all HTML tags to sort on text only
13312 * @param {Mixed} s The value being converted
13313 * @return {String} The comparison value
13315 asText : function(s){
13316 return String(s).replace(this.stripTagsRE, "");
13320 * Strips all HTML tags to sort on text only - Case insensitive
13321 * @param {Mixed} s The value being converted
13322 * @return {String} The comparison value
13324 asUCText : function(s){
13325 return String(s).toUpperCase().replace(this.stripTagsRE, "");
13329 * Case insensitive string
13330 * @param {Mixed} s The value being converted
13331 * @return {String} The comparison value
13333 asUCString : function(s) {
13334 return String(s).toUpperCase();
13339 * @param {Mixed} s The value being converted
13340 * @return {Number} The comparison value
13342 asDate : function(s) {
13346 if(s instanceof Date){
13347 return s.getTime();
13349 return Date.parse(String(s));
13354 * @param {Mixed} s The value being converted
13355 * @return {Float} The comparison value
13357 asFloat : function(s) {
13358 var val = parseFloat(String(s).replace(/,/g, ""));
13367 * @param {Mixed} s The value being converted
13368 * @return {Number} The comparison value
13370 asInt : function(s) {
13371 var val = parseInt(String(s).replace(/,/g, ""));
13379 * Ext JS Library 1.1.1
13380 * Copyright(c) 2006-2007, Ext JS, LLC.
13382 * Originally Released Under LGPL - original licence link has changed is not relivant.
13385 * <script type="text/javascript">
13389 * @class Roo.data.Record
13390 * Instances of this class encapsulate both record <em>definition</em> information, and record
13391 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13392 * to access Records cached in an {@link Roo.data.Store} object.<br>
13394 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13395 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13398 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13400 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13401 * {@link #create}. The parameters are the same.
13402 * @param {Array} data An associative Array of data values keyed by the field name.
13403 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13404 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13405 * not specified an integer id is generated.
13407 Roo.data.Record = function(data, id){
13408 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13413 * Generate a constructor for a specific record layout.
13414 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13415 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13416 * Each field definition object may contain the following properties: <ul>
13417 * <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,
13418 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13419 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13420 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13421 * is being used, then this is a string containing the javascript expression to reference the data relative to
13422 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13423 * to the data item relative to the record element. If the mapping expression is the same as the field name,
13424 * this may be omitted.</p></li>
13425 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13426 * <ul><li>auto (Default, implies no conversion)</li>
13431 * <li>date</li></ul></p></li>
13432 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13433 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13434 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13435 * by the Reader into an object that will be stored in the Record. It is passed the
13436 * following parameters:<ul>
13437 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13439 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13441 * <br>usage:<br><pre><code>
13442 var TopicRecord = Roo.data.Record.create(
13443 {name: 'title', mapping: 'topic_title'},
13444 {name: 'author', mapping: 'username'},
13445 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13446 {name: 'lastPost', mapping: 'post_time', type: 'date'},
13447 {name: 'lastPoster', mapping: 'user2'},
13448 {name: 'excerpt', mapping: 'post_text'}
13451 var myNewRecord = new TopicRecord({
13452 title: 'Do my job please',
13455 lastPost: new Date(),
13456 lastPoster: 'Animal',
13457 excerpt: 'No way dude!'
13459 myStore.add(myNewRecord);
13464 Roo.data.Record.create = function(o){
13465 var f = function(){
13466 f.superclass.constructor.apply(this, arguments);
13468 Roo.extend(f, Roo.data.Record);
13469 var p = f.prototype;
13470 p.fields = new Roo.util.MixedCollection(false, function(field){
13473 for(var i = 0, len = o.length; i < len; i++){
13474 p.fields.add(new Roo.data.Field(o[i]));
13476 f.getField = function(name){
13477 return p.fields.get(name);
13482 Roo.data.Record.AUTO_ID = 1000;
13483 Roo.data.Record.EDIT = 'edit';
13484 Roo.data.Record.REJECT = 'reject';
13485 Roo.data.Record.COMMIT = 'commit';
13487 Roo.data.Record.prototype = {
13489 * Readonly flag - true if this record has been modified.
13498 join : function(store){
13499 this.store = store;
13503 * Set the named field to the specified value.
13504 * @param {String} name The name of the field to set.
13505 * @param {Object} value The value to set the field to.
13507 set : function(name, value){
13508 if(this.data[name] == value){
13512 if(!this.modified){
13513 this.modified = {};
13515 if(typeof this.modified[name] == 'undefined'){
13516 this.modified[name] = this.data[name];
13518 this.data[name] = value;
13519 if(!this.editing && this.store){
13520 this.store.afterEdit(this);
13525 * Get the value of the named field.
13526 * @param {String} name The name of the field to get the value of.
13527 * @return {Object} The value of the field.
13529 get : function(name){
13530 return this.data[name];
13534 beginEdit : function(){
13535 this.editing = true;
13536 this.modified = {};
13540 cancelEdit : function(){
13541 this.editing = false;
13542 delete this.modified;
13546 endEdit : function(){
13547 this.editing = false;
13548 if(this.dirty && this.store){
13549 this.store.afterEdit(this);
13554 * Usually called by the {@link Roo.data.Store} which owns the Record.
13555 * Rejects all changes made to the Record since either creation, or the last commit operation.
13556 * Modified fields are reverted to their original values.
13558 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13559 * of reject operations.
13561 reject : function(){
13562 var m = this.modified;
13564 if(typeof m[n] != "function"){
13565 this.data[n] = m[n];
13568 this.dirty = false;
13569 delete this.modified;
13570 this.editing = false;
13572 this.store.afterReject(this);
13577 * Usually called by the {@link Roo.data.Store} which owns the Record.
13578 * Commits all changes made to the Record since either creation, or the last commit operation.
13580 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13581 * of commit operations.
13583 commit : function(){
13584 this.dirty = false;
13585 delete this.modified;
13586 this.editing = false;
13588 this.store.afterCommit(this);
13593 hasError : function(){
13594 return this.error != null;
13598 clearError : function(){
13603 * Creates a copy of this record.
13604 * @param {String} id (optional) A new record id if you don't want to use this record's id
13607 copy : function(newId) {
13608 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13612 * Ext JS Library 1.1.1
13613 * Copyright(c) 2006-2007, Ext JS, LLC.
13615 * Originally Released Under LGPL - original licence link has changed is not relivant.
13618 * <script type="text/javascript">
13624 * @class Roo.data.Store
13625 * @extends Roo.util.Observable
13626 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13627 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13629 * 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
13630 * has no knowledge of the format of the data returned by the Proxy.<br>
13632 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13633 * instances from the data object. These records are cached and made available through accessor functions.
13635 * Creates a new Store.
13636 * @param {Object} config A config object containing the objects needed for the Store to access data,
13637 * and read the data into Records.
13639 Roo.data.Store = function(config){
13640 this.data = new Roo.util.MixedCollection(false);
13641 this.data.getKey = function(o){
13644 this.baseParams = {};
13646 this.paramNames = {
13651 "multisort" : "_multisort"
13654 if(config && config.data){
13655 this.inlineData = config.data;
13656 delete config.data;
13659 Roo.apply(this, config);
13661 if(this.reader){ // reader passed
13662 this.reader = Roo.factory(this.reader, Roo.data);
13663 this.reader.xmodule = this.xmodule || false;
13664 if(!this.recordType){
13665 this.recordType = this.reader.recordType;
13667 if(this.reader.onMetaChange){
13668 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13672 if(this.recordType){
13673 this.fields = this.recordType.prototype.fields;
13675 this.modified = [];
13679 * @event datachanged
13680 * Fires when the data cache has changed, and a widget which is using this Store
13681 * as a Record cache should refresh its view.
13682 * @param {Store} this
13684 datachanged : true,
13686 * @event metachange
13687 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13688 * @param {Store} this
13689 * @param {Object} meta The JSON metadata
13694 * Fires when Records have been added to the Store
13695 * @param {Store} this
13696 * @param {Roo.data.Record[]} records The array of Records added
13697 * @param {Number} index The index at which the record(s) were added
13702 * Fires when a Record has been removed from the Store
13703 * @param {Store} this
13704 * @param {Roo.data.Record} record The Record that was removed
13705 * @param {Number} index The index at which the record was removed
13710 * Fires when a Record has been updated
13711 * @param {Store} this
13712 * @param {Roo.data.Record} record The Record that was updated
13713 * @param {String} operation The update operation being performed. Value may be one of:
13715 Roo.data.Record.EDIT
13716 Roo.data.Record.REJECT
13717 Roo.data.Record.COMMIT
13723 * Fires when the data cache has been cleared.
13724 * @param {Store} this
13728 * @event beforeload
13729 * Fires before a request is made for a new data object. If the beforeload handler returns false
13730 * the load action will be canceled.
13731 * @param {Store} this
13732 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13736 * @event beforeloadadd
13737 * Fires after a new set of Records has been loaded.
13738 * @param {Store} this
13739 * @param {Roo.data.Record[]} records The Records that were loaded
13740 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13742 beforeloadadd : true,
13745 * Fires after a new set of Records has been loaded, before they are added to the store.
13746 * @param {Store} this
13747 * @param {Roo.data.Record[]} records The Records that were loaded
13748 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13749 * @params {Object} return from reader
13753 * @event loadexception
13754 * Fires if an exception occurs in the Proxy during loading.
13755 * Called with the signature of the Proxy's "loadexception" event.
13756 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13759 * @param {Object} return from JsonData.reader() - success, totalRecords, records
13760 * @param {Object} load options
13761 * @param {Object} jsonData from your request (normally this contains the Exception)
13763 loadexception : true
13767 this.proxy = Roo.factory(this.proxy, Roo.data);
13768 this.proxy.xmodule = this.xmodule || false;
13769 this.relayEvents(this.proxy, ["loadexception"]);
13771 this.sortToggle = {};
13772 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13774 Roo.data.Store.superclass.constructor.call(this);
13776 if(this.inlineData){
13777 this.loadData(this.inlineData);
13778 delete this.inlineData;
13782 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13784 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
13785 * without a remote query - used by combo/forms at present.
13789 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13792 * @cfg {Array} data Inline data to be loaded when the store is initialized.
13795 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13796 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13799 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13800 * on any HTTP request
13803 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13806 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13810 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13811 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13813 remoteSort : false,
13816 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13817 * loaded or when a record is removed. (defaults to false).
13819 pruneModifiedRecords : false,
13822 lastOptions : null,
13825 * Add Records to the Store and fires the add event.
13826 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13828 add : function(records){
13829 records = [].concat(records);
13830 for(var i = 0, len = records.length; i < len; i++){
13831 records[i].join(this);
13833 var index = this.data.length;
13834 this.data.addAll(records);
13835 this.fireEvent("add", this, records, index);
13839 * Remove a Record from the Store and fires the remove event.
13840 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13842 remove : function(record){
13843 var index = this.data.indexOf(record);
13844 this.data.removeAt(index);
13846 if(this.pruneModifiedRecords){
13847 this.modified.remove(record);
13849 this.fireEvent("remove", this, record, index);
13853 * Remove all Records from the Store and fires the clear event.
13855 removeAll : function(){
13857 if(this.pruneModifiedRecords){
13858 this.modified = [];
13860 this.fireEvent("clear", this);
13864 * Inserts Records to the Store at the given index and fires the add event.
13865 * @param {Number} index The start index at which to insert the passed Records.
13866 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13868 insert : function(index, records){
13869 records = [].concat(records);
13870 for(var i = 0, len = records.length; i < len; i++){
13871 this.data.insert(index, records[i]);
13872 records[i].join(this);
13874 this.fireEvent("add", this, records, index);
13878 * Get the index within the cache of the passed Record.
13879 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13880 * @return {Number} The index of the passed Record. Returns -1 if not found.
13882 indexOf : function(record){
13883 return this.data.indexOf(record);
13887 * Get the index within the cache of the Record with the passed id.
13888 * @param {String} id The id of the Record to find.
13889 * @return {Number} The index of the Record. Returns -1 if not found.
13891 indexOfId : function(id){
13892 return this.data.indexOfKey(id);
13896 * Get the Record with the specified id.
13897 * @param {String} id The id of the Record to find.
13898 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13900 getById : function(id){
13901 return this.data.key(id);
13905 * Get the Record at the specified index.
13906 * @param {Number} index The index of the Record to find.
13907 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13909 getAt : function(index){
13910 return this.data.itemAt(index);
13914 * Returns a range of Records between specified indices.
13915 * @param {Number} startIndex (optional) The starting index (defaults to 0)
13916 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13917 * @return {Roo.data.Record[]} An array of Records
13919 getRange : function(start, end){
13920 return this.data.getRange(start, end);
13924 storeOptions : function(o){
13925 o = Roo.apply({}, o);
13928 this.lastOptions = o;
13932 * Loads the Record cache from the configured Proxy using the configured Reader.
13934 * If using remote paging, then the first load call must specify the <em>start</em>
13935 * and <em>limit</em> properties in the options.params property to establish the initial
13936 * position within the dataset, and the number of Records to cache on each read from the Proxy.
13938 * <strong>It is important to note that for remote data sources, loading is asynchronous,
13939 * and this call will return before the new data has been loaded. Perform any post-processing
13940 * in a callback function, or in a "load" event handler.</strong>
13942 * @param {Object} options An object containing properties which control loading options:<ul>
13943 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13944 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13945 * passed the following arguments:<ul>
13946 * <li>r : Roo.data.Record[]</li>
13947 * <li>options: Options object from the load call</li>
13948 * <li>success: Boolean success indicator</li></ul></li>
13949 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13950 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13953 load : function(options){
13954 options = options || {};
13955 if(this.fireEvent("beforeload", this, options) !== false){
13956 this.storeOptions(options);
13957 var p = Roo.apply(options.params || {}, this.baseParams);
13958 // if meta was not loaded from remote source.. try requesting it.
13959 if (!this.reader.metaFromRemote) {
13960 p._requestMeta = 1;
13962 if(this.sortInfo && this.remoteSort){
13963 var pn = this.paramNames;
13964 p[pn["sort"]] = this.sortInfo.field;
13965 p[pn["dir"]] = this.sortInfo.direction;
13967 if (this.multiSort) {
13968 var pn = this.paramNames;
13969 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13972 this.proxy.load(p, this.reader, this.loadRecords, this, options);
13977 * Reloads the Record cache from the configured Proxy using the configured Reader and
13978 * the options from the last load operation performed.
13979 * @param {Object} options (optional) An object containing properties which may override the options
13980 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13981 * the most recently used options are reused).
13983 reload : function(options){
13984 this.load(Roo.applyIf(options||{}, this.lastOptions));
13988 // Called as a callback by the Reader during a load operation.
13989 loadRecords : function(o, options, success){
13990 if(!o || success === false){
13991 if(success !== false){
13992 this.fireEvent("load", this, [], options, o);
13994 if(options.callback){
13995 options.callback.call(options.scope || this, [], options, false);
13999 // if data returned failure - throw an exception.
14000 if (o.success === false) {
14001 // show a message if no listener is registered.
14002 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
14003 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
14005 // loadmask wil be hooked into this..
14006 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
14009 var r = o.records, t = o.totalRecords || r.length;
14011 this.fireEvent("beforeloadadd", this, r, options, o);
14013 if(!options || options.add !== true){
14014 if(this.pruneModifiedRecords){
14015 this.modified = [];
14017 for(var i = 0, len = r.length; i < len; i++){
14021 this.data = this.snapshot;
14022 delete this.snapshot;
14025 this.data.addAll(r);
14026 this.totalLength = t;
14028 this.fireEvent("datachanged", this);
14030 this.totalLength = Math.max(t, this.data.length+r.length);
14034 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
14036 var e = new Roo.data.Record({});
14038 e.set(this.parent.displayField, this.parent.emptyTitle);
14039 e.set(this.parent.valueField, '');
14044 this.fireEvent("load", this, r, options, o);
14045 if(options.callback){
14046 options.callback.call(options.scope || this, r, options, true);
14052 * Loads data from a passed data block. A Reader which understands the format of the data
14053 * must have been configured in the constructor.
14054 * @param {Object} data The data block from which to read the Records. The format of the data expected
14055 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
14056 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
14058 loadData : function(o, append){
14059 var r = this.reader.readRecords(o);
14060 this.loadRecords(r, {add: append}, true);
14064 * using 'cn' the nested child reader read the child array into it's child stores.
14065 * @param {Object} rec The record with a 'children array
14067 loadDataFromChildren : function(rec)
14069 this.loadData(this.reader.toLoadData(rec));
14074 * Gets the number of cached records.
14076 * <em>If using paging, this may not be the total size of the dataset. If the data object
14077 * used by the Reader contains the dataset size, then the getTotalCount() function returns
14078 * the data set size</em>
14080 getCount : function(){
14081 return this.data.length || 0;
14085 * Gets the total number of records in the dataset as returned by the server.
14087 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
14088 * the dataset size</em>
14090 getTotalCount : function(){
14091 return this.totalLength || 0;
14095 * Returns the sort state of the Store as an object with two properties:
14097 field {String} The name of the field by which the Records are sorted
14098 direction {String} The sort order, "ASC" or "DESC"
14101 getSortState : function(){
14102 return this.sortInfo;
14106 applySort : function(){
14107 if(this.sortInfo && !this.remoteSort){
14108 var s = this.sortInfo, f = s.field;
14109 var st = this.fields.get(f).sortType;
14110 var fn = function(r1, r2){
14111 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
14112 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
14114 this.data.sort(s.direction, fn);
14115 if(this.snapshot && this.snapshot != this.data){
14116 this.snapshot.sort(s.direction, fn);
14122 * Sets the default sort column and order to be used by the next load operation.
14123 * @param {String} fieldName The name of the field to sort by.
14124 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14126 setDefaultSort : function(field, dir){
14127 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
14131 * Sort the Records.
14132 * If remote sorting is used, the sort is performed on the server, and the cache is
14133 * reloaded. If local sorting is used, the cache is sorted internally.
14134 * @param {String} fieldName The name of the field to sort by.
14135 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14137 sort : function(fieldName, dir){
14138 var f = this.fields.get(fieldName);
14140 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
14142 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
14143 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
14148 this.sortToggle[f.name] = dir;
14149 this.sortInfo = {field: f.name, direction: dir};
14150 if(!this.remoteSort){
14152 this.fireEvent("datachanged", this);
14154 this.load(this.lastOptions);
14159 * Calls the specified function for each of the Records in the cache.
14160 * @param {Function} fn The function to call. The Record is passed as the first parameter.
14161 * Returning <em>false</em> aborts and exits the iteration.
14162 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
14164 each : function(fn, scope){
14165 this.data.each(fn, scope);
14169 * Gets all records modified since the last commit. Modified records are persisted across load operations
14170 * (e.g., during paging).
14171 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
14173 getModifiedRecords : function(){
14174 return this.modified;
14178 createFilterFn : function(property, value, anyMatch){
14179 if(!value.exec){ // not a regex
14180 value = String(value);
14181 if(value.length == 0){
14184 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
14186 return function(r){
14187 return value.test(r.data[property]);
14192 * Sums the value of <i>property</i> for each record between start and end and returns the result.
14193 * @param {String} property A field on your records
14194 * @param {Number} start The record index to start at (defaults to 0)
14195 * @param {Number} end The last record index to include (defaults to length - 1)
14196 * @return {Number} The sum
14198 sum : function(property, start, end){
14199 var rs = this.data.items, v = 0;
14200 start = start || 0;
14201 end = (end || end === 0) ? end : rs.length-1;
14203 for(var i = start; i <= end; i++){
14204 v += (rs[i].data[property] || 0);
14210 * Filter the records by a specified property.
14211 * @param {String} field A field on your records
14212 * @param {String/RegExp} value Either a string that the field
14213 * should start with or a RegExp to test against the field
14214 * @param {Boolean} anyMatch True to match any part not just the beginning
14216 filter : function(property, value, anyMatch){
14217 var fn = this.createFilterFn(property, value, anyMatch);
14218 return fn ? this.filterBy(fn) : this.clearFilter();
14222 * Filter by a function. The specified function will be called with each
14223 * record in this data source. If the function returns true the record is included,
14224 * otherwise it is filtered.
14225 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14226 * @param {Object} scope (optional) The scope of the function (defaults to this)
14228 filterBy : function(fn, scope){
14229 this.snapshot = this.snapshot || this.data;
14230 this.data = this.queryBy(fn, scope||this);
14231 this.fireEvent("datachanged", this);
14235 * Query the records by a specified property.
14236 * @param {String} field A field on your records
14237 * @param {String/RegExp} value Either a string that the field
14238 * should start with or a RegExp to test against the field
14239 * @param {Boolean} anyMatch True to match any part not just the beginning
14240 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14242 query : function(property, value, anyMatch){
14243 var fn = this.createFilterFn(property, value, anyMatch);
14244 return fn ? this.queryBy(fn) : this.data.clone();
14248 * Query by a function. The specified function will be called with each
14249 * record in this data source. If the function returns true the record is included
14251 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14252 * @param {Object} scope (optional) The scope of the function (defaults to this)
14253 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14255 queryBy : function(fn, scope){
14256 var data = this.snapshot || this.data;
14257 return data.filterBy(fn, scope||this);
14261 * Collects unique values for a particular dataIndex from this store.
14262 * @param {String} dataIndex The property to collect
14263 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
14264 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
14265 * @return {Array} An array of the unique values
14267 collect : function(dataIndex, allowNull, bypassFilter){
14268 var d = (bypassFilter === true && this.snapshot) ?
14269 this.snapshot.items : this.data.items;
14270 var v, sv, r = [], l = {};
14271 for(var i = 0, len = d.length; i < len; i++){
14272 v = d[i].data[dataIndex];
14274 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
14283 * Revert to a view of the Record cache with no filtering applied.
14284 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
14286 clearFilter : function(suppressEvent){
14287 if(this.snapshot && this.snapshot != this.data){
14288 this.data = this.snapshot;
14289 delete this.snapshot;
14290 if(suppressEvent !== true){
14291 this.fireEvent("datachanged", this);
14297 afterEdit : function(record){
14298 if(this.modified.indexOf(record) == -1){
14299 this.modified.push(record);
14301 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
14305 afterReject : function(record){
14306 this.modified.remove(record);
14307 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
14311 afterCommit : function(record){
14312 this.modified.remove(record);
14313 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
14317 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
14318 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
14320 commitChanges : function(){
14321 var m = this.modified.slice(0);
14322 this.modified = [];
14323 for(var i = 0, len = m.length; i < len; i++){
14329 * Cancel outstanding changes on all changed records.
14331 rejectChanges : function(){
14332 var m = this.modified.slice(0);
14333 this.modified = [];
14334 for(var i = 0, len = m.length; i < len; i++){
14339 onMetaChange : function(meta, rtype, o){
14340 this.recordType = rtype;
14341 this.fields = rtype.prototype.fields;
14342 delete this.snapshot;
14343 this.sortInfo = meta.sortInfo || this.sortInfo;
14344 this.modified = [];
14345 this.fireEvent('metachange', this, this.reader.meta);
14348 moveIndex : function(data, type)
14350 var index = this.indexOf(data);
14352 var newIndex = index + type;
14356 this.insert(newIndex, data);
14361 * Ext JS Library 1.1.1
14362 * Copyright(c) 2006-2007, Ext JS, LLC.
14364 * Originally Released Under LGPL - original licence link has changed is not relivant.
14367 * <script type="text/javascript">
14371 * @class Roo.data.SimpleStore
14372 * @extends Roo.data.Store
14373 * Small helper class to make creating Stores from Array data easier.
14374 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
14375 * @cfg {Array} fields An array of field definition objects, or field name strings.
14376 * @cfg {Object} an existing reader (eg. copied from another store)
14377 * @cfg {Array} data The multi-dimensional array of data
14379 * @param {Object} config
14381 Roo.data.SimpleStore = function(config)
14383 Roo.data.SimpleStore.superclass.constructor.call(this, {
14385 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
14388 Roo.data.Record.create(config.fields)
14390 proxy : new Roo.data.MemoryProxy(config.data)
14394 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
14396 * Ext JS Library 1.1.1
14397 * Copyright(c) 2006-2007, Ext JS, LLC.
14399 * Originally Released Under LGPL - original licence link has changed is not relivant.
14402 * <script type="text/javascript">
14407 * @extends Roo.data.Store
14408 * @class Roo.data.JsonStore
14409 * Small helper class to make creating Stores for JSON data easier. <br/>
14411 var store = new Roo.data.JsonStore({
14412 url: 'get-images.php',
14414 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14417 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14418 * JsonReader and HttpProxy (unless inline data is provided).</b>
14419 * @cfg {Array} fields An array of field definition objects, or field name strings.
14421 * @param {Object} config
14423 Roo.data.JsonStore = function(c){
14424 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14425 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14426 reader: new Roo.data.JsonReader(c, c.fields)
14429 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14431 * Ext JS Library 1.1.1
14432 * Copyright(c) 2006-2007, Ext JS, LLC.
14434 * Originally Released Under LGPL - original licence link has changed is not relivant.
14437 * <script type="text/javascript">
14441 Roo.data.Field = function(config){
14442 if(typeof config == "string"){
14443 config = {name: config};
14445 Roo.apply(this, config);
14448 this.type = "auto";
14451 var st = Roo.data.SortTypes;
14452 // named sortTypes are supported, here we look them up
14453 if(typeof this.sortType == "string"){
14454 this.sortType = st[this.sortType];
14457 // set default sortType for strings and dates
14458 if(!this.sortType){
14461 this.sortType = st.asUCString;
14464 this.sortType = st.asDate;
14467 this.sortType = st.none;
14472 var stripRe = /[\$,%]/g;
14474 // prebuilt conversion function for this field, instead of
14475 // switching every time we're reading a value
14477 var cv, dateFormat = this.dateFormat;
14482 cv = function(v){ return v; };
14485 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14489 return v !== undefined && v !== null && v !== '' ?
14490 parseInt(String(v).replace(stripRe, ""), 10) : '';
14495 return v !== undefined && v !== null && v !== '' ?
14496 parseFloat(String(v).replace(stripRe, ""), 10) : '';
14501 cv = function(v){ return v === true || v === "true" || v == 1; };
14508 if(v instanceof Date){
14512 if(dateFormat == "timestamp"){
14513 return new Date(v*1000);
14515 return Date.parseDate(v, dateFormat);
14517 var parsed = Date.parse(v);
14518 return parsed ? new Date(parsed) : null;
14527 Roo.data.Field.prototype = {
14535 * Ext JS Library 1.1.1
14536 * Copyright(c) 2006-2007, Ext JS, LLC.
14538 * Originally Released Under LGPL - original licence link has changed is not relivant.
14541 * <script type="text/javascript">
14544 // Base class for reading structured data from a data source. This class is intended to be
14545 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14548 * @class Roo.data.DataReader
14549 * Base class for reading structured data from a data source. This class is intended to be
14550 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14553 Roo.data.DataReader = function(meta, recordType){
14557 this.recordType = recordType instanceof Array ?
14558 Roo.data.Record.create(recordType) : recordType;
14561 Roo.data.DataReader.prototype = {
14564 readerType : 'Data',
14566 * Create an empty record
14567 * @param {Object} data (optional) - overlay some values
14568 * @return {Roo.data.Record} record created.
14570 newRow : function(d) {
14572 this.recordType.prototype.fields.each(function(c) {
14574 case 'int' : da[c.name] = 0; break;
14575 case 'date' : da[c.name] = new Date(); break;
14576 case 'float' : da[c.name] = 0.0; break;
14577 case 'boolean' : da[c.name] = false; break;
14578 default : da[c.name] = ""; break;
14582 return new this.recordType(Roo.apply(da, d));
14588 * Ext JS Library 1.1.1
14589 * Copyright(c) 2006-2007, Ext JS, LLC.
14591 * Originally Released Under LGPL - original licence link has changed is not relivant.
14594 * <script type="text/javascript">
14598 * @class Roo.data.DataProxy
14599 * @extends Roo.data.Observable
14600 * This class is an abstract base class for implementations which provide retrieval of
14601 * unformatted data objects.<br>
14603 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14604 * (of the appropriate type which knows how to parse the data object) to provide a block of
14605 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14607 * Custom implementations must implement the load method as described in
14608 * {@link Roo.data.HttpProxy#load}.
14610 Roo.data.DataProxy = function(){
14613 * @event beforeload
14614 * Fires before a network request is made to retrieve a data object.
14615 * @param {Object} This DataProxy object.
14616 * @param {Object} params The params parameter to the load function.
14621 * Fires before the load method's callback is called.
14622 * @param {Object} This DataProxy object.
14623 * @param {Object} o The data object.
14624 * @param {Object} arg The callback argument object passed to the load function.
14628 * @event loadexception
14629 * Fires if an Exception occurs during data retrieval.
14630 * @param {Object} This DataProxy object.
14631 * @param {Object} o The data object.
14632 * @param {Object} arg The callback argument object passed to the load function.
14633 * @param {Object} e The Exception.
14635 loadexception : true
14637 Roo.data.DataProxy.superclass.constructor.call(this);
14640 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14643 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14647 * Ext JS Library 1.1.1
14648 * Copyright(c) 2006-2007, Ext JS, LLC.
14650 * Originally Released Under LGPL - original licence link has changed is not relivant.
14653 * <script type="text/javascript">
14656 * @class Roo.data.MemoryProxy
14657 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14658 * to the Reader when its load method is called.
14660 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14662 Roo.data.MemoryProxy = function(data){
14666 Roo.data.MemoryProxy.superclass.constructor.call(this);
14670 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14673 * Load data from the requested source (in this case an in-memory
14674 * data object passed to the constructor), read the data object into
14675 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14676 * process that block using the passed callback.
14677 * @param {Object} params This parameter is not used by the MemoryProxy class.
14678 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14679 * object into a block of Roo.data.Records.
14680 * @param {Function} callback The function into which to pass the block of Roo.data.records.
14681 * The function must be passed <ul>
14682 * <li>The Record block object</li>
14683 * <li>The "arg" argument from the load function</li>
14684 * <li>A boolean success indicator</li>
14686 * @param {Object} scope The scope in which to call the callback
14687 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14689 load : function(params, reader, callback, scope, arg){
14690 params = params || {};
14693 result = reader.readRecords(params.data ? params.data :this.data);
14695 this.fireEvent("loadexception", this, arg, null, e);
14696 callback.call(scope, null, arg, false);
14699 callback.call(scope, result, arg, true);
14703 update : function(params, records){
14708 * Ext JS Library 1.1.1
14709 * Copyright(c) 2006-2007, Ext JS, LLC.
14711 * Originally Released Under LGPL - original licence link has changed is not relivant.
14714 * <script type="text/javascript">
14717 * @class Roo.data.HttpProxy
14718 * @extends Roo.data.DataProxy
14719 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14720 * configured to reference a certain URL.<br><br>
14722 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14723 * from which the running page was served.<br><br>
14725 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14727 * Be aware that to enable the browser to parse an XML document, the server must set
14728 * the Content-Type header in the HTTP response to "text/xml".
14730 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14731 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
14732 * will be used to make the request.
14734 Roo.data.HttpProxy = function(conn){
14735 Roo.data.HttpProxy.superclass.constructor.call(this);
14736 // is conn a conn config or a real conn?
14738 this.useAjax = !conn || !conn.events;
14742 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14743 // thse are take from connection...
14746 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14749 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14750 * extra parameters to each request made by this object. (defaults to undefined)
14753 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14754 * to each request made by this object. (defaults to undefined)
14757 * @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)
14760 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14763 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14769 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14773 * Return the {@link Roo.data.Connection} object being used by this Proxy.
14774 * @return {Connection} The Connection object. This object may be used to subscribe to events on
14775 * a finer-grained basis than the DataProxy events.
14777 getConnection : function(){
14778 return this.useAjax ? Roo.Ajax : this.conn;
14782 * Load data from the configured {@link Roo.data.Connection}, read the data object into
14783 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14784 * process that block using the passed callback.
14785 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14786 * for the request to the remote server.
14787 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14788 * object into a block of Roo.data.Records.
14789 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14790 * The function must be passed <ul>
14791 * <li>The Record block object</li>
14792 * <li>The "arg" argument from the load function</li>
14793 * <li>A boolean success indicator</li>
14795 * @param {Object} scope The scope in which to call the callback
14796 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14798 load : function(params, reader, callback, scope, arg){
14799 if(this.fireEvent("beforeload", this, params) !== false){
14801 params : params || {},
14803 callback : callback,
14808 callback : this.loadResponse,
14812 Roo.applyIf(o, this.conn);
14813 if(this.activeRequest){
14814 Roo.Ajax.abort(this.activeRequest);
14816 this.activeRequest = Roo.Ajax.request(o);
14818 this.conn.request(o);
14821 callback.call(scope||this, null, arg, false);
14826 loadResponse : function(o, success, response){
14827 delete this.activeRequest;
14829 this.fireEvent("loadexception", this, o, response);
14830 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14835 result = o.reader.read(response);
14837 this.fireEvent("loadexception", this, o, response, e);
14838 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14842 this.fireEvent("load", this, o, o.request.arg);
14843 o.request.callback.call(o.request.scope, result, o.request.arg, true);
14847 update : function(dataSet){
14852 updateResponse : function(dataSet){
14857 * Ext JS Library 1.1.1
14858 * Copyright(c) 2006-2007, Ext JS, LLC.
14860 * Originally Released Under LGPL - original licence link has changed is not relivant.
14863 * <script type="text/javascript">
14867 * @class Roo.data.ScriptTagProxy
14868 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14869 * other than the originating domain of the running page.<br><br>
14871 * <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
14872 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14874 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14875 * source code that is used as the source inside a <script> tag.<br><br>
14877 * In order for the browser to process the returned data, the server must wrap the data object
14878 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14879 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14880 * depending on whether the callback name was passed:
14883 boolean scriptTag = false;
14884 String cb = request.getParameter("callback");
14887 response.setContentType("text/javascript");
14889 response.setContentType("application/x-json");
14891 Writer out = response.getWriter();
14893 out.write(cb + "(");
14895 out.print(dataBlock.toJsonString());
14902 * @param {Object} config A configuration object.
14904 Roo.data.ScriptTagProxy = function(config){
14905 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14906 Roo.apply(this, config);
14907 this.head = document.getElementsByTagName("head")[0];
14910 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14912 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14914 * @cfg {String} url The URL from which to request the data object.
14917 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14921 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14922 * the server the name of the callback function set up by the load call to process the returned data object.
14923 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14924 * javascript output which calls this named function passing the data object as its only parameter.
14926 callbackParam : "callback",
14928 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14929 * name to the request.
14934 * Load data from the configured URL, read the data object into
14935 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14936 * process that block using the passed callback.
14937 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14938 * for the request to the remote server.
14939 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14940 * object into a block of Roo.data.Records.
14941 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14942 * The function must be passed <ul>
14943 * <li>The Record block object</li>
14944 * <li>The "arg" argument from the load function</li>
14945 * <li>A boolean success indicator</li>
14947 * @param {Object} scope The scope in which to call the callback
14948 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14950 load : function(params, reader, callback, scope, arg){
14951 if(this.fireEvent("beforeload", this, params) !== false){
14953 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14955 var url = this.url;
14956 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14958 url += "&_dc=" + (new Date().getTime());
14960 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14963 cb : "stcCallback"+transId,
14964 scriptId : "stcScript"+transId,
14968 callback : callback,
14974 window[trans.cb] = function(o){
14975 conn.handleResponse(o, trans);
14978 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14980 if(this.autoAbort !== false){
14984 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14986 var script = document.createElement("script");
14987 script.setAttribute("src", url);
14988 script.setAttribute("type", "text/javascript");
14989 script.setAttribute("id", trans.scriptId);
14990 this.head.appendChild(script);
14992 this.trans = trans;
14994 callback.call(scope||this, null, arg, false);
14999 isLoading : function(){
15000 return this.trans ? true : false;
15004 * Abort the current server request.
15006 abort : function(){
15007 if(this.isLoading()){
15008 this.destroyTrans(this.trans);
15013 destroyTrans : function(trans, isLoaded){
15014 this.head.removeChild(document.getElementById(trans.scriptId));
15015 clearTimeout(trans.timeoutId);
15017 window[trans.cb] = undefined;
15019 delete window[trans.cb];
15022 // if hasn't been loaded, wait for load to remove it to prevent script error
15023 window[trans.cb] = function(){
15024 window[trans.cb] = undefined;
15026 delete window[trans.cb];
15033 handleResponse : function(o, trans){
15034 this.trans = false;
15035 this.destroyTrans(trans, true);
15038 result = trans.reader.readRecords(o);
15040 this.fireEvent("loadexception", this, o, trans.arg, e);
15041 trans.callback.call(trans.scope||window, null, trans.arg, false);
15044 this.fireEvent("load", this, o, trans.arg);
15045 trans.callback.call(trans.scope||window, result, trans.arg, true);
15049 handleFailure : function(trans){
15050 this.trans = false;
15051 this.destroyTrans(trans, false);
15052 this.fireEvent("loadexception", this, null, trans.arg);
15053 trans.callback.call(trans.scope||window, null, trans.arg, false);
15057 * Ext JS Library 1.1.1
15058 * Copyright(c) 2006-2007, Ext JS, LLC.
15060 * Originally Released Under LGPL - original licence link has changed is not relivant.
15063 * <script type="text/javascript">
15067 * @class Roo.data.JsonReader
15068 * @extends Roo.data.DataReader
15069 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
15070 * based on mappings in a provided Roo.data.Record constructor.
15072 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
15073 * in the reply previously.
15078 var RecordDef = Roo.data.Record.create([
15079 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
15080 {name: 'occupation'} // This field will use "occupation" as the mapping.
15082 var myReader = new Roo.data.JsonReader({
15083 totalProperty: "results", // The property which contains the total dataset size (optional)
15084 root: "rows", // The property which contains an Array of row objects
15085 id: "id" // The property within each row object that provides an ID for the record (optional)
15089 * This would consume a JSON file like this:
15091 { 'results': 2, 'rows': [
15092 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
15093 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
15096 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
15097 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
15098 * paged from the remote server.
15099 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
15100 * @cfg {String} root name of the property which contains the Array of row objects.
15101 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15102 * @cfg {Array} fields Array of field definition objects
15104 * Create a new JsonReader
15105 * @param {Object} meta Metadata configuration options
15106 * @param {Object} recordType Either an Array of field definition objects,
15107 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
15109 Roo.data.JsonReader = function(meta, recordType){
15112 // set some defaults:
15113 Roo.applyIf(meta, {
15114 totalProperty: 'total',
15115 successProperty : 'success',
15120 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15122 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
15124 readerType : 'Json',
15127 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
15128 * Used by Store query builder to append _requestMeta to params.
15131 metaFromRemote : false,
15133 * This method is only used by a DataProxy which has retrieved data from a remote server.
15134 * @param {Object} response The XHR object which contains the JSON data in its responseText.
15135 * @return {Object} data A data block which is used by an Roo.data.Store object as
15136 * a cache of Roo.data.Records.
15138 read : function(response){
15139 var json = response.responseText;
15141 var o = /* eval:var:o */ eval("("+json+")");
15143 throw {message: "JsonReader.read: Json object not found"};
15149 this.metaFromRemote = true;
15150 this.meta = o.metaData;
15151 this.recordType = Roo.data.Record.create(o.metaData.fields);
15152 this.onMetaChange(this.meta, this.recordType, o);
15154 return this.readRecords(o);
15157 // private function a store will implement
15158 onMetaChange : function(meta, recordType, o){
15165 simpleAccess: function(obj, subsc) {
15172 getJsonAccessor: function(){
15174 return function(expr) {
15176 return(re.test(expr))
15177 ? new Function("obj", "return obj." + expr)
15182 return Roo.emptyFn;
15187 * Create a data block containing Roo.data.Records from an XML document.
15188 * @param {Object} o An object which contains an Array of row objects in the property specified
15189 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
15190 * which contains the total size of the dataset.
15191 * @return {Object} data A data block which is used by an Roo.data.Store object as
15192 * a cache of Roo.data.Records.
15194 readRecords : function(o){
15196 * After any data loads, the raw JSON data is available for further custom processing.
15200 var s = this.meta, Record = this.recordType,
15201 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
15203 // Generate extraction functions for the totalProperty, the root, the id, and for each field
15205 if(s.totalProperty) {
15206 this.getTotal = this.getJsonAccessor(s.totalProperty);
15208 if(s.successProperty) {
15209 this.getSuccess = this.getJsonAccessor(s.successProperty);
15211 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
15213 var g = this.getJsonAccessor(s.id);
15214 this.getId = function(rec) {
15216 return (r === undefined || r === "") ? null : r;
15219 this.getId = function(){return null;};
15222 for(var jj = 0; jj < fl; jj++){
15224 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
15225 this.ef[jj] = this.getJsonAccessor(map);
15229 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
15230 if(s.totalProperty){
15231 var vt = parseInt(this.getTotal(o), 10);
15236 if(s.successProperty){
15237 var vs = this.getSuccess(o);
15238 if(vs === false || vs === 'false'){
15243 for(var i = 0; i < c; i++){
15246 var id = this.getId(n);
15247 for(var j = 0; j < fl; j++){
15249 var v = this.ef[j](n);
15251 Roo.log('missing convert for ' + f.name);
15255 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
15257 var record = new Record(values, id);
15259 records[i] = record;
15265 totalRecords : totalRecords
15268 // used when loading children.. @see loadDataFromChildren
15269 toLoadData: function(rec)
15271 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15272 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15273 return { data : data, total : data.length };
15278 * Ext JS Library 1.1.1
15279 * Copyright(c) 2006-2007, Ext JS, LLC.
15281 * Originally Released Under LGPL - original licence link has changed is not relivant.
15284 * <script type="text/javascript">
15288 * @class Roo.data.ArrayReader
15289 * @extends Roo.data.DataReader
15290 * Data reader class to create an Array of Roo.data.Record objects from an Array.
15291 * Each element of that Array represents a row of data fields. The
15292 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
15293 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
15297 var RecordDef = Roo.data.Record.create([
15298 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
15299 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
15301 var myReader = new Roo.data.ArrayReader({
15302 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
15306 * This would consume an Array like this:
15308 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
15312 * Create a new JsonReader
15313 * @param {Object} meta Metadata configuration options.
15314 * @param {Object|Array} recordType Either an Array of field definition objects
15316 * @cfg {Array} fields Array of field definition objects
15317 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15318 * as specified to {@link Roo.data.Record#create},
15319 * or an {@link Roo.data.Record} object
15322 * created using {@link Roo.data.Record#create}.
15324 Roo.data.ArrayReader = function(meta, recordType)
15326 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15329 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
15332 * Create a data block containing Roo.data.Records from an XML document.
15333 * @param {Object} o An Array of row objects which represents the dataset.
15334 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
15335 * a cache of Roo.data.Records.
15337 readRecords : function(o)
15339 var sid = this.meta ? this.meta.id : null;
15340 var recordType = this.recordType, fields = recordType.prototype.fields;
15343 for(var i = 0; i < root.length; i++){
15346 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
15347 for(var j = 0, jlen = fields.length; j < jlen; j++){
15348 var f = fields.items[j];
15349 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
15350 var v = n[k] !== undefined ? n[k] : f.defaultValue;
15352 values[f.name] = v;
15354 var record = new recordType(values, id);
15356 records[records.length] = record;
15360 totalRecords : records.length
15363 // used when loading children.. @see loadDataFromChildren
15364 toLoadData: function(rec)
15366 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15367 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15378 * @class Roo.bootstrap.ComboBox
15379 * @extends Roo.bootstrap.TriggerField
15380 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
15381 * @cfg {Boolean} append (true|false) default false
15382 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
15383 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
15384 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
15385 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
15386 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
15387 * @cfg {Boolean} animate default true
15388 * @cfg {Boolean} emptyResultText only for touch device
15389 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15390 * @cfg {String} emptyTitle default ''
15391 * @cfg {Number} width fixed with? experimental
15393 * Create a new ComboBox.
15394 * @param {Object} config Configuration options
15396 Roo.bootstrap.ComboBox = function(config){
15397 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15401 * Fires when the dropdown list is expanded
15402 * @param {Roo.bootstrap.ComboBox} combo This combo box
15407 * Fires when the dropdown list is collapsed
15408 * @param {Roo.bootstrap.ComboBox} combo This combo box
15412 * @event beforeselect
15413 * Fires before a list item is selected. Return false to cancel the selection.
15414 * @param {Roo.bootstrap.ComboBox} combo This combo box
15415 * @param {Roo.data.Record} record The data record returned from the underlying store
15416 * @param {Number} index The index of the selected item in the dropdown list
15418 'beforeselect' : true,
15421 * Fires when a list item is selected
15422 * @param {Roo.bootstrap.ComboBox} combo This combo box
15423 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15424 * @param {Number} index The index of the selected item in the dropdown list
15428 * @event beforequery
15429 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15430 * The event object passed has these properties:
15431 * @param {Roo.bootstrap.ComboBox} combo This combo box
15432 * @param {String} query The query
15433 * @param {Boolean} forceAll true to force "all" query
15434 * @param {Boolean} cancel true to cancel the query
15435 * @param {Object} e The query event object
15437 'beforequery': true,
15440 * Fires when the 'add' icon is pressed (add a listener to enable add button)
15441 * @param {Roo.bootstrap.ComboBox} combo This combo box
15446 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15447 * @param {Roo.bootstrap.ComboBox} combo This combo box
15448 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15453 * Fires when the remove value from the combobox array
15454 * @param {Roo.bootstrap.ComboBox} combo This combo box
15458 * @event afterremove
15459 * Fires when the remove value from the combobox array
15460 * @param {Roo.bootstrap.ComboBox} combo This combo box
15462 'afterremove' : true,
15464 * @event specialfilter
15465 * Fires when specialfilter
15466 * @param {Roo.bootstrap.ComboBox} combo This combo box
15468 'specialfilter' : true,
15471 * Fires when tick the element
15472 * @param {Roo.bootstrap.ComboBox} combo This combo box
15476 * @event touchviewdisplay
15477 * Fires when touch view require special display (default is using displayField)
15478 * @param {Roo.bootstrap.ComboBox} combo This combo box
15479 * @param {Object} cfg set html .
15481 'touchviewdisplay' : true
15486 this.tickItems = [];
15488 this.selectedIndex = -1;
15489 if(this.mode == 'local'){
15490 if(config.queryDelay === undefined){
15491 this.queryDelay = 10;
15493 if(config.minChars === undefined){
15499 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15502 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15503 * rendering into an Roo.Editor, defaults to false)
15506 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15507 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15510 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15513 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15514 * the dropdown list (defaults to undefined, with no header element)
15518 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
15522 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15524 listWidth: undefined,
15526 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15527 * mode = 'remote' or 'text' if mode = 'local')
15529 displayField: undefined,
15532 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15533 * mode = 'remote' or 'value' if mode = 'local').
15534 * Note: use of a valueField requires the user make a selection
15535 * in order for a value to be mapped.
15537 valueField: undefined,
15539 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15544 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15545 * field's data value (defaults to the underlying DOM element's name)
15547 hiddenName: undefined,
15549 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15553 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15555 selectedClass: 'active',
15558 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15562 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15563 * anchor positions (defaults to 'tl-bl')
15565 listAlign: 'tl-bl?',
15567 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15571 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
15572 * query specified by the allQuery config option (defaults to 'query')
15574 triggerAction: 'query',
15576 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15577 * (defaults to 4, does not apply if editable = false)
15581 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15582 * delay (typeAheadDelay) if it matches a known value (defaults to false)
15586 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15587 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15591 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15592 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
15596 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
15597 * when editable = true (defaults to false)
15599 selectOnFocus:false,
15601 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15603 queryParam: 'query',
15605 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
15606 * when mode = 'remote' (defaults to 'Loading...')
15608 loadingText: 'Loading...',
15610 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15614 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15618 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15619 * traditional select (defaults to true)
15623 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15627 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15631 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15632 * listWidth has a higher value)
15636 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15637 * allow the user to set arbitrary text into the field (defaults to false)
15639 forceSelection:false,
15641 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15642 * if typeAhead = true (defaults to 250)
15644 typeAheadDelay : 250,
15646 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15647 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15649 valueNotFoundText : undefined,
15651 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15653 blockFocus : false,
15656 * @cfg {Boolean} disableClear Disable showing of clear button.
15658 disableClear : false,
15660 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
15662 alwaysQuery : false,
15665 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
15670 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15672 invalidClass : "has-warning",
15675 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15677 validClass : "has-success",
15680 * @cfg {Boolean} specialFilter (true|false) special filter default false
15682 specialFilter : false,
15685 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15687 mobileTouchView : true,
15690 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15692 useNativeIOS : false,
15695 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15697 mobile_restrict_height : false,
15699 ios_options : false,
15711 btnPosition : 'right',
15712 triggerList : true,
15713 showToggleBtn : true,
15715 emptyResultText: 'Empty',
15716 triggerText : 'Select',
15720 // element that contains real text value.. (when hidden is used..)
15722 getAutoCreate : function()
15727 * Render classic select for iso
15730 if(Roo.isIOS && this.useNativeIOS){
15731 cfg = this.getAutoCreateNativeIOS();
15739 if(Roo.isTouch && this.mobileTouchView){
15740 cfg = this.getAutoCreateTouchView();
15747 if(!this.tickable){
15748 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15753 * ComboBox with tickable selections
15756 var align = this.labelAlign || this.parentLabelAlign();
15759 cls : 'form-group roo-combobox-tickable' //input-group
15762 var btn_text_select = '';
15763 var btn_text_done = '';
15764 var btn_text_cancel = '';
15766 if (this.btn_text_show) {
15767 btn_text_select = 'Select';
15768 btn_text_done = 'Done';
15769 btn_text_cancel = 'Cancel';
15774 cls : 'tickable-buttons',
15779 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15780 //html : this.triggerText
15781 html: btn_text_select
15787 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15789 html: btn_text_done
15795 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15797 html: btn_text_cancel
15803 buttons.cn.unshift({
15805 cls: 'roo-select2-search-field-input'
15811 Roo.each(buttons.cn, function(c){
15813 c.cls += ' btn-' + _this.size;
15816 if (_this.disabled) {
15823 style : 'display: contents',
15828 cls: 'form-hidden-field'
15832 cls: 'roo-select2-choices',
15836 cls: 'roo-select2-search-field',
15847 cls: 'roo-select2-container input-group roo-select2-container-multi',
15853 // cls: 'typeahead typeahead-long dropdown-menu',
15854 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
15859 if(this.hasFeedback && !this.allowBlank){
15863 cls: 'glyphicon form-control-feedback'
15866 combobox.cn.push(feedback);
15873 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15874 tooltip : 'This field is required'
15876 if (Roo.bootstrap.version == 4) {
15879 style : 'display:none'
15882 if (align ==='left' && this.fieldLabel.length) {
15884 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
15891 cls : 'control-label col-form-label',
15892 html : this.fieldLabel
15904 var labelCfg = cfg.cn[1];
15905 var contentCfg = cfg.cn[2];
15908 if(this.indicatorpos == 'right'){
15914 cls : 'control-label col-form-label',
15918 html : this.fieldLabel
15934 labelCfg = cfg.cn[0];
15935 contentCfg = cfg.cn[1];
15939 if(this.labelWidth > 12){
15940 labelCfg.style = "width: " + this.labelWidth + 'px';
15942 if(this.width * 1 > 0){
15943 contentCfg.style = "width: " + this.width + 'px';
15945 if(this.labelWidth < 13 && this.labelmd == 0){
15946 this.labelmd = this.labelWidth;
15949 if(this.labellg > 0){
15950 labelCfg.cls += ' col-lg-' + this.labellg;
15951 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15954 if(this.labelmd > 0){
15955 labelCfg.cls += ' col-md-' + this.labelmd;
15956 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15959 if(this.labelsm > 0){
15960 labelCfg.cls += ' col-sm-' + this.labelsm;
15961 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15964 if(this.labelxs > 0){
15965 labelCfg.cls += ' col-xs-' + this.labelxs;
15966 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15970 } else if ( this.fieldLabel.length) {
15971 // Roo.log(" label");
15976 //cls : 'input-group-addon',
15977 html : this.fieldLabel
15982 if(this.indicatorpos == 'right'){
15986 //cls : 'input-group-addon',
15987 html : this.fieldLabel
15997 // Roo.log(" no label && no align");
16004 ['xs','sm','md','lg'].map(function(size){
16005 if (settings[size]) {
16006 cfg.cls += ' col-' + size + '-' + settings[size];
16014 _initEventsCalled : false,
16017 initEvents: function()
16019 if (this._initEventsCalled) { // as we call render... prevent looping...
16022 this._initEventsCalled = true;
16025 throw "can not find store for combo";
16028 this.indicator = this.indicatorEl();
16030 this.store = Roo.factory(this.store, Roo.data);
16031 this.store.parent = this;
16033 // if we are building from html. then this element is so complex, that we can not really
16034 // use the rendered HTML.
16035 // so we have to trash and replace the previous code.
16036 if (Roo.XComponent.build_from_html) {
16037 // remove this element....
16038 var e = this.el.dom, k=0;
16039 while (e ) { e = e.previousSibling; ++k;}
16044 this.rendered = false;
16046 this.render(this.parent().getChildContainer(true), k);
16049 if(Roo.isIOS && this.useNativeIOS){
16050 this.initIOSView();
16058 if(Roo.isTouch && this.mobileTouchView){
16059 this.initTouchView();
16064 this.initTickableEvents();
16068 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
16070 if(this.hiddenName){
16072 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16074 this.hiddenField.dom.value =
16075 this.hiddenValue !== undefined ? this.hiddenValue :
16076 this.value !== undefined ? this.value : '';
16078 // prevent input submission
16079 this.el.dom.removeAttribute('name');
16080 this.hiddenField.dom.setAttribute('name', this.hiddenName);
16085 // this.el.dom.setAttribute('autocomplete', 'off');
16088 var cls = 'x-combo-list';
16090 //this.list = new Roo.Layer({
16091 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
16097 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16098 _this.list.setWidth(lw);
16101 this.list.on('mouseover', this.onViewOver, this);
16102 this.list.on('mousemove', this.onViewMove, this);
16103 this.list.on('scroll', this.onViewScroll, this);
16106 this.list.swallowEvent('mousewheel');
16107 this.assetHeight = 0;
16110 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
16111 this.assetHeight += this.header.getHeight();
16114 this.innerList = this.list.createChild({cls:cls+'-inner'});
16115 this.innerList.on('mouseover', this.onViewOver, this);
16116 this.innerList.on('mousemove', this.onViewMove, this);
16117 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16119 if(this.allowBlank && !this.pageSize && !this.disableClear){
16120 this.footer = this.list.createChild({cls:cls+'-ft'});
16121 this.pageTb = new Roo.Toolbar(this.footer);
16125 this.footer = this.list.createChild({cls:cls+'-ft'});
16126 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
16127 {pageSize: this.pageSize});
16131 if (this.pageTb && this.allowBlank && !this.disableClear) {
16133 this.pageTb.add(new Roo.Toolbar.Fill(), {
16134 cls: 'x-btn-icon x-btn-clear',
16136 handler: function()
16139 _this.clearValue();
16140 _this.onSelect(false, -1);
16145 this.assetHeight += this.footer.getHeight();
16150 this.tpl = Roo.bootstrap.version == 4 ?
16151 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
16152 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
16155 this.view = new Roo.View(this.list, this.tpl, {
16156 singleSelect:true, store: this.store, selectedClass: this.selectedClass
16158 //this.view.wrapEl.setDisplayed(false);
16159 this.view.on('click', this.onViewClick, this);
16162 this.store.on('beforeload', this.onBeforeLoad, this);
16163 this.store.on('load', this.onLoad, this);
16164 this.store.on('loadexception', this.onLoadException, this);
16166 if(this.resizable){
16167 this.resizer = new Roo.Resizable(this.list, {
16168 pinned:true, handles:'se'
16170 this.resizer.on('resize', function(r, w, h){
16171 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
16172 this.listWidth = w;
16173 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
16174 this.restrictHeight();
16176 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
16179 if(!this.editable){
16180 this.editable = true;
16181 this.setEditable(false);
16186 if (typeof(this.events.add.listeners) != 'undefined') {
16188 this.addicon = this.wrap.createChild(
16189 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
16191 this.addicon.on('click', function(e) {
16192 this.fireEvent('add', this);
16195 if (typeof(this.events.edit.listeners) != 'undefined') {
16197 this.editicon = this.wrap.createChild(
16198 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
16199 if (this.addicon) {
16200 this.editicon.setStyle('margin-left', '40px');
16202 this.editicon.on('click', function(e) {
16204 // we fire even if inothing is selected..
16205 this.fireEvent('edit', this, this.lastData );
16211 this.keyNav = new Roo.KeyNav(this.inputEl(), {
16212 "up" : function(e){
16213 this.inKeyMode = true;
16217 "down" : function(e){
16218 if(!this.isExpanded()){
16219 this.onTriggerClick();
16221 this.inKeyMode = true;
16226 "enter" : function(e){
16227 // this.onViewClick();
16231 if(this.fireEvent("specialkey", this, e)){
16232 this.onViewClick(false);
16238 "esc" : function(e){
16242 "tab" : function(e){
16245 if(this.fireEvent("specialkey", this, e)){
16246 this.onViewClick(false);
16254 doRelay : function(foo, bar, hname){
16255 if(hname == 'down' || this.scope.isExpanded()){
16256 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16265 this.queryDelay = Math.max(this.queryDelay || 10,
16266 this.mode == 'local' ? 10 : 250);
16269 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16271 if(this.typeAhead){
16272 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16274 if(this.editable !== false){
16275 this.inputEl().on("keyup", this.onKeyUp, this);
16277 if(this.forceSelection){
16278 this.inputEl().on('blur', this.doForce, this);
16282 this.choices = this.el.select('ul.roo-select2-choices', true).first();
16283 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16287 initTickableEvents: function()
16291 if(this.hiddenName){
16293 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16295 this.hiddenField.dom.value =
16296 this.hiddenValue !== undefined ? this.hiddenValue :
16297 this.value !== undefined ? this.value : '';
16299 // prevent input submission
16300 this.el.dom.removeAttribute('name');
16301 this.hiddenField.dom.setAttribute('name', this.hiddenName);
16306 // this.list = this.el.select('ul.dropdown-menu',true).first();
16308 this.choices = this.el.select('ul.roo-select2-choices', true).first();
16309 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16310 if(this.triggerList){
16311 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
16314 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
16315 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
16317 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
16318 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
16320 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
16321 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
16323 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
16324 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
16325 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
16328 this.cancelBtn.hide();
16333 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16334 _this.list.setWidth(lw);
16337 this.list.on('mouseover', this.onViewOver, this);
16338 this.list.on('mousemove', this.onViewMove, this);
16340 this.list.on('scroll', this.onViewScroll, this);
16343 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
16344 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
16347 this.view = new Roo.View(this.list, this.tpl, {
16352 selectedClass: this.selectedClass
16355 //this.view.wrapEl.setDisplayed(false);
16356 this.view.on('click', this.onViewClick, this);
16360 this.store.on('beforeload', this.onBeforeLoad, this);
16361 this.store.on('load', this.onLoad, this);
16362 this.store.on('loadexception', this.onLoadException, this);
16365 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
16366 "up" : function(e){
16367 this.inKeyMode = true;
16371 "down" : function(e){
16372 this.inKeyMode = true;
16376 "enter" : function(e){
16377 if(this.fireEvent("specialkey", this, e)){
16378 this.onViewClick(false);
16384 "esc" : function(e){
16385 this.onTickableFooterButtonClick(e, false, false);
16388 "tab" : function(e){
16389 this.fireEvent("specialkey", this, e);
16391 this.onTickableFooterButtonClick(e, false, false);
16398 doRelay : function(e, fn, key){
16399 if(this.scope.isExpanded()){
16400 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16409 this.queryDelay = Math.max(this.queryDelay || 10,
16410 this.mode == 'local' ? 10 : 250);
16413 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16415 if(this.typeAhead){
16416 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16419 if(this.editable !== false){
16420 this.tickableInputEl().on("keyup", this.onKeyUp, this);
16423 this.indicator = this.indicatorEl();
16425 if(this.indicator){
16426 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16427 this.indicator.hide();
16432 onDestroy : function(){
16434 this.view.setStore(null);
16435 this.view.el.removeAllListeners();
16436 this.view.el.remove();
16437 this.view.purgeListeners();
16440 this.list.dom.innerHTML = '';
16444 this.store.un('beforeload', this.onBeforeLoad, this);
16445 this.store.un('load', this.onLoad, this);
16446 this.store.un('loadexception', this.onLoadException, this);
16448 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16452 fireKey : function(e){
16453 if(e.isNavKeyPress() && !this.list.isVisible()){
16454 this.fireEvent("specialkey", this, e);
16459 onResize: function(w, h)
16463 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16465 // if(typeof w != 'number'){
16466 // // we do not handle it!?!?
16469 // var tw = this.trigger.getWidth();
16470 // // tw += this.addicon ? this.addicon.getWidth() : 0;
16471 // // tw += this.editicon ? this.editicon.getWidth() : 0;
16473 // this.inputEl().setWidth( this.adjustWidth('input', x));
16475 // //this.trigger.setStyle('left', x+'px');
16477 // if(this.list && this.listWidth === undefined){
16478 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16479 // this.list.setWidth(lw);
16480 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16488 * Allow or prevent the user from directly editing the field text. If false is passed,
16489 * the user will only be able to select from the items defined in the dropdown list. This method
16490 * is the runtime equivalent of setting the 'editable' config option at config time.
16491 * @param {Boolean} value True to allow the user to directly edit the field text
16493 setEditable : function(value){
16494 if(value == this.editable){
16497 this.editable = value;
16499 this.inputEl().dom.setAttribute('readOnly', true);
16500 this.inputEl().on('mousedown', this.onTriggerClick, this);
16501 this.inputEl().addClass('x-combo-noedit');
16503 this.inputEl().dom.removeAttribute('readOnly');
16504 this.inputEl().un('mousedown', this.onTriggerClick, this);
16505 this.inputEl().removeClass('x-combo-noedit');
16511 onBeforeLoad : function(combo,opts){
16512 if(!this.hasFocus){
16516 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16518 this.restrictHeight();
16519 this.selectedIndex = -1;
16523 onLoad : function(){
16525 this.hasQuery = false;
16527 if(!this.hasFocus){
16531 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16532 this.loading.hide();
16535 if(this.store.getCount() > 0){
16538 this.restrictHeight();
16539 if(this.lastQuery == this.allQuery){
16540 if(this.editable && !this.tickable){
16541 this.inputEl().dom.select();
16545 !this.selectByValue(this.value, true) &&
16548 !this.store.lastOptions ||
16549 typeof(this.store.lastOptions.add) == 'undefined' ||
16550 this.store.lastOptions.add != true
16553 this.select(0, true);
16556 if(this.autoFocus){
16559 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16560 this.taTask.delay(this.typeAheadDelay);
16564 this.onEmptyResults();
16570 onLoadException : function()
16572 this.hasQuery = false;
16574 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16575 this.loading.hide();
16578 if(this.tickable && this.editable){
16583 // only causes errors at present
16584 //Roo.log(this.store.reader.jsonData);
16585 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16587 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16593 onTypeAhead : function(){
16594 if(this.store.getCount() > 0){
16595 var r = this.store.getAt(0);
16596 var newValue = r.data[this.displayField];
16597 var len = newValue.length;
16598 var selStart = this.getRawValue().length;
16600 if(selStart != len){
16601 this.setRawValue(newValue);
16602 this.selectText(selStart, newValue.length);
16608 onSelect : function(record, index){
16610 if(this.fireEvent('beforeselect', this, record, index) !== false){
16612 this.setFromData(index > -1 ? record.data : false);
16615 this.fireEvent('select', this, record, index);
16620 * Returns the currently selected field value or empty string if no value is set.
16621 * @return {String} value The selected value
16623 getValue : function()
16625 if(Roo.isIOS && this.useNativeIOS){
16626 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16630 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16633 if(this.valueField){
16634 return typeof this.value != 'undefined' ? this.value : '';
16636 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16640 getRawValue : function()
16642 if(Roo.isIOS && this.useNativeIOS){
16643 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16646 var v = this.inputEl().getValue();
16652 * Clears any text/value currently set in the field
16654 clearValue : function(){
16656 if(this.hiddenField){
16657 this.hiddenField.dom.value = '';
16660 this.setRawValue('');
16661 this.lastSelectionText = '';
16662 this.lastData = false;
16664 var close = this.closeTriggerEl();
16675 * Sets the specified value into the field. If the value finds a match, the corresponding record text
16676 * will be displayed in the field. If the value does not match the data value of an existing item,
16677 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16678 * Otherwise the field will be blank (although the value will still be set).
16679 * @param {String} value The value to match
16681 setValue : function(v)
16683 if(Roo.isIOS && this.useNativeIOS){
16684 this.setIOSValue(v);
16694 if(this.valueField){
16695 var r = this.findRecord(this.valueField, v);
16697 text = r.data[this.displayField];
16698 }else if(this.valueNotFoundText !== undefined){
16699 text = this.valueNotFoundText;
16702 this.lastSelectionText = text;
16703 if(this.hiddenField){
16704 this.hiddenField.dom.value = v;
16706 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16709 var close = this.closeTriggerEl();
16712 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16718 * @property {Object} the last set data for the element
16723 * Sets the value of the field based on a object which is related to the record format for the store.
16724 * @param {Object} value the value to set as. or false on reset?
16726 setFromData : function(o){
16733 var dv = ''; // display value
16734 var vv = ''; // value value..
16736 if (this.displayField) {
16737 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16739 // this is an error condition!!!
16740 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16743 if(this.valueField){
16744 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16747 var close = this.closeTriggerEl();
16750 if(dv.length || vv * 1 > 0){
16752 this.blockFocus=true;
16758 if(this.hiddenField){
16759 this.hiddenField.dom.value = vv;
16761 this.lastSelectionText = dv;
16762 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16766 // no hidden field.. - we store the value in 'value', but still display
16767 // display field!!!!
16768 this.lastSelectionText = dv;
16769 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16776 reset : function(){
16777 // overridden so that last data is reset..
16784 this.setValue(this.originalValue);
16785 //this.clearInvalid();
16786 this.lastData = false;
16788 this.view.clearSelections();
16794 findRecord : function(prop, value){
16796 if(this.store.getCount() > 0){
16797 this.store.each(function(r){
16798 if(r.data[prop] == value){
16808 getName: function()
16810 // returns hidden if it's set..
16811 if (!this.rendered) {return ''};
16812 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
16816 onViewMove : function(e, t){
16817 this.inKeyMode = false;
16821 onViewOver : function(e, t){
16822 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16825 var item = this.view.findItemFromChild(t);
16828 var index = this.view.indexOf(item);
16829 this.select(index, false);
16834 onViewClick : function(view, doFocus, el, e)
16836 var index = this.view.getSelectedIndexes()[0];
16838 var r = this.store.getAt(index);
16842 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16849 Roo.each(this.tickItems, function(v,k){
16851 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16853 _this.tickItems.splice(k, 1);
16855 if(typeof(e) == 'undefined' && view == false){
16856 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16868 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16869 this.tickItems.push(r.data);
16872 if(typeof(e) == 'undefined' && view == false){
16873 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16880 this.onSelect(r, index);
16882 if(doFocus !== false && !this.blockFocus){
16883 this.inputEl().focus();
16888 restrictHeight : function(){
16889 //this.innerList.dom.style.height = '';
16890 //var inner = this.innerList.dom;
16891 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16892 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16893 //this.list.beginUpdate();
16894 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16895 this.list.alignTo(this.inputEl(), this.listAlign);
16896 this.list.alignTo(this.inputEl(), this.listAlign);
16897 //this.list.endUpdate();
16901 onEmptyResults : function(){
16903 if(this.tickable && this.editable){
16904 this.hasFocus = false;
16905 this.restrictHeight();
16913 * Returns true if the dropdown list is expanded, else false.
16915 isExpanded : function(){
16916 return this.list.isVisible();
16920 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16921 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16922 * @param {String} value The data value of the item to select
16923 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16924 * selected item if it is not currently in view (defaults to true)
16925 * @return {Boolean} True if the value matched an item in the list, else false
16927 selectByValue : function(v, scrollIntoView){
16928 if(v !== undefined && v !== null){
16929 var r = this.findRecord(this.valueField || this.displayField, v);
16931 this.select(this.store.indexOf(r), scrollIntoView);
16939 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16940 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16941 * @param {Number} index The zero-based index of the list item to select
16942 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16943 * selected item if it is not currently in view (defaults to true)
16945 select : function(index, scrollIntoView){
16946 this.selectedIndex = index;
16947 this.view.select(index);
16948 if(scrollIntoView !== false){
16949 var el = this.view.getNode(index);
16951 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16954 this.list.scrollChildIntoView(el, false);
16960 selectNext : function(){
16961 var ct = this.store.getCount();
16963 if(this.selectedIndex == -1){
16965 }else if(this.selectedIndex < ct-1){
16966 this.select(this.selectedIndex+1);
16972 selectPrev : function(){
16973 var ct = this.store.getCount();
16975 if(this.selectedIndex == -1){
16977 }else if(this.selectedIndex != 0){
16978 this.select(this.selectedIndex-1);
16984 onKeyUp : function(e){
16985 if(this.editable !== false && !e.isSpecialKey()){
16986 this.lastKey = e.getKey();
16987 this.dqTask.delay(this.queryDelay);
16992 validateBlur : function(){
16993 return !this.list || !this.list.isVisible();
16997 initQuery : function(){
16999 var v = this.getRawValue();
17001 if(this.tickable && this.editable){
17002 v = this.tickableInputEl().getValue();
17009 doForce : function(){
17010 if(this.inputEl().dom.value.length > 0){
17011 this.inputEl().dom.value =
17012 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
17018 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
17019 * query allowing the query action to be canceled if needed.
17020 * @param {String} query The SQL query to execute
17021 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
17022 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
17023 * saved in the current store (defaults to false)
17025 doQuery : function(q, forceAll){
17027 if(q === undefined || q === null){
17032 forceAll: forceAll,
17036 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
17041 forceAll = qe.forceAll;
17042 if(forceAll === true || (q.length >= this.minChars)){
17044 this.hasQuery = true;
17046 if(this.lastQuery != q || this.alwaysQuery){
17047 this.lastQuery = q;
17048 if(this.mode == 'local'){
17049 this.selectedIndex = -1;
17051 this.store.clearFilter();
17054 if(this.specialFilter){
17055 this.fireEvent('specialfilter', this);
17060 this.store.filter(this.displayField, q);
17063 this.store.fireEvent("datachanged", this.store);
17070 this.store.baseParams[this.queryParam] = q;
17072 var options = {params : this.getParams(q)};
17075 options.add = true;
17076 options.params.start = this.page * this.pageSize;
17079 this.store.load(options);
17082 * this code will make the page width larger, at the beginning, the list not align correctly,
17083 * we should expand the list on onLoad
17084 * so command out it
17089 this.selectedIndex = -1;
17094 this.loadNext = false;
17098 getParams : function(q){
17100 //p[this.queryParam] = q;
17104 p.limit = this.pageSize;
17110 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
17112 collapse : function(){
17113 if(!this.isExpanded()){
17119 this.hasFocus = false;
17123 this.cancelBtn.hide();
17124 this.trigger.show();
17127 this.tickableInputEl().dom.value = '';
17128 this.tickableInputEl().blur();
17133 Roo.get(document).un('mousedown', this.collapseIf, this);
17134 Roo.get(document).un('mousewheel', this.collapseIf, this);
17135 if (!this.editable) {
17136 Roo.get(document).un('keydown', this.listKeyPress, this);
17138 this.fireEvent('collapse', this);
17144 collapseIf : function(e){
17145 var in_combo = e.within(this.el);
17146 var in_list = e.within(this.list);
17147 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
17149 if (in_combo || in_list || is_list) {
17150 //e.stopPropagation();
17155 this.onTickableFooterButtonClick(e, false, false);
17163 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
17165 expand : function(){
17167 if(this.isExpanded() || !this.hasFocus){
17171 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
17172 this.list.setWidth(lw);
17178 this.restrictHeight();
17182 this.tickItems = Roo.apply([], this.item);
17185 this.cancelBtn.show();
17186 this.trigger.hide();
17189 this.tickableInputEl().focus();
17194 Roo.get(document).on('mousedown', this.collapseIf, this);
17195 Roo.get(document).on('mousewheel', this.collapseIf, this);
17196 if (!this.editable) {
17197 Roo.get(document).on('keydown', this.listKeyPress, this);
17200 this.fireEvent('expand', this);
17204 // Implements the default empty TriggerField.onTriggerClick function
17205 onTriggerClick : function(e)
17207 Roo.log('trigger click');
17209 if(this.disabled || !this.triggerList){
17214 this.loadNext = false;
17216 if(this.isExpanded()){
17218 if (!this.blockFocus) {
17219 this.inputEl().focus();
17223 this.hasFocus = true;
17224 if(this.triggerAction == 'all') {
17225 this.doQuery(this.allQuery, true);
17227 this.doQuery(this.getRawValue());
17229 if (!this.blockFocus) {
17230 this.inputEl().focus();
17235 onTickableTriggerClick : function(e)
17242 this.loadNext = false;
17243 this.hasFocus = true;
17245 if(this.triggerAction == 'all') {
17246 this.doQuery(this.allQuery, true);
17248 this.doQuery(this.getRawValue());
17252 onSearchFieldClick : function(e)
17254 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
17255 this.onTickableFooterButtonClick(e, false, false);
17259 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
17264 this.loadNext = false;
17265 this.hasFocus = true;
17267 if(this.triggerAction == 'all') {
17268 this.doQuery(this.allQuery, true);
17270 this.doQuery(this.getRawValue());
17274 listKeyPress : function(e)
17276 //Roo.log('listkeypress');
17277 // scroll to first matching element based on key pres..
17278 if (e.isSpecialKey()) {
17281 var k = String.fromCharCode(e.getKey()).toUpperCase();
17284 var csel = this.view.getSelectedNodes();
17285 var cselitem = false;
17287 var ix = this.view.indexOf(csel[0]);
17288 cselitem = this.store.getAt(ix);
17289 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
17295 this.store.each(function(v) {
17297 // start at existing selection.
17298 if (cselitem.id == v.id) {
17304 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
17305 match = this.store.indexOf(v);
17311 if (match === false) {
17312 return true; // no more action?
17315 this.view.select(match);
17316 var sn = Roo.get(this.view.getSelectedNodes()[0]);
17317 sn.scrollIntoView(sn.dom.parentNode, false);
17320 onViewScroll : function(e, t){
17322 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){
17326 this.hasQuery = true;
17328 this.loading = this.list.select('.loading', true).first();
17330 if(this.loading === null){
17331 this.list.createChild({
17333 cls: 'loading roo-select2-more-results roo-select2-active',
17334 html: 'Loading more results...'
17337 this.loading = this.list.select('.loading', true).first();
17339 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
17341 this.loading.hide();
17344 this.loading.show();
17349 this.loadNext = true;
17351 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
17356 addItem : function(o)
17358 var dv = ''; // display value
17360 if (this.displayField) {
17361 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17363 // this is an error condition!!!
17364 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
17371 var choice = this.choices.createChild({
17373 cls: 'roo-select2-search-choice',
17382 cls: 'roo-select2-search-choice-close fa fa-times',
17387 }, this.searchField);
17389 var close = choice.select('a.roo-select2-search-choice-close', true).first();
17391 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17399 this.inputEl().dom.value = '';
17404 onRemoveItem : function(e, _self, o)
17406 e.preventDefault();
17408 this.lastItem = Roo.apply([], this.item);
17410 var index = this.item.indexOf(o.data) * 1;
17413 Roo.log('not this item?!');
17417 this.item.splice(index, 1);
17422 this.fireEvent('remove', this, e);
17428 syncValue : function()
17430 if(!this.item.length){
17437 Roo.each(this.item, function(i){
17438 if(_this.valueField){
17439 value.push(i[_this.valueField]);
17446 this.value = value.join(',');
17448 if(this.hiddenField){
17449 this.hiddenField.dom.value = this.value;
17452 this.store.fireEvent("datachanged", this.store);
17457 clearItem : function()
17459 if(!this.multiple){
17465 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17473 if(this.tickable && !Roo.isTouch){
17474 this.view.refresh();
17478 inputEl: function ()
17480 if(Roo.isIOS && this.useNativeIOS){
17481 return this.el.select('select.roo-ios-select', true).first();
17484 if(Roo.isTouch && this.mobileTouchView){
17485 return this.el.select('input.form-control',true).first();
17489 return this.searchField;
17492 return this.el.select('input.form-control',true).first();
17495 onTickableFooterButtonClick : function(e, btn, el)
17497 e.preventDefault();
17499 this.lastItem = Roo.apply([], this.item);
17501 if(btn && btn.name == 'cancel'){
17502 this.tickItems = Roo.apply([], this.item);
17511 Roo.each(this.tickItems, function(o){
17519 validate : function()
17521 if(this.getVisibilityEl().hasClass('hidden')){
17525 var v = this.getRawValue();
17528 v = this.getValue();
17531 if(this.disabled || this.allowBlank || v.length){
17536 this.markInvalid();
17540 tickableInputEl : function()
17542 if(!this.tickable || !this.editable){
17543 return this.inputEl();
17546 return this.inputEl().select('.roo-select2-search-field-input', true).first();
17550 getAutoCreateTouchView : function()
17555 cls: 'form-group' //input-group
17561 type : this.inputType,
17562 cls : 'form-control x-combo-noedit',
17563 autocomplete: 'new-password',
17564 placeholder : this.placeholder || '',
17569 input.name = this.name;
17573 input.cls += ' input-' + this.size;
17576 if (this.disabled) {
17577 input.disabled = true;
17581 cls : 'roo-combobox-wrap',
17588 inputblock.cls += ' input-group';
17590 inputblock.cn.unshift({
17592 cls : 'input-group-addon input-group-prepend input-group-text',
17597 if(this.removable && !this.multiple){
17598 inputblock.cls += ' roo-removable';
17600 inputblock.cn.push({
17603 cls : 'roo-combo-removable-btn close'
17607 if(this.hasFeedback && !this.allowBlank){
17609 inputblock.cls += ' has-feedback';
17611 inputblock.cn.push({
17613 cls: 'glyphicon form-control-feedback'
17620 inputblock.cls += (this.before) ? '' : ' input-group';
17622 inputblock.cn.push({
17624 cls : 'input-group-addon input-group-append input-group-text',
17630 var ibwrap = inputblock;
17635 cls: 'roo-select2-choices',
17639 cls: 'roo-select2-search-field',
17652 cls: 'roo-select2-container input-group roo-touchview-combobox ',
17657 cls: 'form-hidden-field'
17663 if(!this.multiple && this.showToggleBtn){
17669 if (this.caret != false) {
17672 cls: 'fa fa-' + this.caret
17679 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17681 Roo.bootstrap.version == 3 ? caret : '',
17684 cls: 'combobox-clear',
17698 combobox.cls += ' roo-select2-container-multi';
17701 var required = this.allowBlank ? {
17703 style: 'display: none'
17706 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17707 tooltip : 'This field is required'
17710 var align = this.labelAlign || this.parentLabelAlign();
17712 if (align ==='left' && this.fieldLabel.length) {
17718 cls : 'control-label col-form-label',
17719 html : this.fieldLabel
17723 cls : 'roo-combobox-wrap ',
17730 var labelCfg = cfg.cn[1];
17731 var contentCfg = cfg.cn[2];
17734 if(this.indicatorpos == 'right'){
17739 cls : 'control-label col-form-label',
17743 html : this.fieldLabel
17749 cls : "roo-combobox-wrap ",
17757 labelCfg = cfg.cn[0];
17758 contentCfg = cfg.cn[1];
17763 if(this.labelWidth > 12){
17764 labelCfg.style = "width: " + this.labelWidth + 'px';
17767 if(this.labelWidth < 13 && this.labelmd == 0){
17768 this.labelmd = this.labelWidth;
17771 if(this.labellg > 0){
17772 labelCfg.cls += ' col-lg-' + this.labellg;
17773 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17776 if(this.labelmd > 0){
17777 labelCfg.cls += ' col-md-' + this.labelmd;
17778 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17781 if(this.labelsm > 0){
17782 labelCfg.cls += ' col-sm-' + this.labelsm;
17783 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17786 if(this.labelxs > 0){
17787 labelCfg.cls += ' col-xs-' + this.labelxs;
17788 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17792 } else if ( this.fieldLabel.length) {
17797 cls : 'control-label',
17798 html : this.fieldLabel
17809 if(this.indicatorpos == 'right'){
17813 cls : 'control-label',
17814 html : this.fieldLabel,
17832 var settings = this;
17834 ['xs','sm','md','lg'].map(function(size){
17835 if (settings[size]) {
17836 cfg.cls += ' col-' + size + '-' + settings[size];
17843 initTouchView : function()
17845 this.renderTouchView();
17847 this.touchViewEl.on('scroll', function(){
17848 this.el.dom.scrollTop = 0;
17851 this.originalValue = this.getValue();
17853 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17855 this.inputEl().on("click", this.showTouchView, this);
17856 if (this.triggerEl) {
17857 this.triggerEl.on("click", this.showTouchView, this);
17861 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17862 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17864 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17866 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17867 this.store.on('load', this.onTouchViewLoad, this);
17868 this.store.on('loadexception', this.onTouchViewLoadException, this);
17870 if(this.hiddenName){
17872 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17874 this.hiddenField.dom.value =
17875 this.hiddenValue !== undefined ? this.hiddenValue :
17876 this.value !== undefined ? this.value : '';
17878 this.el.dom.removeAttribute('name');
17879 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17883 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17884 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17887 if(this.removable && !this.multiple){
17888 var close = this.closeTriggerEl();
17890 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17891 close.on('click', this.removeBtnClick, this, close);
17895 * fix the bug in Safari iOS8
17897 this.inputEl().on("focus", function(e){
17898 document.activeElement.blur();
17901 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17908 renderTouchView : function()
17910 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17911 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17913 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17914 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17916 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17917 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17918 this.touchViewBodyEl.setStyle('overflow', 'auto');
17920 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17921 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17923 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17924 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17928 showTouchView : function()
17934 this.touchViewHeaderEl.hide();
17936 if(this.modalTitle.length){
17937 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17938 this.touchViewHeaderEl.show();
17941 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17942 this.touchViewEl.show();
17944 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17946 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17947 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17949 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17951 if(this.modalTitle.length){
17952 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17955 this.touchViewBodyEl.setHeight(bodyHeight);
17959 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17961 this.touchViewEl.addClass(['in','show']);
17964 if(this._touchViewMask){
17965 Roo.get(document.body).addClass("x-body-masked");
17966 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17967 this._touchViewMask.setStyle('z-index', 10000);
17968 this._touchViewMask.addClass('show');
17971 this.doTouchViewQuery();
17975 hideTouchView : function()
17977 this.touchViewEl.removeClass(['in','show']);
17981 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17983 this.touchViewEl.setStyle('display', 'none');
17986 if(this._touchViewMask){
17987 this._touchViewMask.removeClass('show');
17988 Roo.get(document.body).removeClass("x-body-masked");
17992 setTouchViewValue : function()
17999 Roo.each(this.tickItems, function(o){
18004 this.hideTouchView();
18007 doTouchViewQuery : function()
18016 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
18020 if(!this.alwaysQuery || this.mode == 'local'){
18021 this.onTouchViewLoad();
18028 onTouchViewBeforeLoad : function(combo,opts)
18034 onTouchViewLoad : function()
18036 if(this.store.getCount() < 1){
18037 this.onTouchViewEmptyResults();
18041 this.clearTouchView();
18043 var rawValue = this.getRawValue();
18045 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
18047 this.tickItems = [];
18049 this.store.data.each(function(d, rowIndex){
18050 var row = this.touchViewListGroup.createChild(template);
18052 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
18053 row.addClass(d.data.cls);
18056 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
18059 html : d.data[this.displayField]
18062 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
18063 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
18066 row.removeClass('selected');
18067 if(!this.multiple && this.valueField &&
18068 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
18071 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18072 row.addClass('selected');
18075 if(this.multiple && this.valueField &&
18076 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
18080 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18081 this.tickItems.push(d.data);
18084 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
18088 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
18090 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18092 if(this.modalTitle.length){
18093 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18096 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
18098 if(this.mobile_restrict_height && listHeight < bodyHeight){
18099 this.touchViewBodyEl.setHeight(listHeight);
18104 if(firstChecked && listHeight > bodyHeight){
18105 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
18110 onTouchViewLoadException : function()
18112 this.hideTouchView();
18115 onTouchViewEmptyResults : function()
18117 this.clearTouchView();
18119 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
18121 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
18125 clearTouchView : function()
18127 this.touchViewListGroup.dom.innerHTML = '';
18130 onTouchViewClick : function(e, el, o)
18132 e.preventDefault();
18135 var rowIndex = o.rowIndex;
18137 var r = this.store.getAt(rowIndex);
18139 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
18141 if(!this.multiple){
18142 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
18143 c.dom.removeAttribute('checked');
18146 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18148 this.setFromData(r.data);
18150 var close = this.closeTriggerEl();
18156 this.hideTouchView();
18158 this.fireEvent('select', this, r, rowIndex);
18163 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
18164 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
18165 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
18169 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18170 this.addItem(r.data);
18171 this.tickItems.push(r.data);
18175 getAutoCreateNativeIOS : function()
18178 cls: 'form-group' //input-group,
18183 cls : 'roo-ios-select'
18187 combobox.name = this.name;
18190 if (this.disabled) {
18191 combobox.disabled = true;
18194 var settings = this;
18196 ['xs','sm','md','lg'].map(function(size){
18197 if (settings[size]) {
18198 cfg.cls += ' col-' + size + '-' + settings[size];
18208 initIOSView : function()
18210 this.store.on('load', this.onIOSViewLoad, this);
18215 onIOSViewLoad : function()
18217 if(this.store.getCount() < 1){
18221 this.clearIOSView();
18223 if(this.allowBlank) {
18225 var default_text = '-- SELECT --';
18227 if(this.placeholder.length){
18228 default_text = this.placeholder;
18231 if(this.emptyTitle.length){
18232 default_text += ' - ' + this.emptyTitle + ' -';
18235 var opt = this.inputEl().createChild({
18238 html : default_text
18242 o[this.valueField] = 0;
18243 o[this.displayField] = default_text;
18245 this.ios_options.push({
18252 this.store.data.each(function(d, rowIndex){
18256 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
18257 html = d.data[this.displayField];
18262 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
18263 value = d.data[this.valueField];
18272 if(this.value == d.data[this.valueField]){
18273 option['selected'] = true;
18276 var opt = this.inputEl().createChild(option);
18278 this.ios_options.push({
18285 this.inputEl().on('change', function(){
18286 this.fireEvent('select', this);
18291 clearIOSView: function()
18293 this.inputEl().dom.innerHTML = '';
18295 this.ios_options = [];
18298 setIOSValue: function(v)
18302 if(!this.ios_options){
18306 Roo.each(this.ios_options, function(opts){
18308 opts.el.dom.removeAttribute('selected');
18310 if(opts.data[this.valueField] != v){
18314 opts.el.dom.setAttribute('selected', true);
18320 * @cfg {Boolean} grow
18324 * @cfg {Number} growMin
18328 * @cfg {Number} growMax
18337 Roo.apply(Roo.bootstrap.ComboBox, {
18341 cls: 'modal-header',
18363 cls: 'list-group-item',
18367 cls: 'roo-combobox-list-group-item-value'
18371 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
18385 listItemCheckbox : {
18387 cls: 'list-group-item',
18391 cls: 'roo-combobox-list-group-item-value'
18395 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18411 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18416 cls: 'modal-footer',
18424 cls: 'col-xs-6 text-left',
18427 cls: 'btn btn-danger roo-touch-view-cancel',
18433 cls: 'col-xs-6 text-right',
18436 cls: 'btn btn-success roo-touch-view-ok',
18447 Roo.apply(Roo.bootstrap.ComboBox, {
18449 touchViewTemplate : {
18451 cls: 'modal fade roo-combobox-touch-view',
18455 cls: 'modal-dialog',
18456 style : 'position:fixed', // we have to fix position....
18460 cls: 'modal-content',
18462 Roo.bootstrap.ComboBox.header,
18463 Roo.bootstrap.ComboBox.body,
18464 Roo.bootstrap.ComboBox.footer
18473 * Ext JS Library 1.1.1
18474 * Copyright(c) 2006-2007, Ext JS, LLC.
18476 * Originally Released Under LGPL - original licence link has changed is not relivant.
18479 * <script type="text/javascript">
18484 * @extends Roo.util.Observable
18485 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
18486 * This class also supports single and multi selection modes. <br>
18487 * Create a data model bound view:
18489 var store = new Roo.data.Store(...);
18491 var view = new Roo.View({
18493 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
18495 singleSelect: true,
18496 selectedClass: "ydataview-selected",
18500 // listen for node click?
18501 view.on("click", function(vw, index, node, e){
18502 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18506 dataModel.load("foobar.xml");
18508 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18510 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18511 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18513 * Note: old style constructor is still suported (container, template, config)
18516 * Create a new View
18517 * @param {Object} config The config object
18520 Roo.View = function(config, depreciated_tpl, depreciated_config){
18522 this.parent = false;
18524 if (typeof(depreciated_tpl) == 'undefined') {
18525 // new way.. - universal constructor.
18526 Roo.apply(this, config);
18527 this.el = Roo.get(this.el);
18530 this.el = Roo.get(config);
18531 this.tpl = depreciated_tpl;
18532 Roo.apply(this, depreciated_config);
18534 this.wrapEl = this.el.wrap().wrap();
18535 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18538 if(typeof(this.tpl) == "string"){
18539 this.tpl = new Roo.Template(this.tpl);
18541 // support xtype ctors..
18542 this.tpl = new Roo.factory(this.tpl, Roo);
18546 this.tpl.compile();
18551 * @event beforeclick
18552 * Fires before a click is processed. Returns false to cancel the default action.
18553 * @param {Roo.View} this
18554 * @param {Number} index The index of the target node
18555 * @param {HTMLElement} node The target node
18556 * @param {Roo.EventObject} e The raw event object
18558 "beforeclick" : true,
18561 * Fires when a template node is clicked.
18562 * @param {Roo.View} this
18563 * @param {Number} index The index of the target node
18564 * @param {HTMLElement} node The target node
18565 * @param {Roo.EventObject} e The raw event object
18570 * Fires when a template node is double clicked.
18571 * @param {Roo.View} this
18572 * @param {Number} index The index of the target node
18573 * @param {HTMLElement} node The target node
18574 * @param {Roo.EventObject} e The raw event object
18578 * @event contextmenu
18579 * Fires when a template node is right clicked.
18580 * @param {Roo.View} this
18581 * @param {Number} index The index of the target node
18582 * @param {HTMLElement} node The target node
18583 * @param {Roo.EventObject} e The raw event object
18585 "contextmenu" : true,
18587 * @event selectionchange
18588 * Fires when the selected nodes change.
18589 * @param {Roo.View} this
18590 * @param {Array} selections Array of the selected nodes
18592 "selectionchange" : true,
18595 * @event beforeselect
18596 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18597 * @param {Roo.View} this
18598 * @param {HTMLElement} node The node to be selected
18599 * @param {Array} selections Array of currently selected nodes
18601 "beforeselect" : true,
18603 * @event preparedata
18604 * Fires on every row to render, to allow you to change the data.
18605 * @param {Roo.View} this
18606 * @param {Object} data to be rendered (change this)
18608 "preparedata" : true
18616 "click": this.onClick,
18617 "dblclick": this.onDblClick,
18618 "contextmenu": this.onContextMenu,
18622 this.selections = [];
18624 this.cmp = new Roo.CompositeElementLite([]);
18626 this.store = Roo.factory(this.store, Roo.data);
18627 this.setStore(this.store, true);
18630 if ( this.footer && this.footer.xtype) {
18632 var fctr = this.wrapEl.appendChild(document.createElement("div"));
18634 this.footer.dataSource = this.store;
18635 this.footer.container = fctr;
18636 this.footer = Roo.factory(this.footer, Roo);
18637 fctr.insertFirst(this.el);
18639 // this is a bit insane - as the paging toolbar seems to detach the el..
18640 // dom.parentNode.parentNode.parentNode
18641 // they get detached?
18645 Roo.View.superclass.constructor.call(this);
18650 Roo.extend(Roo.View, Roo.util.Observable, {
18653 * @cfg {Roo.data.Store} store Data store to load data from.
18658 * @cfg {String|Roo.Element} el The container element.
18663 * @cfg {String|Roo.Template} tpl The template used by this View
18667 * @cfg {String} dataName the named area of the template to use as the data area
18668 * Works with domtemplates roo-name="name"
18672 * @cfg {String} selectedClass The css class to add to selected nodes
18674 selectedClass : "x-view-selected",
18676 * @cfg {String} emptyText The empty text to show when nothing is loaded.
18681 * @cfg {String} text to display on mask (default Loading)
18685 * @cfg {Boolean} multiSelect Allow multiple selection
18687 multiSelect : false,
18689 * @cfg {Boolean} singleSelect Allow single selection
18691 singleSelect: false,
18694 * @cfg {Boolean} toggleSelect - selecting
18696 toggleSelect : false,
18699 * @cfg {Boolean} tickable - selecting
18704 * Returns the element this view is bound to.
18705 * @return {Roo.Element}
18707 getEl : function(){
18708 return this.wrapEl;
18714 * Refreshes the view. - called by datachanged on the store. - do not call directly.
18716 refresh : function(){
18717 //Roo.log('refresh');
18720 // if we are using something like 'domtemplate', then
18721 // the what gets used is:
18722 // t.applySubtemplate(NAME, data, wrapping data..)
18723 // the outer template then get' applied with
18724 // the store 'extra data'
18725 // and the body get's added to the
18726 // roo-name="data" node?
18727 // <span class='roo-tpl-{name}'></span> ?????
18731 this.clearSelections();
18732 this.el.update("");
18734 var records = this.store.getRange();
18735 if(records.length < 1) {
18737 // is this valid?? = should it render a template??
18739 this.el.update(this.emptyText);
18743 if (this.dataName) {
18744 this.el.update(t.apply(this.store.meta)); //????
18745 el = this.el.child('.roo-tpl-' + this.dataName);
18748 for(var i = 0, len = records.length; i < len; i++){
18749 var data = this.prepareData(records[i].data, i, records[i]);
18750 this.fireEvent("preparedata", this, data, i, records[i]);
18752 var d = Roo.apply({}, data);
18755 Roo.apply(d, {'roo-id' : Roo.id()});
18759 Roo.each(this.parent.item, function(item){
18760 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18763 Roo.apply(d, {'roo-data-checked' : 'checked'});
18767 html[html.length] = Roo.util.Format.trim(
18769 t.applySubtemplate(this.dataName, d, this.store.meta) :
18776 el.update(html.join(""));
18777 this.nodes = el.dom.childNodes;
18778 this.updateIndexes(0);
18783 * Function to override to reformat the data that is sent to
18784 * the template for each node.
18785 * DEPRICATED - use the preparedata event handler.
18786 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18787 * a JSON object for an UpdateManager bound view).
18789 prepareData : function(data, index, record)
18791 this.fireEvent("preparedata", this, data, index, record);
18795 onUpdate : function(ds, record){
18796 // Roo.log('on update');
18797 this.clearSelections();
18798 var index = this.store.indexOf(record);
18799 var n = this.nodes[index];
18800 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18801 n.parentNode.removeChild(n);
18802 this.updateIndexes(index, index);
18808 onAdd : function(ds, records, index)
18810 //Roo.log(['on Add', ds, records, index] );
18811 this.clearSelections();
18812 if(this.nodes.length == 0){
18816 var n = this.nodes[index];
18817 for(var i = 0, len = records.length; i < len; i++){
18818 var d = this.prepareData(records[i].data, i, records[i]);
18820 this.tpl.insertBefore(n, d);
18823 this.tpl.append(this.el, d);
18826 this.updateIndexes(index);
18829 onRemove : function(ds, record, index){
18830 // Roo.log('onRemove');
18831 this.clearSelections();
18832 var el = this.dataName ?
18833 this.el.child('.roo-tpl-' + this.dataName) :
18836 el.dom.removeChild(this.nodes[index]);
18837 this.updateIndexes(index);
18841 * Refresh an individual node.
18842 * @param {Number} index
18844 refreshNode : function(index){
18845 this.onUpdate(this.store, this.store.getAt(index));
18848 updateIndexes : function(startIndex, endIndex){
18849 var ns = this.nodes;
18850 startIndex = startIndex || 0;
18851 endIndex = endIndex || ns.length - 1;
18852 for(var i = startIndex; i <= endIndex; i++){
18853 ns[i].nodeIndex = i;
18858 * Changes the data store this view uses and refresh the view.
18859 * @param {Store} store
18861 setStore : function(store, initial){
18862 if(!initial && this.store){
18863 this.store.un("datachanged", this.refresh);
18864 this.store.un("add", this.onAdd);
18865 this.store.un("remove", this.onRemove);
18866 this.store.un("update", this.onUpdate);
18867 this.store.un("clear", this.refresh);
18868 this.store.un("beforeload", this.onBeforeLoad);
18869 this.store.un("load", this.onLoad);
18870 this.store.un("loadexception", this.onLoad);
18874 store.on("datachanged", this.refresh, this);
18875 store.on("add", this.onAdd, this);
18876 store.on("remove", this.onRemove, this);
18877 store.on("update", this.onUpdate, this);
18878 store.on("clear", this.refresh, this);
18879 store.on("beforeload", this.onBeforeLoad, this);
18880 store.on("load", this.onLoad, this);
18881 store.on("loadexception", this.onLoad, this);
18889 * onbeforeLoad - masks the loading area.
18892 onBeforeLoad : function(store,opts)
18894 //Roo.log('onBeforeLoad');
18896 this.el.update("");
18898 this.el.mask(this.mask ? this.mask : "Loading" );
18900 onLoad : function ()
18907 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18908 * @param {HTMLElement} node
18909 * @return {HTMLElement} The template node
18911 findItemFromChild : function(node){
18912 var el = this.dataName ?
18913 this.el.child('.roo-tpl-' + this.dataName,true) :
18916 if(!node || node.parentNode == el){
18919 var p = node.parentNode;
18920 while(p && p != el){
18921 if(p.parentNode == el){
18930 onClick : function(e){
18931 var item = this.findItemFromChild(e.getTarget());
18933 var index = this.indexOf(item);
18934 if(this.onItemClick(item, index, e) !== false){
18935 this.fireEvent("click", this, index, item, e);
18938 this.clearSelections();
18943 onContextMenu : function(e){
18944 var item = this.findItemFromChild(e.getTarget());
18946 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18951 onDblClick : function(e){
18952 var item = this.findItemFromChild(e.getTarget());
18954 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18958 onItemClick : function(item, index, e)
18960 if(this.fireEvent("beforeclick", this, index, item, e) === false){
18963 if (this.toggleSelect) {
18964 var m = this.isSelected(item) ? 'unselect' : 'select';
18967 _t[m](item, true, false);
18970 if(this.multiSelect || this.singleSelect){
18971 if(this.multiSelect && e.shiftKey && this.lastSelection){
18972 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18974 this.select(item, this.multiSelect && e.ctrlKey);
18975 this.lastSelection = item;
18978 if(!this.tickable){
18979 e.preventDefault();
18987 * Get the number of selected nodes.
18990 getSelectionCount : function(){
18991 return this.selections.length;
18995 * Get the currently selected nodes.
18996 * @return {Array} An array of HTMLElements
18998 getSelectedNodes : function(){
18999 return this.selections;
19003 * Get the indexes of the selected nodes.
19006 getSelectedIndexes : function(){
19007 var indexes = [], s = this.selections;
19008 for(var i = 0, len = s.length; i < len; i++){
19009 indexes.push(s[i].nodeIndex);
19015 * Clear all selections
19016 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
19018 clearSelections : function(suppressEvent){
19019 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
19020 this.cmp.elements = this.selections;
19021 this.cmp.removeClass(this.selectedClass);
19022 this.selections = [];
19023 if(!suppressEvent){
19024 this.fireEvent("selectionchange", this, this.selections);
19030 * Returns true if the passed node is selected
19031 * @param {HTMLElement/Number} node The node or node index
19032 * @return {Boolean}
19034 isSelected : function(node){
19035 var s = this.selections;
19039 node = this.getNode(node);
19040 return s.indexOf(node) !== -1;
19045 * @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
19046 * @param {Boolean} keepExisting (optional) true to keep existing selections
19047 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
19049 select : function(nodeInfo, keepExisting, suppressEvent){
19050 if(nodeInfo instanceof Array){
19052 this.clearSelections(true);
19054 for(var i = 0, len = nodeInfo.length; i < len; i++){
19055 this.select(nodeInfo[i], true, true);
19059 var node = this.getNode(nodeInfo);
19060 if(!node || this.isSelected(node)){
19061 return; // already selected.
19064 this.clearSelections(true);
19067 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
19068 Roo.fly(node).addClass(this.selectedClass);
19069 this.selections.push(node);
19070 if(!suppressEvent){
19071 this.fireEvent("selectionchange", this, this.selections);
19079 * @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
19080 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
19081 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
19083 unselect : function(nodeInfo, keepExisting, suppressEvent)
19085 if(nodeInfo instanceof Array){
19086 Roo.each(this.selections, function(s) {
19087 this.unselect(s, nodeInfo);
19091 var node = this.getNode(nodeInfo);
19092 if(!node || !this.isSelected(node)){
19093 //Roo.log("not selected");
19094 return; // not selected.
19098 Roo.each(this.selections, function(s) {
19100 Roo.fly(node).removeClass(this.selectedClass);
19107 this.selections= ns;
19108 this.fireEvent("selectionchange", this, this.selections);
19112 * Gets a template node.
19113 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19114 * @return {HTMLElement} The node or null if it wasn't found
19116 getNode : function(nodeInfo){
19117 if(typeof nodeInfo == "string"){
19118 return document.getElementById(nodeInfo);
19119 }else if(typeof nodeInfo == "number"){
19120 return this.nodes[nodeInfo];
19126 * Gets a range template nodes.
19127 * @param {Number} startIndex
19128 * @param {Number} endIndex
19129 * @return {Array} An array of nodes
19131 getNodes : function(start, end){
19132 var ns = this.nodes;
19133 start = start || 0;
19134 end = typeof end == "undefined" ? ns.length - 1 : end;
19137 for(var i = start; i <= end; i++){
19141 for(var i = start; i >= end; i--){
19149 * Finds the index of the passed node
19150 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19151 * @return {Number} The index of the node or -1
19153 indexOf : function(node){
19154 node = this.getNode(node);
19155 if(typeof node.nodeIndex == "number"){
19156 return node.nodeIndex;
19158 var ns = this.nodes;
19159 for(var i = 0, len = ns.length; i < len; i++){
19170 * based on jquery fullcalendar
19174 Roo.bootstrap = Roo.bootstrap || {};
19176 * @class Roo.bootstrap.Calendar
19177 * @extends Roo.bootstrap.Component
19178 * Bootstrap Calendar class
19179 * @cfg {Boolean} loadMask (true|false) default false
19180 * @cfg {Object} header generate the user specific header of the calendar, default false
19183 * Create a new Container
19184 * @param {Object} config The config object
19189 Roo.bootstrap.Calendar = function(config){
19190 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
19194 * Fires when a date is selected
19195 * @param {DatePicker} this
19196 * @param {Date} date The selected date
19200 * @event monthchange
19201 * Fires when the displayed month changes
19202 * @param {DatePicker} this
19203 * @param {Date} date The selected month
19205 'monthchange': true,
19207 * @event evententer
19208 * Fires when mouse over an event
19209 * @param {Calendar} this
19210 * @param {event} Event
19212 'evententer': true,
19214 * @event eventleave
19215 * Fires when the mouse leaves an
19216 * @param {Calendar} this
19219 'eventleave': true,
19221 * @event eventclick
19222 * Fires when the mouse click an
19223 * @param {Calendar} this
19232 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
19235 * @cfg {Number} startDay
19236 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
19244 getAutoCreate : function(){
19247 var fc_button = function(name, corner, style, content ) {
19248 return Roo.apply({},{
19250 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
19252 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
19255 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
19266 style : 'width:100%',
19273 cls : 'fc-header-left',
19275 fc_button('prev', 'left', 'arrow', '‹' ),
19276 fc_button('next', 'right', 'arrow', '›' ),
19277 { tag: 'span', cls: 'fc-header-space' },
19278 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
19286 cls : 'fc-header-center',
19290 cls: 'fc-header-title',
19293 html : 'month / year'
19301 cls : 'fc-header-right',
19303 /* fc_button('month', 'left', '', 'month' ),
19304 fc_button('week', '', '', 'week' ),
19305 fc_button('day', 'right', '', 'day' )
19317 header = this.header;
19320 var cal_heads = function() {
19322 // fixme - handle this.
19324 for (var i =0; i < Date.dayNames.length; i++) {
19325 var d = Date.dayNames[i];
19328 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
19329 html : d.substring(0,3)
19333 ret[0].cls += ' fc-first';
19334 ret[6].cls += ' fc-last';
19337 var cal_cell = function(n) {
19340 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
19345 cls: 'fc-day-number',
19349 cls: 'fc-day-content',
19353 style: 'position: relative;' // height: 17px;
19365 var cal_rows = function() {
19368 for (var r = 0; r < 6; r++) {
19375 for (var i =0; i < Date.dayNames.length; i++) {
19376 var d = Date.dayNames[i];
19377 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
19380 row.cn[0].cls+=' fc-first';
19381 row.cn[0].cn[0].style = 'min-height:90px';
19382 row.cn[6].cls+=' fc-last';
19386 ret[0].cls += ' fc-first';
19387 ret[4].cls += ' fc-prev-last';
19388 ret[5].cls += ' fc-last';
19395 cls: 'fc-border-separate',
19396 style : 'width:100%',
19404 cls : 'fc-first fc-last',
19422 cls : 'fc-content',
19423 style : "position: relative;",
19426 cls : 'fc-view fc-view-month fc-grid',
19427 style : 'position: relative',
19428 unselectable : 'on',
19431 cls : 'fc-event-container',
19432 style : 'position:absolute;z-index:8;top:0;left:0;'
19450 initEvents : function()
19453 throw "can not find store for calendar";
19459 style: "text-align:center",
19463 style: "background-color:white;width:50%;margin:250 auto",
19467 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
19478 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19480 var size = this.el.select('.fc-content', true).first().getSize();
19481 this.maskEl.setSize(size.width, size.height);
19482 this.maskEl.enableDisplayMode("block");
19483 if(!this.loadMask){
19484 this.maskEl.hide();
19487 this.store = Roo.factory(this.store, Roo.data);
19488 this.store.on('load', this.onLoad, this);
19489 this.store.on('beforeload', this.onBeforeLoad, this);
19493 this.cells = this.el.select('.fc-day',true);
19494 //Roo.log(this.cells);
19495 this.textNodes = this.el.query('.fc-day-number');
19496 this.cells.addClassOnOver('fc-state-hover');
19498 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19499 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19500 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19501 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19503 this.on('monthchange', this.onMonthChange, this);
19505 this.update(new Date().clearTime());
19508 resize : function() {
19509 var sz = this.el.getSize();
19511 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19512 this.el.select('.fc-day-content div',true).setHeight(34);
19517 showPrevMonth : function(e){
19518 this.update(this.activeDate.add("mo", -1));
19520 showToday : function(e){
19521 this.update(new Date().clearTime());
19524 showNextMonth : function(e){
19525 this.update(this.activeDate.add("mo", 1));
19529 showPrevYear : function(){
19530 this.update(this.activeDate.add("y", -1));
19534 showNextYear : function(){
19535 this.update(this.activeDate.add("y", 1));
19540 update : function(date)
19542 var vd = this.activeDate;
19543 this.activeDate = date;
19544 // if(vd && this.el){
19545 // var t = date.getTime();
19546 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19547 // Roo.log('using add remove');
19549 // this.fireEvent('monthchange', this, date);
19551 // this.cells.removeClass("fc-state-highlight");
19552 // this.cells.each(function(c){
19553 // if(c.dateValue == t){
19554 // c.addClass("fc-state-highlight");
19555 // setTimeout(function(){
19556 // try{c.dom.firstChild.focus();}catch(e){}
19566 var days = date.getDaysInMonth();
19568 var firstOfMonth = date.getFirstDateOfMonth();
19569 var startingPos = firstOfMonth.getDay()-this.startDay;
19571 if(startingPos < this.startDay){
19575 var pm = date.add(Date.MONTH, -1);
19576 var prevStart = pm.getDaysInMonth()-startingPos;
19578 this.cells = this.el.select('.fc-day',true);
19579 this.textNodes = this.el.query('.fc-day-number');
19580 this.cells.addClassOnOver('fc-state-hover');
19582 var cells = this.cells.elements;
19583 var textEls = this.textNodes;
19585 Roo.each(cells, function(cell){
19586 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19589 days += startingPos;
19591 // convert everything to numbers so it's fast
19592 var day = 86400000;
19593 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19596 //Roo.log(prevStart);
19598 var today = new Date().clearTime().getTime();
19599 var sel = date.clearTime().getTime();
19600 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19601 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19602 var ddMatch = this.disabledDatesRE;
19603 var ddText = this.disabledDatesText;
19604 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19605 var ddaysText = this.disabledDaysText;
19606 var format = this.format;
19608 var setCellClass = function(cal, cell){
19612 //Roo.log('set Cell Class');
19614 var t = d.getTime();
19618 cell.dateValue = t;
19620 cell.className += " fc-today";
19621 cell.className += " fc-state-highlight";
19622 cell.title = cal.todayText;
19625 // disable highlight in other month..
19626 //cell.className += " fc-state-highlight";
19631 cell.className = " fc-state-disabled";
19632 cell.title = cal.minText;
19636 cell.className = " fc-state-disabled";
19637 cell.title = cal.maxText;
19641 if(ddays.indexOf(d.getDay()) != -1){
19642 cell.title = ddaysText;
19643 cell.className = " fc-state-disabled";
19646 if(ddMatch && format){
19647 var fvalue = d.dateFormat(format);
19648 if(ddMatch.test(fvalue)){
19649 cell.title = ddText.replace("%0", fvalue);
19650 cell.className = " fc-state-disabled";
19654 if (!cell.initialClassName) {
19655 cell.initialClassName = cell.dom.className;
19658 cell.dom.className = cell.initialClassName + ' ' + cell.className;
19663 for(; i < startingPos; i++) {
19664 textEls[i].innerHTML = (++prevStart);
19665 d.setDate(d.getDate()+1);
19667 cells[i].className = "fc-past fc-other-month";
19668 setCellClass(this, cells[i]);
19673 for(; i < days; i++){
19674 intDay = i - startingPos + 1;
19675 textEls[i].innerHTML = (intDay);
19676 d.setDate(d.getDate()+1);
19678 cells[i].className = ''; // "x-date-active";
19679 setCellClass(this, cells[i]);
19683 for(; i < 42; i++) {
19684 textEls[i].innerHTML = (++extraDays);
19685 d.setDate(d.getDate()+1);
19687 cells[i].className = "fc-future fc-other-month";
19688 setCellClass(this, cells[i]);
19691 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19693 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19695 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19696 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19698 if(totalRows != 6){
19699 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19700 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19703 this.fireEvent('monthchange', this, date);
19707 if(!this.internalRender){
19708 var main = this.el.dom.firstChild;
19709 var w = main.offsetWidth;
19710 this.el.setWidth(w + this.el.getBorderWidth("lr"));
19711 Roo.fly(main).setWidth(w);
19712 this.internalRender = true;
19713 // opera does not respect the auto grow header center column
19714 // then, after it gets a width opera refuses to recalculate
19715 // without a second pass
19716 if(Roo.isOpera && !this.secondPass){
19717 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19718 this.secondPass = true;
19719 this.update.defer(10, this, [date]);
19726 findCell : function(dt) {
19727 dt = dt.clearTime().getTime();
19729 this.cells.each(function(c){
19730 //Roo.log("check " +c.dateValue + '?=' + dt);
19731 if(c.dateValue == dt){
19741 findCells : function(ev) {
19742 var s = ev.start.clone().clearTime().getTime();
19744 var e= ev.end.clone().clearTime().getTime();
19747 this.cells.each(function(c){
19748 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19750 if(c.dateValue > e){
19753 if(c.dateValue < s){
19762 // findBestRow: function(cells)
19766 // for (var i =0 ; i < cells.length;i++) {
19767 // ret = Math.max(cells[i].rows || 0,ret);
19774 addItem : function(ev)
19776 // look for vertical location slot in
19777 var cells = this.findCells(ev);
19779 // ev.row = this.findBestRow(cells);
19781 // work out the location.
19785 for(var i =0; i < cells.length; i++) {
19787 cells[i].row = cells[0].row;
19790 cells[i].row = cells[i].row + 1;
19800 if (crow.start.getY() == cells[i].getY()) {
19802 crow.end = cells[i];
19819 cells[0].events.push(ev);
19821 this.calevents.push(ev);
19824 clearEvents: function() {
19826 if(!this.calevents){
19830 Roo.each(this.cells.elements, function(c){
19836 Roo.each(this.calevents, function(e) {
19837 Roo.each(e.els, function(el) {
19838 el.un('mouseenter' ,this.onEventEnter, this);
19839 el.un('mouseleave' ,this.onEventLeave, this);
19844 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19850 renderEvents: function()
19854 this.cells.each(function(c) {
19863 if(c.row != c.events.length){
19864 r = 4 - (4 - (c.row - c.events.length));
19867 c.events = ev.slice(0, r);
19868 c.more = ev.slice(r);
19870 if(c.more.length && c.more.length == 1){
19871 c.events.push(c.more.pop());
19874 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19878 this.cells.each(function(c) {
19880 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19883 for (var e = 0; e < c.events.length; e++){
19884 var ev = c.events[e];
19885 var rows = ev.rows;
19887 for(var i = 0; i < rows.length; i++) {
19889 // how many rows should it span..
19892 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19893 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19895 unselectable : "on",
19898 cls: 'fc-event-inner',
19902 // cls: 'fc-event-time',
19903 // html : cells.length > 1 ? '' : ev.time
19907 cls: 'fc-event-title',
19908 html : String.format('{0}', ev.title)
19915 cls: 'ui-resizable-handle ui-resizable-e',
19916 html : '  '
19923 cfg.cls += ' fc-event-start';
19925 if ((i+1) == rows.length) {
19926 cfg.cls += ' fc-event-end';
19929 var ctr = _this.el.select('.fc-event-container',true).first();
19930 var cg = ctr.createChild(cfg);
19932 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19933 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19935 var r = (c.more.length) ? 1 : 0;
19936 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
19937 cg.setWidth(ebox.right - sbox.x -2);
19939 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19940 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19941 cg.on('click', _this.onEventClick, _this, ev);
19952 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19953 style : 'position: absolute',
19954 unselectable : "on",
19957 cls: 'fc-event-inner',
19961 cls: 'fc-event-title',
19969 cls: 'ui-resizable-handle ui-resizable-e',
19970 html : '  '
19976 var ctr = _this.el.select('.fc-event-container',true).first();
19977 var cg = ctr.createChild(cfg);
19979 var sbox = c.select('.fc-day-content',true).first().getBox();
19980 var ebox = c.select('.fc-day-content',true).first().getBox();
19982 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
19983 cg.setWidth(ebox.right - sbox.x -2);
19985 cg.on('click', _this.onMoreEventClick, _this, c.more);
19995 onEventEnter: function (e, el,event,d) {
19996 this.fireEvent('evententer', this, el, event);
19999 onEventLeave: function (e, el,event,d) {
20000 this.fireEvent('eventleave', this, el, event);
20003 onEventClick: function (e, el,event,d) {
20004 this.fireEvent('eventclick', this, el, event);
20007 onMonthChange: function () {
20011 onMoreEventClick: function(e, el, more)
20015 this.calpopover.placement = 'right';
20016 this.calpopover.setTitle('More');
20018 this.calpopover.setContent('');
20020 var ctr = this.calpopover.el.select('.popover-content', true).first();
20022 Roo.each(more, function(m){
20024 cls : 'fc-event-hori fc-event-draggable',
20027 var cg = ctr.createChild(cfg);
20029 cg.on('click', _this.onEventClick, _this, m);
20032 this.calpopover.show(el);
20037 onLoad: function ()
20039 this.calevents = [];
20042 if(this.store.getCount() > 0){
20043 this.store.data.each(function(d){
20046 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
20047 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
20048 time : d.data.start_time,
20049 title : d.data.title,
20050 description : d.data.description,
20051 venue : d.data.venue
20056 this.renderEvents();
20058 if(this.calevents.length && this.loadMask){
20059 this.maskEl.hide();
20063 onBeforeLoad: function()
20065 this.clearEvents();
20067 this.maskEl.show();
20081 * @class Roo.bootstrap.Popover
20082 * @extends Roo.bootstrap.Component
20083 * Bootstrap Popover class
20084 * @cfg {String} html contents of the popover (or false to use children..)
20085 * @cfg {String} title of popover (or false to hide)
20086 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
20087 * @cfg {String} trigger click || hover (or false to trigger manually)
20088 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
20089 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
20090 * - if false and it has a 'parent' then it will be automatically added to that element
20091 * - if string - Roo.get will be called
20092 * @cfg {Number} delay - delay before showing
20095 * Create a new Popover
20096 * @param {Object} config The config object
20099 Roo.bootstrap.Popover = function(config){
20100 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
20106 * After the popover show
20108 * @param {Roo.bootstrap.Popover} this
20113 * After the popover hide
20115 * @param {Roo.bootstrap.Popover} this
20121 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
20126 placement : 'right',
20127 trigger : 'hover', // hover
20133 can_build_overlaid : false,
20135 maskEl : false, // the mask element
20138 alignEl : false, // when show is called with an element - this get's stored.
20140 getChildContainer : function()
20142 return this.contentEl;
20145 getPopoverHeader : function()
20147 this.title = true; // flag not to hide it..
20148 this.headerEl.addClass('p-0');
20149 return this.headerEl
20153 getAutoCreate : function(){
20156 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
20157 style: 'display:block',
20163 cls : 'popover-inner ',
20167 cls: 'popover-title popover-header',
20168 html : this.title === false ? '' : this.title
20171 cls : 'popover-content popover-body ' + (this.cls || ''),
20172 html : this.html || ''
20183 * @param {string} the title
20185 setTitle: function(str)
20189 this.headerEl.dom.innerHTML = str;
20194 * @param {string} the body content
20196 setContent: function(str)
20199 if (this.contentEl) {
20200 this.contentEl.dom.innerHTML = str;
20204 // as it get's added to the bottom of the page.
20205 onRender : function(ct, position)
20207 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
20212 var cfg = Roo.apply({}, this.getAutoCreate());
20216 cfg.cls += ' ' + this.cls;
20219 cfg.style = this.style;
20221 //Roo.log("adding to ");
20222 this.el = Roo.get(document.body).createChild(cfg, position);
20223 // Roo.log(this.el);
20226 this.contentEl = this.el.select('.popover-content',true).first();
20227 this.headerEl = this.el.select('.popover-title',true).first();
20230 if(typeof(this.items) != 'undefined'){
20231 var items = this.items;
20234 for(var i =0;i < items.length;i++) {
20235 nitems.push(this.addxtype(Roo.apply({}, items[i])));
20239 this.items = nitems;
20241 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
20242 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
20249 resizeMask : function()
20251 this.maskEl.setSize(
20252 Roo.lib.Dom.getViewWidth(true),
20253 Roo.lib.Dom.getViewHeight(true)
20257 initEvents : function()
20261 Roo.bootstrap.Popover.register(this);
20264 this.arrowEl = this.el.select('.arrow',true).first();
20265 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
20266 this.el.enableDisplayMode('block');
20270 if (this.over === false && !this.parent()) {
20273 if (this.triggers === false) {
20278 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
20279 var triggers = this.trigger ? this.trigger.split(' ') : [];
20280 Roo.each(triggers, function(trigger) {
20282 if (trigger == 'click') {
20283 on_el.on('click', this.toggle, this);
20284 } else if (trigger != 'manual') {
20285 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
20286 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
20288 on_el.on(eventIn ,this.enter, this);
20289 on_el.on(eventOut, this.leave, this);
20299 toggle : function () {
20300 this.hoverState == 'in' ? this.leave() : this.enter();
20303 enter : function () {
20305 clearTimeout(this.timeout);
20307 this.hoverState = 'in';
20309 if (!this.delay || !this.delay.show) {
20314 this.timeout = setTimeout(function () {
20315 if (_t.hoverState == 'in') {
20318 }, this.delay.show)
20321 leave : function() {
20322 clearTimeout(this.timeout);
20324 this.hoverState = 'out';
20326 if (!this.delay || !this.delay.hide) {
20331 this.timeout = setTimeout(function () {
20332 if (_t.hoverState == 'out') {
20335 }, this.delay.hide)
20339 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
20340 * @param {string} (left|right|top|bottom) position
20342 show : function (on_el, placement)
20344 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
20345 on_el = on_el || false; // default to false
20348 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
20349 on_el = this.parent().el;
20350 } else if (this.over) {
20351 on_el = Roo.get(this.over);
20356 this.alignEl = Roo.get( on_el );
20359 this.render(document.body);
20365 if (this.title === false) {
20366 this.headerEl.hide();
20371 this.el.dom.style.display = 'block';
20374 if (this.alignEl) {
20375 this.updatePosition(this.placement, true);
20378 // this is usually just done by the builder = to show the popoup in the middle of the scren.
20379 var es = this.el.getSize();
20380 var x = Roo.lib.Dom.getViewWidth()/2;
20381 var y = Roo.lib.Dom.getViewHeight()/2;
20382 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
20387 //var arrow = this.el.select('.arrow',true).first();
20388 //arrow.set(align[2],
20390 this.el.addClass('in');
20394 this.hoverState = 'in';
20397 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
20398 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20399 this.maskEl.dom.style.display = 'block';
20400 this.maskEl.addClass('show');
20402 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20404 this.fireEvent('show', this);
20408 * fire this manually after loading a grid in the table for example
20409 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20410 * @param {Boolean} try and move it if we cant get right position.
20412 updatePosition : function(placement, try_move)
20414 // allow for calling with no parameters
20415 placement = placement ? placement : this.placement;
20416 try_move = typeof(try_move) == 'undefined' ? true : try_move;
20418 this.el.removeClass([
20419 'fade','top','bottom', 'left', 'right','in',
20420 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20422 this.el.addClass(placement + ' bs-popover-' + placement);
20424 if (!this.alignEl ) {
20428 switch (placement) {
20430 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20431 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20432 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20433 //normal display... or moved up/down.
20434 this.el.setXY(offset);
20435 var xy = this.alignEl.getAnchorXY('tr', false);
20437 this.arrowEl.setXY(xy);
20440 // continue through...
20441 return this.updatePosition('left', false);
20445 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20446 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20447 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20448 //normal display... or moved up/down.
20449 this.el.setXY(offset);
20450 var xy = this.alignEl.getAnchorXY('tl', false);
20451 xy[0]-=10;xy[1]+=5; // << fix me
20452 this.arrowEl.setXY(xy);
20456 return this.updatePosition('right', false);
20459 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20460 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20461 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20462 //normal display... or moved up/down.
20463 this.el.setXY(offset);
20464 var xy = this.alignEl.getAnchorXY('t', false);
20465 xy[1]-=10; // << fix me
20466 this.arrowEl.setXY(xy);
20470 return this.updatePosition('bottom', false);
20473 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20474 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20475 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20476 //normal display... or moved up/down.
20477 this.el.setXY(offset);
20478 var xy = this.alignEl.getAnchorXY('b', false);
20479 xy[1]+=2; // << fix me
20480 this.arrowEl.setXY(xy);
20484 return this.updatePosition('top', false);
20495 this.el.setXY([0,0]);
20496 this.el.removeClass('in');
20498 this.hoverState = null;
20499 this.maskEl.hide(); // always..
20500 this.fireEvent('hide', this);
20506 Roo.apply(Roo.bootstrap.Popover, {
20509 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20510 'right' : ['l-br', [10,0], 'right bs-popover-right'],
20511 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20512 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20517 clickHander : false,
20521 onMouseDown : function(e)
20523 if (this.popups.length && !e.getTarget(".roo-popover")) {
20524 /// what is nothing is showing..
20533 register : function(popup)
20535 if (!Roo.bootstrap.Popover.clickHandler) {
20536 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20538 // hide other popups.
20539 popup.on('show', Roo.bootstrap.Popover.onShow, popup);
20540 popup.on('hide', Roo.bootstrap.Popover.onHide, popup);
20541 this.hideAll(); //<< why?
20542 //this.popups.push(popup);
20544 hideAll : function()
20546 this.popups.forEach(function(p) {
20550 onShow : function() {
20551 Roo.bootstrap.Popover.popups.push(this);
20553 onHide : function() {
20554 Roo.bootstrap.Popover.popups.remove(this);
20560 * Card header - holder for the card header elements.
20565 * @class Roo.bootstrap.PopoverNav
20566 * @extends Roo.bootstrap.NavGroup
20567 * Bootstrap Popover header navigation class
20569 * Create a new Popover Header Navigation
20570 * @param {Object} config The config object
20573 Roo.bootstrap.PopoverNav = function(config){
20574 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20577 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
20580 container_method : 'getPopoverHeader'
20598 * @class Roo.bootstrap.Progress
20599 * @extends Roo.bootstrap.Component
20600 * Bootstrap Progress class
20601 * @cfg {Boolean} striped striped of the progress bar
20602 * @cfg {Boolean} active animated of the progress bar
20606 * Create a new Progress
20607 * @param {Object} config The config object
20610 Roo.bootstrap.Progress = function(config){
20611 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20614 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
20619 getAutoCreate : function(){
20627 cfg.cls += ' progress-striped';
20631 cfg.cls += ' active';
20650 * @class Roo.bootstrap.ProgressBar
20651 * @extends Roo.bootstrap.Component
20652 * Bootstrap ProgressBar class
20653 * @cfg {Number} aria_valuenow aria-value now
20654 * @cfg {Number} aria_valuemin aria-value min
20655 * @cfg {Number} aria_valuemax aria-value max
20656 * @cfg {String} label label for the progress bar
20657 * @cfg {String} panel (success | info | warning | danger )
20658 * @cfg {String} role role of the progress bar
20659 * @cfg {String} sr_only text
20663 * Create a new ProgressBar
20664 * @param {Object} config The config object
20667 Roo.bootstrap.ProgressBar = function(config){
20668 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20671 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
20675 aria_valuemax : 100,
20681 getAutoCreate : function()
20686 cls: 'progress-bar',
20687 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20699 cfg.role = this.role;
20702 if(this.aria_valuenow){
20703 cfg['aria-valuenow'] = this.aria_valuenow;
20706 if(this.aria_valuemin){
20707 cfg['aria-valuemin'] = this.aria_valuemin;
20710 if(this.aria_valuemax){
20711 cfg['aria-valuemax'] = this.aria_valuemax;
20714 if(this.label && !this.sr_only){
20715 cfg.html = this.label;
20719 cfg.cls += ' progress-bar-' + this.panel;
20725 update : function(aria_valuenow)
20727 this.aria_valuenow = aria_valuenow;
20729 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20744 * @class Roo.bootstrap.TabGroup
20745 * @extends Roo.bootstrap.Column
20746 * Bootstrap Column class
20747 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20748 * @cfg {Boolean} carousel true to make the group behave like a carousel
20749 * @cfg {Boolean} bullets show bullets for the panels
20750 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20751 * @cfg {Number} timer auto slide timer .. default 0 millisecond
20752 * @cfg {Boolean} showarrow (true|false) show arrow default true
20755 * Create a new TabGroup
20756 * @param {Object} config The config object
20759 Roo.bootstrap.TabGroup = function(config){
20760 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20762 this.navId = Roo.id();
20765 Roo.bootstrap.TabGroup.register(this);
20769 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
20772 transition : false,
20777 slideOnTouch : false,
20780 getAutoCreate : function()
20782 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20784 cfg.cls += ' tab-content';
20786 if (this.carousel) {
20787 cfg.cls += ' carousel slide';
20790 cls : 'carousel-inner',
20794 if(this.bullets && !Roo.isTouch){
20797 cls : 'carousel-bullets',
20801 if(this.bullets_cls){
20802 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20809 cfg.cn[0].cn.push(bullets);
20812 if(this.showarrow){
20813 cfg.cn[0].cn.push({
20815 class : 'carousel-arrow',
20819 class : 'carousel-prev',
20823 class : 'fa fa-chevron-left'
20829 class : 'carousel-next',
20833 class : 'fa fa-chevron-right'
20846 initEvents: function()
20848 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20849 // this.el.on("touchstart", this.onTouchStart, this);
20852 if(this.autoslide){
20855 this.slideFn = window.setInterval(function() {
20856 _this.showPanelNext();
20860 if(this.showarrow){
20861 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20862 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20868 // onTouchStart : function(e, el, o)
20870 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20874 // this.showPanelNext();
20878 getChildContainer : function()
20880 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20884 * register a Navigation item
20885 * @param {Roo.bootstrap.NavItem} the navitem to add
20887 register : function(item)
20889 this.tabs.push( item);
20890 item.navId = this.navId; // not really needed..
20895 getActivePanel : function()
20898 Roo.each(this.tabs, function(t) {
20908 getPanelByName : function(n)
20911 Roo.each(this.tabs, function(t) {
20912 if (t.tabId == n) {
20920 indexOfPanel : function(p)
20923 Roo.each(this.tabs, function(t,i) {
20924 if (t.tabId == p.tabId) {
20933 * show a specific panel
20934 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20935 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20937 showPanel : function (pan)
20939 if(this.transition || typeof(pan) == 'undefined'){
20940 Roo.log("waiting for the transitionend");
20944 if (typeof(pan) == 'number') {
20945 pan = this.tabs[pan];
20948 if (typeof(pan) == 'string') {
20949 pan = this.getPanelByName(pan);
20952 var cur = this.getActivePanel();
20955 Roo.log('pan or acitve pan is undefined');
20959 if (pan.tabId == this.getActivePanel().tabId) {
20963 if (false === cur.fireEvent('beforedeactivate')) {
20967 if(this.bullets > 0 && !Roo.isTouch){
20968 this.setActiveBullet(this.indexOfPanel(pan));
20971 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20973 //class="carousel-item carousel-item-next carousel-item-left"
20975 this.transition = true;
20976 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
20977 var lr = dir == 'next' ? 'left' : 'right';
20978 pan.el.addClass(dir); // or prev
20979 pan.el.addClass('carousel-item-' + dir); // or prev
20980 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20981 cur.el.addClass(lr); // or right
20982 pan.el.addClass(lr);
20983 cur.el.addClass('carousel-item-' +lr); // or right
20984 pan.el.addClass('carousel-item-' +lr);
20988 cur.el.on('transitionend', function() {
20989 Roo.log("trans end?");
20991 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20992 pan.setActive(true);
20994 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20995 cur.setActive(false);
20997 _this.transition = false;
20999 }, this, { single: true } );
21004 cur.setActive(false);
21005 pan.setActive(true);
21010 showPanelNext : function()
21012 var i = this.indexOfPanel(this.getActivePanel());
21014 if (i >= this.tabs.length - 1 && !this.autoslide) {
21018 if (i >= this.tabs.length - 1 && this.autoslide) {
21022 this.showPanel(this.tabs[i+1]);
21025 showPanelPrev : function()
21027 var i = this.indexOfPanel(this.getActivePanel());
21029 if (i < 1 && !this.autoslide) {
21033 if (i < 1 && this.autoslide) {
21034 i = this.tabs.length;
21037 this.showPanel(this.tabs[i-1]);
21041 addBullet: function()
21043 if(!this.bullets || Roo.isTouch){
21046 var ctr = this.el.select('.carousel-bullets',true).first();
21047 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
21048 var bullet = ctr.createChild({
21049 cls : 'bullet bullet-' + i
21050 },ctr.dom.lastChild);
21055 bullet.on('click', (function(e, el, o, ii, t){
21057 e.preventDefault();
21059 this.showPanel(ii);
21061 if(this.autoslide && this.slideFn){
21062 clearInterval(this.slideFn);
21063 this.slideFn = window.setInterval(function() {
21064 _this.showPanelNext();
21068 }).createDelegate(this, [i, bullet], true));
21073 setActiveBullet : function(i)
21079 Roo.each(this.el.select('.bullet', true).elements, function(el){
21080 el.removeClass('selected');
21083 var bullet = this.el.select('.bullet-' + i, true).first();
21089 bullet.addClass('selected');
21100 Roo.apply(Roo.bootstrap.TabGroup, {
21104 * register a Navigation Group
21105 * @param {Roo.bootstrap.NavGroup} the navgroup to add
21107 register : function(navgrp)
21109 this.groups[navgrp.navId] = navgrp;
21113 * fetch a Navigation Group based on the navigation ID
21114 * if one does not exist , it will get created.
21115 * @param {string} the navgroup to add
21116 * @returns {Roo.bootstrap.NavGroup} the navgroup
21118 get: function(navId) {
21119 if (typeof(this.groups[navId]) == 'undefined') {
21120 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
21122 return this.groups[navId] ;
21137 * @class Roo.bootstrap.TabPanel
21138 * @extends Roo.bootstrap.Component
21139 * Bootstrap TabPanel class
21140 * @cfg {Boolean} active panel active
21141 * @cfg {String} html panel content
21142 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
21143 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
21144 * @cfg {String} href click to link..
21145 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
21149 * Create a new TabPanel
21150 * @param {Object} config The config object
21153 Roo.bootstrap.TabPanel = function(config){
21154 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
21158 * Fires when the active status changes
21159 * @param {Roo.bootstrap.TabPanel} this
21160 * @param {Boolean} state the new state
21165 * @event beforedeactivate
21166 * Fires before a tab is de-activated - can be used to do validation on a form.
21167 * @param {Roo.bootstrap.TabPanel} this
21168 * @return {Boolean} false if there is an error
21171 'beforedeactivate': true
21174 this.tabId = this.tabId || Roo.id();
21178 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
21185 touchSlide : false,
21186 getAutoCreate : function(){
21191 // item is needed for carousel - not sure if it has any effect otherwise
21192 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
21193 html: this.html || ''
21197 cfg.cls += ' active';
21201 cfg.tabId = this.tabId;
21209 initEvents: function()
21211 var p = this.parent();
21213 this.navId = this.navId || p.navId;
21215 if (typeof(this.navId) != 'undefined') {
21216 // not really needed.. but just in case.. parent should be a NavGroup.
21217 var tg = Roo.bootstrap.TabGroup.get(this.navId);
21221 var i = tg.tabs.length - 1;
21223 if(this.active && tg.bullets > 0 && i < tg.bullets){
21224 tg.setActiveBullet(i);
21228 this.el.on('click', this.onClick, this);
21230 if(Roo.isTouch && this.touchSlide){
21231 this.el.on("touchstart", this.onTouchStart, this);
21232 this.el.on("touchmove", this.onTouchMove, this);
21233 this.el.on("touchend", this.onTouchEnd, this);
21238 onRender : function(ct, position)
21240 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
21243 setActive : function(state)
21245 Roo.log("panel - set active " + this.tabId + "=" + state);
21247 this.active = state;
21249 this.el.removeClass('active');
21251 } else if (!this.el.hasClass('active')) {
21252 this.el.addClass('active');
21255 this.fireEvent('changed', this, state);
21258 onClick : function(e)
21260 e.preventDefault();
21262 if(!this.href.length){
21266 window.location.href = this.href;
21275 onTouchStart : function(e)
21277 this.swiping = false;
21279 this.startX = e.browserEvent.touches[0].clientX;
21280 this.startY = e.browserEvent.touches[0].clientY;
21283 onTouchMove : function(e)
21285 this.swiping = true;
21287 this.endX = e.browserEvent.touches[0].clientX;
21288 this.endY = e.browserEvent.touches[0].clientY;
21291 onTouchEnd : function(e)
21298 var tabGroup = this.parent();
21300 if(this.endX > this.startX){ // swiping right
21301 tabGroup.showPanelPrev();
21305 if(this.startX > this.endX){ // swiping left
21306 tabGroup.showPanelNext();
21325 * @class Roo.bootstrap.DateField
21326 * @extends Roo.bootstrap.Input
21327 * Bootstrap DateField class
21328 * @cfg {Number} weekStart default 0
21329 * @cfg {String} viewMode default empty, (months|years)
21330 * @cfg {String} minViewMode default empty, (months|years)
21331 * @cfg {Number} startDate default -Infinity
21332 * @cfg {Number} endDate default Infinity
21333 * @cfg {Boolean} todayHighlight default false
21334 * @cfg {Boolean} todayBtn default false
21335 * @cfg {Boolean} calendarWeeks default false
21336 * @cfg {Object} daysOfWeekDisabled default empty
21337 * @cfg {Boolean} singleMode default false (true | false)
21339 * @cfg {Boolean} keyboardNavigation default true
21340 * @cfg {String} language default en
21343 * Create a new DateField
21344 * @param {Object} config The config object
21347 Roo.bootstrap.DateField = function(config){
21348 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
21352 * Fires when this field show.
21353 * @param {Roo.bootstrap.DateField} this
21354 * @param {Mixed} date The date value
21359 * Fires when this field hide.
21360 * @param {Roo.bootstrap.DateField} this
21361 * @param {Mixed} date The date value
21366 * Fires when select a date.
21367 * @param {Roo.bootstrap.DateField} this
21368 * @param {Mixed} date The date value
21372 * @event beforeselect
21373 * Fires when before select a date.
21374 * @param {Roo.bootstrap.DateField} this
21375 * @param {Mixed} date The date value
21377 beforeselect : true
21381 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
21384 * @cfg {String} format
21385 * The default date format string which can be overriden for localization support. The format must be
21386 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
21390 * @cfg {String} altFormats
21391 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
21392 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21394 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21402 todayHighlight : false,
21408 keyboardNavigation: true,
21410 calendarWeeks: false,
21412 startDate: -Infinity,
21416 daysOfWeekDisabled: [],
21420 singleMode : false,
21422 UTCDate: function()
21424 return new Date(Date.UTC.apply(Date, arguments));
21427 UTCToday: function()
21429 var today = new Date();
21430 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21433 getDate: function() {
21434 var d = this.getUTCDate();
21435 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21438 getUTCDate: function() {
21442 setDate: function(d) {
21443 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21446 setUTCDate: function(d) {
21448 this.setValue(this.formatDate(this.date));
21451 onRender: function(ct, position)
21454 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21456 this.language = this.language || 'en';
21457 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21458 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21460 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21461 this.format = this.format || 'm/d/y';
21462 this.isInline = false;
21463 this.isInput = true;
21464 this.component = this.el.select('.add-on', true).first() || false;
21465 this.component = (this.component && this.component.length === 0) ? false : this.component;
21466 this.hasInput = this.component && this.inputEl().length;
21468 if (typeof(this.minViewMode === 'string')) {
21469 switch (this.minViewMode) {
21471 this.minViewMode = 1;
21474 this.minViewMode = 2;
21477 this.minViewMode = 0;
21482 if (typeof(this.viewMode === 'string')) {
21483 switch (this.viewMode) {
21496 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21498 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21500 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21502 this.picker().on('mousedown', this.onMousedown, this);
21503 this.picker().on('click', this.onClick, this);
21505 this.picker().addClass('datepicker-dropdown');
21507 this.startViewMode = this.viewMode;
21509 if(this.singleMode){
21510 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21511 v.setVisibilityMode(Roo.Element.DISPLAY);
21515 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21516 v.setStyle('width', '189px');
21520 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21521 if(!this.calendarWeeks){
21526 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21527 v.attr('colspan', function(i, val){
21528 return parseInt(val) + 1;
21533 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21535 this.setStartDate(this.startDate);
21536 this.setEndDate(this.endDate);
21538 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21545 if(this.isInline) {
21550 picker : function()
21552 return this.pickerEl;
21553 // return this.el.select('.datepicker', true).first();
21556 fillDow: function()
21558 var dowCnt = this.weekStart;
21567 if(this.calendarWeeks){
21575 while (dowCnt < this.weekStart + 7) {
21579 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21583 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21586 fillMonths: function()
21589 var months = this.picker().select('>.datepicker-months td', true).first();
21591 months.dom.innerHTML = '';
21597 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21600 months.createChild(month);
21607 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;
21609 if (this.date < this.startDate) {
21610 this.viewDate = new Date(this.startDate);
21611 } else if (this.date > this.endDate) {
21612 this.viewDate = new Date(this.endDate);
21614 this.viewDate = new Date(this.date);
21622 var d = new Date(this.viewDate),
21623 year = d.getUTCFullYear(),
21624 month = d.getUTCMonth(),
21625 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21626 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21627 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21628 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21629 currentDate = this.date && this.date.valueOf(),
21630 today = this.UTCToday();
21632 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21634 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21636 // this.picker.select('>tfoot th.today').
21637 // .text(dates[this.language].today)
21638 // .toggle(this.todayBtn !== false);
21640 this.updateNavArrows();
21643 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21645 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21647 prevMonth.setUTCDate(day);
21649 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21651 var nextMonth = new Date(prevMonth);
21653 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21655 nextMonth = nextMonth.valueOf();
21657 var fillMonths = false;
21659 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21661 while(prevMonth.valueOf() <= nextMonth) {
21664 if (prevMonth.getUTCDay() === this.weekStart) {
21666 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21674 if(this.calendarWeeks){
21675 // ISO 8601: First week contains first thursday.
21676 // ISO also states week starts on Monday, but we can be more abstract here.
21678 // Start of current week: based on weekstart/current date
21679 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21680 // Thursday of this week
21681 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21682 // First Thursday of year, year from thursday
21683 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21684 // Calendar week: ms between thursdays, div ms per day, div 7 days
21685 calWeek = (th - yth) / 864e5 / 7 + 1;
21687 fillMonths.cn.push({
21695 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21697 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21700 if (this.todayHighlight &&
21701 prevMonth.getUTCFullYear() == today.getFullYear() &&
21702 prevMonth.getUTCMonth() == today.getMonth() &&
21703 prevMonth.getUTCDate() == today.getDate()) {
21704 clsName += ' today';
21707 if (currentDate && prevMonth.valueOf() === currentDate) {
21708 clsName += ' active';
21711 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21712 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21713 clsName += ' disabled';
21716 fillMonths.cn.push({
21718 cls: 'day ' + clsName,
21719 html: prevMonth.getDate()
21722 prevMonth.setDate(prevMonth.getDate()+1);
21725 var currentYear = this.date && this.date.getUTCFullYear();
21726 var currentMonth = this.date && this.date.getUTCMonth();
21728 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21730 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21731 v.removeClass('active');
21733 if(currentYear === year && k === currentMonth){
21734 v.addClass('active');
21737 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21738 v.addClass('disabled');
21744 year = parseInt(year/10, 10) * 10;
21746 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21748 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21751 for (var i = -1; i < 11; i++) {
21752 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21754 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21762 showMode: function(dir)
21765 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21768 Roo.each(this.picker().select('>div',true).elements, function(v){
21769 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21772 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21777 if(this.isInline) {
21781 this.picker().removeClass(['bottom', 'top']);
21783 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21785 * place to the top of element!
21789 this.picker().addClass('top');
21790 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21795 this.picker().addClass('bottom');
21797 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21800 parseDate : function(value)
21802 if(!value || value instanceof Date){
21805 var v = Date.parseDate(value, this.format);
21806 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21807 v = Date.parseDate(value, 'Y-m-d');
21809 if(!v && this.altFormats){
21810 if(!this.altFormatsArray){
21811 this.altFormatsArray = this.altFormats.split("|");
21813 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21814 v = Date.parseDate(value, this.altFormatsArray[i]);
21820 formatDate : function(date, fmt)
21822 return (!date || !(date instanceof Date)) ?
21823 date : date.dateFormat(fmt || this.format);
21826 onFocus : function()
21828 Roo.bootstrap.DateField.superclass.onFocus.call(this);
21832 onBlur : function()
21834 Roo.bootstrap.DateField.superclass.onBlur.call(this);
21836 var d = this.inputEl().getValue();
21843 showPopup : function()
21845 this.picker().show();
21849 this.fireEvent('showpopup', this, this.date);
21852 hidePopup : function()
21854 if(this.isInline) {
21857 this.picker().hide();
21858 this.viewMode = this.startViewMode;
21861 this.fireEvent('hidepopup', this, this.date);
21865 onMousedown: function(e)
21867 e.stopPropagation();
21868 e.preventDefault();
21873 Roo.bootstrap.DateField.superclass.keyup.call(this);
21877 setValue: function(v)
21879 if(this.fireEvent('beforeselect', this, v) !== false){
21880 var d = new Date(this.parseDate(v) ).clearTime();
21882 if(isNaN(d.getTime())){
21883 this.date = this.viewDate = '';
21884 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21888 v = this.formatDate(d);
21890 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21892 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21896 this.fireEvent('select', this, this.date);
21900 getValue: function()
21902 return this.formatDate(this.date);
21905 fireKey: function(e)
21907 if (!this.picker().isVisible()){
21908 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21914 var dateChanged = false,
21916 newDate, newViewDate;
21921 e.preventDefault();
21925 if (!this.keyboardNavigation) {
21928 dir = e.keyCode == 37 ? -1 : 1;
21931 newDate = this.moveYear(this.date, dir);
21932 newViewDate = this.moveYear(this.viewDate, dir);
21933 } else if (e.shiftKey){
21934 newDate = this.moveMonth(this.date, dir);
21935 newViewDate = this.moveMonth(this.viewDate, dir);
21937 newDate = new Date(this.date);
21938 newDate.setUTCDate(this.date.getUTCDate() + dir);
21939 newViewDate = new Date(this.viewDate);
21940 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21942 if (this.dateWithinRange(newDate)){
21943 this.date = newDate;
21944 this.viewDate = newViewDate;
21945 this.setValue(this.formatDate(this.date));
21947 e.preventDefault();
21948 dateChanged = true;
21953 if (!this.keyboardNavigation) {
21956 dir = e.keyCode == 38 ? -1 : 1;
21958 newDate = this.moveYear(this.date, dir);
21959 newViewDate = this.moveYear(this.viewDate, dir);
21960 } else if (e.shiftKey){
21961 newDate = this.moveMonth(this.date, dir);
21962 newViewDate = this.moveMonth(this.viewDate, dir);
21964 newDate = new Date(this.date);
21965 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21966 newViewDate = new Date(this.viewDate);
21967 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21969 if (this.dateWithinRange(newDate)){
21970 this.date = newDate;
21971 this.viewDate = newViewDate;
21972 this.setValue(this.formatDate(this.date));
21974 e.preventDefault();
21975 dateChanged = true;
21979 this.setValue(this.formatDate(this.date));
21981 e.preventDefault();
21984 this.setValue(this.formatDate(this.date));
21998 onClick: function(e)
22000 e.stopPropagation();
22001 e.preventDefault();
22003 var target = e.getTarget();
22005 if(target.nodeName.toLowerCase() === 'i'){
22006 target = Roo.get(target).dom.parentNode;
22009 var nodeName = target.nodeName;
22010 var className = target.className;
22011 var html = target.innerHTML;
22012 //Roo.log(nodeName);
22014 switch(nodeName.toLowerCase()) {
22016 switch(className) {
22022 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
22023 switch(this.viewMode){
22025 this.viewDate = this.moveMonth(this.viewDate, dir);
22029 this.viewDate = this.moveYear(this.viewDate, dir);
22035 var date = new Date();
22036 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
22038 this.setValue(this.formatDate(this.date));
22045 if (className.indexOf('disabled') < 0) {
22046 if (!this.viewDate) {
22047 this.viewDate = new Date();
22049 this.viewDate.setUTCDate(1);
22050 if (className.indexOf('month') > -1) {
22051 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
22053 var year = parseInt(html, 10) || 0;
22054 this.viewDate.setUTCFullYear(year);
22058 if(this.singleMode){
22059 this.setValue(this.formatDate(this.viewDate));
22070 //Roo.log(className);
22071 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
22072 var day = parseInt(html, 10) || 1;
22073 var year = (this.viewDate || new Date()).getUTCFullYear(),
22074 month = (this.viewDate || new Date()).getUTCMonth();
22076 if (className.indexOf('old') > -1) {
22083 } else if (className.indexOf('new') > -1) {
22091 //Roo.log([year,month,day]);
22092 this.date = this.UTCDate(year, month, day,0,0,0,0);
22093 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
22095 //Roo.log(this.formatDate(this.date));
22096 this.setValue(this.formatDate(this.date));
22103 setStartDate: function(startDate)
22105 this.startDate = startDate || -Infinity;
22106 if (this.startDate !== -Infinity) {
22107 this.startDate = this.parseDate(this.startDate);
22110 this.updateNavArrows();
22113 setEndDate: function(endDate)
22115 this.endDate = endDate || Infinity;
22116 if (this.endDate !== Infinity) {
22117 this.endDate = this.parseDate(this.endDate);
22120 this.updateNavArrows();
22123 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
22125 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
22126 if (typeof(this.daysOfWeekDisabled) !== 'object') {
22127 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
22129 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
22130 return parseInt(d, 10);
22133 this.updateNavArrows();
22136 updateNavArrows: function()
22138 if(this.singleMode){
22142 var d = new Date(this.viewDate),
22143 year = d.getUTCFullYear(),
22144 month = d.getUTCMonth();
22146 Roo.each(this.picker().select('.prev', true).elements, function(v){
22148 switch (this.viewMode) {
22151 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
22157 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
22164 Roo.each(this.picker().select('.next', true).elements, function(v){
22166 switch (this.viewMode) {
22169 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
22175 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
22183 moveMonth: function(date, dir)
22188 var new_date = new Date(date.valueOf()),
22189 day = new_date.getUTCDate(),
22190 month = new_date.getUTCMonth(),
22191 mag = Math.abs(dir),
22193 dir = dir > 0 ? 1 : -1;
22196 // If going back one month, make sure month is not current month
22197 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
22199 return new_date.getUTCMonth() == month;
22201 // If going forward one month, make sure month is as expected
22202 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
22204 return new_date.getUTCMonth() != new_month;
22206 new_month = month + dir;
22207 new_date.setUTCMonth(new_month);
22208 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
22209 if (new_month < 0 || new_month > 11) {
22210 new_month = (new_month + 12) % 12;
22213 // For magnitudes >1, move one month at a time...
22214 for (var i=0; i<mag; i++) {
22215 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
22216 new_date = this.moveMonth(new_date, dir);
22218 // ...then reset the day, keeping it in the new month
22219 new_month = new_date.getUTCMonth();
22220 new_date.setUTCDate(day);
22222 return new_month != new_date.getUTCMonth();
22225 // Common date-resetting loop -- if date is beyond end of month, make it
22228 new_date.setUTCDate(--day);
22229 new_date.setUTCMonth(new_month);
22234 moveYear: function(date, dir)
22236 return this.moveMonth(date, dir*12);
22239 dateWithinRange: function(date)
22241 return date >= this.startDate && date <= this.endDate;
22247 this.picker().remove();
22250 validateValue : function(value)
22252 if(this.getVisibilityEl().hasClass('hidden')){
22256 if(value.length < 1) {
22257 if(this.allowBlank){
22263 if(value.length < this.minLength){
22266 if(value.length > this.maxLength){
22270 var vt = Roo.form.VTypes;
22271 if(!vt[this.vtype](value, this)){
22275 if(typeof this.validator == "function"){
22276 var msg = this.validator(value);
22282 if(this.regex && !this.regex.test(value)){
22286 if(typeof(this.parseDate(value)) == 'undefined'){
22290 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
22294 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
22304 this.date = this.viewDate = '';
22306 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22311 Roo.apply(Roo.bootstrap.DateField, {
22322 html: '<i class="fa fa-arrow-left"/>'
22332 html: '<i class="fa fa-arrow-right"/>'
22374 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
22375 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
22376 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
22377 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22378 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
22391 navFnc: 'FullYear',
22396 navFnc: 'FullYear',
22401 Roo.apply(Roo.bootstrap.DateField, {
22405 cls: 'datepicker dropdown-menu roo-dynamic shadow',
22409 cls: 'datepicker-days',
22413 cls: 'table-condensed',
22415 Roo.bootstrap.DateField.head,
22419 Roo.bootstrap.DateField.footer
22426 cls: 'datepicker-months',
22430 cls: 'table-condensed',
22432 Roo.bootstrap.DateField.head,
22433 Roo.bootstrap.DateField.content,
22434 Roo.bootstrap.DateField.footer
22441 cls: 'datepicker-years',
22445 cls: 'table-condensed',
22447 Roo.bootstrap.DateField.head,
22448 Roo.bootstrap.DateField.content,
22449 Roo.bootstrap.DateField.footer
22468 * @class Roo.bootstrap.TimeField
22469 * @extends Roo.bootstrap.Input
22470 * Bootstrap DateField class
22474 * Create a new TimeField
22475 * @param {Object} config The config object
22478 Roo.bootstrap.TimeField = function(config){
22479 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22483 * Fires when this field show.
22484 * @param {Roo.bootstrap.DateField} thisthis
22485 * @param {Mixed} date The date value
22490 * Fires when this field hide.
22491 * @param {Roo.bootstrap.DateField} this
22492 * @param {Mixed} date The date value
22497 * Fires when select a date.
22498 * @param {Roo.bootstrap.DateField} this
22499 * @param {Mixed} date The date value
22505 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
22508 * @cfg {String} format
22509 * The default time format string which can be overriden for localization support. The format must be
22510 * valid according to {@link Date#parseDate} (defaults to 'H:i').
22514 getAutoCreate : function()
22516 this.after = '<i class="fa far fa-clock"></i>';
22517 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22521 onRender: function(ct, position)
22524 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22526 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22528 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22530 this.pop = this.picker().select('>.datepicker-time',true).first();
22531 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22533 this.picker().on('mousedown', this.onMousedown, this);
22534 this.picker().on('click', this.onClick, this);
22536 this.picker().addClass('datepicker-dropdown');
22541 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22542 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22543 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22544 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22545 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22546 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22550 fireKey: function(e){
22551 if (!this.picker().isVisible()){
22552 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22558 e.preventDefault();
22566 this.onTogglePeriod();
22569 this.onIncrementMinutes();
22572 this.onDecrementMinutes();
22581 onClick: function(e) {
22582 e.stopPropagation();
22583 e.preventDefault();
22586 picker : function()
22588 return this.pickerEl;
22591 fillTime: function()
22593 var time = this.pop.select('tbody', true).first();
22595 time.dom.innerHTML = '';
22610 cls: 'hours-up fa fas fa-chevron-up'
22630 cls: 'minutes-up fa fas fa-chevron-up'
22651 cls: 'timepicker-hour',
22666 cls: 'timepicker-minute',
22681 cls: 'btn btn-primary period',
22703 cls: 'hours-down fa fas fa-chevron-down'
22723 cls: 'minutes-down fa fas fa-chevron-down'
22741 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22748 var hours = this.time.getHours();
22749 var minutes = this.time.getMinutes();
22762 hours = hours - 12;
22766 hours = '0' + hours;
22770 minutes = '0' + minutes;
22773 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22774 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22775 this.pop.select('button', true).first().dom.innerHTML = period;
22781 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22783 var cls = ['bottom'];
22785 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22792 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22796 //this.picker().setXY(20000,20000);
22797 this.picker().addClass(cls.join('-'));
22801 Roo.each(cls, function(c){
22806 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
22807 //_this.picker().setTop(_this.inputEl().getHeight());
22811 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
22813 //_this.picker().setTop(0 - _this.picker().getHeight());
22818 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22822 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22830 onFocus : function()
22832 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22836 onBlur : function()
22838 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22844 this.picker().show();
22849 this.fireEvent('show', this, this.date);
22854 this.picker().hide();
22857 this.fireEvent('hide', this, this.date);
22860 setTime : function()
22863 this.setValue(this.time.format(this.format));
22865 this.fireEvent('select', this, this.date);
22870 onMousedown: function(e){
22871 e.stopPropagation();
22872 e.preventDefault();
22875 onIncrementHours: function()
22877 Roo.log('onIncrementHours');
22878 this.time = this.time.add(Date.HOUR, 1);
22883 onDecrementHours: function()
22885 Roo.log('onDecrementHours');
22886 this.time = this.time.add(Date.HOUR, -1);
22890 onIncrementMinutes: function()
22892 Roo.log('onIncrementMinutes');
22893 this.time = this.time.add(Date.MINUTE, 1);
22897 onDecrementMinutes: function()
22899 Roo.log('onDecrementMinutes');
22900 this.time = this.time.add(Date.MINUTE, -1);
22904 onTogglePeriod: function()
22906 Roo.log('onTogglePeriod');
22907 this.time = this.time.add(Date.HOUR, 12);
22915 Roo.apply(Roo.bootstrap.TimeField, {
22919 cls: 'datepicker dropdown-menu',
22923 cls: 'datepicker-time',
22927 cls: 'table-condensed',
22956 cls: 'btn btn-info ok',
22984 * @class Roo.bootstrap.MonthField
22985 * @extends Roo.bootstrap.Input
22986 * Bootstrap MonthField class
22988 * @cfg {String} language default en
22991 * Create a new MonthField
22992 * @param {Object} config The config object
22995 Roo.bootstrap.MonthField = function(config){
22996 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
23001 * Fires when this field show.
23002 * @param {Roo.bootstrap.MonthField} this
23003 * @param {Mixed} date The date value
23008 * Fires when this field hide.
23009 * @param {Roo.bootstrap.MonthField} this
23010 * @param {Mixed} date The date value
23015 * Fires when select a date.
23016 * @param {Roo.bootstrap.MonthField} this
23017 * @param {String} oldvalue The old value
23018 * @param {String} newvalue The new value
23024 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
23026 onRender: function(ct, position)
23029 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
23031 this.language = this.language || 'en';
23032 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
23033 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
23035 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
23036 this.isInline = false;
23037 this.isInput = true;
23038 this.component = this.el.select('.add-on', true).first() || false;
23039 this.component = (this.component && this.component.length === 0) ? false : this.component;
23040 this.hasInput = this.component && this.inputEL().length;
23042 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
23044 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23046 this.picker().on('mousedown', this.onMousedown, this);
23047 this.picker().on('click', this.onClick, this);
23049 this.picker().addClass('datepicker-dropdown');
23051 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
23052 v.setStyle('width', '189px');
23059 if(this.isInline) {
23065 setValue: function(v, suppressEvent)
23067 var o = this.getValue();
23069 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
23073 if(suppressEvent !== true){
23074 this.fireEvent('select', this, o, v);
23079 getValue: function()
23084 onClick: function(e)
23086 e.stopPropagation();
23087 e.preventDefault();
23089 var target = e.getTarget();
23091 if(target.nodeName.toLowerCase() === 'i'){
23092 target = Roo.get(target).dom.parentNode;
23095 var nodeName = target.nodeName;
23096 var className = target.className;
23097 var html = target.innerHTML;
23099 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
23103 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
23105 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23111 picker : function()
23113 return this.pickerEl;
23116 fillMonths: function()
23119 var months = this.picker().select('>.datepicker-months td', true).first();
23121 months.dom.innerHTML = '';
23127 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
23130 months.createChild(month);
23139 if(typeof(this.vIndex) == 'undefined' && this.value.length){
23140 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
23143 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
23144 e.removeClass('active');
23146 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
23147 e.addClass('active');
23154 if(this.isInline) {
23158 this.picker().removeClass(['bottom', 'top']);
23160 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23162 * place to the top of element!
23166 this.picker().addClass('top');
23167 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23172 this.picker().addClass('bottom');
23174 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23177 onFocus : function()
23179 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
23183 onBlur : function()
23185 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
23187 var d = this.inputEl().getValue();
23196 this.picker().show();
23197 this.picker().select('>.datepicker-months', true).first().show();
23201 this.fireEvent('show', this, this.date);
23206 if(this.isInline) {
23209 this.picker().hide();
23210 this.fireEvent('hide', this, this.date);
23214 onMousedown: function(e)
23216 e.stopPropagation();
23217 e.preventDefault();
23222 Roo.bootstrap.MonthField.superclass.keyup.call(this);
23226 fireKey: function(e)
23228 if (!this.picker().isVisible()){
23229 if (e.keyCode == 27) {// allow escape to hide and re-show picker
23240 e.preventDefault();
23244 dir = e.keyCode == 37 ? -1 : 1;
23246 this.vIndex = this.vIndex + dir;
23248 if(this.vIndex < 0){
23252 if(this.vIndex > 11){
23256 if(isNaN(this.vIndex)){
23260 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23266 dir = e.keyCode == 38 ? -1 : 1;
23268 this.vIndex = this.vIndex + dir * 4;
23270 if(this.vIndex < 0){
23274 if(this.vIndex > 11){
23278 if(isNaN(this.vIndex)){
23282 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23287 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23288 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23292 e.preventDefault();
23295 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23296 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23312 this.picker().remove();
23317 Roo.apply(Roo.bootstrap.MonthField, {
23336 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23337 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
23342 Roo.apply(Roo.bootstrap.MonthField, {
23346 cls: 'datepicker dropdown-menu roo-dynamic',
23350 cls: 'datepicker-months',
23354 cls: 'table-condensed',
23356 Roo.bootstrap.DateField.content
23376 * @class Roo.bootstrap.CheckBox
23377 * @extends Roo.bootstrap.Input
23378 * Bootstrap CheckBox class
23380 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23381 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
23382 * @cfg {String} boxLabel The text that appears beside the checkbox
23383 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
23384 * @cfg {Boolean} checked initnal the element
23385 * @cfg {Boolean} inline inline the element (default false)
23386 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
23387 * @cfg {String} tooltip label tooltip
23390 * Create a new CheckBox
23391 * @param {Object} config The config object
23394 Roo.bootstrap.CheckBox = function(config){
23395 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23400 * Fires when the element is checked or unchecked.
23401 * @param {Roo.bootstrap.CheckBox} this This input
23402 * @param {Boolean} checked The new checked value
23407 * Fires when the element is click.
23408 * @param {Roo.bootstrap.CheckBox} this This input
23415 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
23417 inputType: 'checkbox',
23426 // checkbox success does not make any sense really..
23431 getAutoCreate : function()
23433 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23439 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23442 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
23448 type : this.inputType,
23449 value : this.inputValue,
23450 cls : 'roo-' + this.inputType, //'form-box',
23451 placeholder : this.placeholder || ''
23455 if(this.inputType != 'radio'){
23459 cls : 'roo-hidden-value',
23460 value : this.checked ? this.inputValue : this.valueOff
23465 if (this.weight) { // Validity check?
23466 cfg.cls += " " + this.inputType + "-" + this.weight;
23469 if (this.disabled) {
23470 input.disabled=true;
23474 input.checked = this.checked;
23479 input.name = this.name;
23481 if(this.inputType != 'radio'){
23482 hidden.name = this.name;
23483 input.name = '_hidden_' + this.name;
23488 input.cls += ' input-' + this.size;
23493 ['xs','sm','md','lg'].map(function(size){
23494 if (settings[size]) {
23495 cfg.cls += ' col-' + size + '-' + settings[size];
23499 var inputblock = input;
23501 if (this.before || this.after) {
23504 cls : 'input-group',
23509 inputblock.cn.push({
23511 cls : 'input-group-addon',
23516 inputblock.cn.push(input);
23518 if(this.inputType != 'radio'){
23519 inputblock.cn.push(hidden);
23523 inputblock.cn.push({
23525 cls : 'input-group-addon',
23531 var boxLabelCfg = false;
23537 //'for': id, // box label is handled by onclick - so no for...
23539 html: this.boxLabel
23542 boxLabelCfg.tooltip = this.tooltip;
23548 if (align ==='left' && this.fieldLabel.length) {
23549 // Roo.log("left and has label");
23554 cls : 'control-label',
23555 html : this.fieldLabel
23566 cfg.cn[1].cn.push(boxLabelCfg);
23569 if(this.labelWidth > 12){
23570 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23573 if(this.labelWidth < 13 && this.labelmd == 0){
23574 this.labelmd = this.labelWidth;
23577 if(this.labellg > 0){
23578 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23579 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23582 if(this.labelmd > 0){
23583 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23584 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23587 if(this.labelsm > 0){
23588 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23589 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23592 if(this.labelxs > 0){
23593 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23594 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23597 } else if ( this.fieldLabel.length) {
23598 // Roo.log(" label");
23602 tag: this.boxLabel ? 'span' : 'label',
23604 cls: 'control-label box-input-label',
23605 //cls : 'input-group-addon',
23606 html : this.fieldLabel
23613 cfg.cn.push(boxLabelCfg);
23618 // Roo.log(" no label && no align");
23619 cfg.cn = [ inputblock ] ;
23621 cfg.cn.push(boxLabelCfg);
23629 if(this.inputType != 'radio'){
23630 cfg.cn.push(hidden);
23638 * return the real input element.
23640 inputEl: function ()
23642 return this.el.select('input.roo-' + this.inputType,true).first();
23644 hiddenEl: function ()
23646 return this.el.select('input.roo-hidden-value',true).first();
23649 labelEl: function()
23651 return this.el.select('label.control-label',true).first();
23653 /* depricated... */
23657 return this.labelEl();
23660 boxLabelEl: function()
23662 return this.el.select('label.box-label',true).first();
23665 initEvents : function()
23667 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23669 this.inputEl().on('click', this.onClick, this);
23671 if (this.boxLabel) {
23672 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
23675 this.startValue = this.getValue();
23678 Roo.bootstrap.CheckBox.register(this);
23682 onClick : function(e)
23684 if(this.fireEvent('click', this, e) !== false){
23685 this.setChecked(!this.checked);
23690 setChecked : function(state,suppressEvent)
23692 this.startValue = this.getValue();
23694 if(this.inputType == 'radio'){
23696 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23697 e.dom.checked = false;
23700 this.inputEl().dom.checked = true;
23702 this.inputEl().dom.value = this.inputValue;
23704 if(suppressEvent !== true){
23705 this.fireEvent('check', this, true);
23713 this.checked = state;
23715 this.inputEl().dom.checked = state;
23718 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23720 if(suppressEvent !== true){
23721 this.fireEvent('check', this, state);
23727 getValue : function()
23729 if(this.inputType == 'radio'){
23730 return this.getGroupValue();
23733 return this.hiddenEl().dom.value;
23737 getGroupValue : function()
23739 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23743 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23746 setValue : function(v,suppressEvent)
23748 if(this.inputType == 'radio'){
23749 this.setGroupValue(v, suppressEvent);
23753 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23758 setGroupValue : function(v, suppressEvent)
23760 this.startValue = this.getValue();
23762 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23763 e.dom.checked = false;
23765 if(e.dom.value == v){
23766 e.dom.checked = true;
23770 if(suppressEvent !== true){
23771 this.fireEvent('check', this, true);
23779 validate : function()
23781 if(this.getVisibilityEl().hasClass('hidden')){
23787 (this.inputType == 'radio' && this.validateRadio()) ||
23788 (this.inputType == 'checkbox' && this.validateCheckbox())
23794 this.markInvalid();
23798 validateRadio : function()
23800 if(this.getVisibilityEl().hasClass('hidden')){
23804 if(this.allowBlank){
23810 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23811 if(!e.dom.checked){
23823 validateCheckbox : function()
23826 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23827 //return (this.getValue() == this.inputValue) ? true : false;
23830 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23838 for(var i in group){
23839 if(group[i].el.isVisible(true)){
23847 for(var i in group){
23852 r = (group[i].getValue() == group[i].inputValue) ? true : false;
23859 * Mark this field as valid
23861 markValid : function()
23865 this.fireEvent('valid', this);
23867 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23870 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23877 if(this.inputType == 'radio'){
23878 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23879 var fg = e.findParent('.form-group', false, true);
23880 if (Roo.bootstrap.version == 3) {
23881 fg.removeClass([_this.invalidClass, _this.validClass]);
23882 fg.addClass(_this.validClass);
23884 fg.removeClass(['is-valid', 'is-invalid']);
23885 fg.addClass('is-valid');
23893 var fg = this.el.findParent('.form-group', false, true);
23894 if (Roo.bootstrap.version == 3) {
23895 fg.removeClass([this.invalidClass, this.validClass]);
23896 fg.addClass(this.validClass);
23898 fg.removeClass(['is-valid', 'is-invalid']);
23899 fg.addClass('is-valid');
23904 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23910 for(var i in group){
23911 var fg = group[i].el.findParent('.form-group', false, true);
23912 if (Roo.bootstrap.version == 3) {
23913 fg.removeClass([this.invalidClass, this.validClass]);
23914 fg.addClass(this.validClass);
23916 fg.removeClass(['is-valid', 'is-invalid']);
23917 fg.addClass('is-valid');
23923 * Mark this field as invalid
23924 * @param {String} msg The validation message
23926 markInvalid : function(msg)
23928 if(this.allowBlank){
23934 this.fireEvent('invalid', this, msg);
23936 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23939 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23943 label.markInvalid();
23946 if(this.inputType == 'radio'){
23948 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23949 var fg = e.findParent('.form-group', false, true);
23950 if (Roo.bootstrap.version == 3) {
23951 fg.removeClass([_this.invalidClass, _this.validClass]);
23952 fg.addClass(_this.invalidClass);
23954 fg.removeClass(['is-invalid', 'is-valid']);
23955 fg.addClass('is-invalid');
23963 var fg = this.el.findParent('.form-group', false, true);
23964 if (Roo.bootstrap.version == 3) {
23965 fg.removeClass([_this.invalidClass, _this.validClass]);
23966 fg.addClass(_this.invalidClass);
23968 fg.removeClass(['is-invalid', 'is-valid']);
23969 fg.addClass('is-invalid');
23974 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23980 for(var i in group){
23981 var fg = group[i].el.findParent('.form-group', false, true);
23982 if (Roo.bootstrap.version == 3) {
23983 fg.removeClass([_this.invalidClass, _this.validClass]);
23984 fg.addClass(_this.invalidClass);
23986 fg.removeClass(['is-invalid', 'is-valid']);
23987 fg.addClass('is-invalid');
23993 clearInvalid : function()
23995 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23997 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23999 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24001 if (label && label.iconEl) {
24002 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
24003 label.iconEl.removeClass(['is-invalid', 'is-valid']);
24007 disable : function()
24009 if(this.inputType != 'radio'){
24010 Roo.bootstrap.CheckBox.superclass.disable.call(this);
24017 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24018 _this.getActionEl().addClass(this.disabledClass);
24019 e.dom.disabled = true;
24023 this.disabled = true;
24024 this.fireEvent("disable", this);
24028 enable : function()
24030 if(this.inputType != 'radio'){
24031 Roo.bootstrap.CheckBox.superclass.enable.call(this);
24038 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24039 _this.getActionEl().removeClass(this.disabledClass);
24040 e.dom.disabled = false;
24044 this.disabled = false;
24045 this.fireEvent("enable", this);
24049 setBoxLabel : function(v)
24054 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24060 Roo.apply(Roo.bootstrap.CheckBox, {
24065 * register a CheckBox Group
24066 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
24068 register : function(checkbox)
24070 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
24071 this.groups[checkbox.groupId] = {};
24074 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
24078 this.groups[checkbox.groupId][checkbox.name] = checkbox;
24082 * fetch a CheckBox Group based on the group ID
24083 * @param {string} the group ID
24084 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
24086 get: function(groupId) {
24087 if (typeof(this.groups[groupId]) == 'undefined') {
24091 return this.groups[groupId] ;
24104 * @class Roo.bootstrap.Radio
24105 * @extends Roo.bootstrap.Component
24106 * Bootstrap Radio class
24107 * @cfg {String} boxLabel - the label associated
24108 * @cfg {String} value - the value of radio
24111 * Create a new Radio
24112 * @param {Object} config The config object
24114 Roo.bootstrap.Radio = function(config){
24115 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
24119 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
24125 getAutoCreate : function()
24129 cls : 'form-group radio',
24134 html : this.boxLabel
24142 initEvents : function()
24144 this.parent().register(this);
24146 this.el.on('click', this.onClick, this);
24150 onClick : function(e)
24152 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
24153 this.setChecked(true);
24157 setChecked : function(state, suppressEvent)
24159 this.parent().setValue(this.value, suppressEvent);
24163 setBoxLabel : function(v)
24168 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24183 * @class Roo.bootstrap.SecurePass
24184 * @extends Roo.bootstrap.Input
24185 * Bootstrap SecurePass class
24189 * Create a new SecurePass
24190 * @param {Object} config The config object
24193 Roo.bootstrap.SecurePass = function (config) {
24194 // these go here, so the translation tool can replace them..
24196 PwdEmpty: "Please type a password, and then retype it to confirm.",
24197 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24198 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24199 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24200 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24201 FNInPwd: "Your password can't contain your first name. Please type a different password.",
24202 LNInPwd: "Your password can't contain your last name. Please type a different password.",
24203 TooWeak: "Your password is Too Weak."
24205 this.meterLabel = "Password strength:";
24206 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
24207 this.meterClass = [
24208 "roo-password-meter-tooweak",
24209 "roo-password-meter-weak",
24210 "roo-password-meter-medium",
24211 "roo-password-meter-strong",
24212 "roo-password-meter-grey"
24217 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
24220 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
24222 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
24224 * PwdEmpty: "Please type a password, and then retype it to confirm.",
24225 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24226 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24227 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24228 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24229 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
24230 * LNInPwd: "Your password can't contain your last name. Please type a different password."
24240 * @cfg {String/Object} Label for the strength meter (defaults to
24241 * 'Password strength:')
24246 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
24247 * ['Weak', 'Medium', 'Strong'])
24250 pwdStrengths: false,
24263 initEvents: function ()
24265 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
24267 if (this.el.is('input[type=password]') && Roo.isSafari) {
24268 this.el.on('keydown', this.SafariOnKeyDown, this);
24271 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
24274 onRender: function (ct, position)
24276 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
24277 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
24278 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
24280 this.trigger.createChild({
24285 cls: 'roo-password-meter-grey col-xs-12',
24288 //width: this.meterWidth + 'px'
24292 cls: 'roo-password-meter-text'
24298 if (this.hideTrigger) {
24299 this.trigger.setDisplayed(false);
24301 this.setSize(this.width || '', this.height || '');
24304 onDestroy: function ()
24306 if (this.trigger) {
24307 this.trigger.removeAllListeners();
24308 this.trigger.remove();
24311 this.wrap.remove();
24313 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
24316 checkStrength: function ()
24318 var pwd = this.inputEl().getValue();
24319 if (pwd == this._lastPwd) {
24324 if (this.ClientSideStrongPassword(pwd)) {
24326 } else if (this.ClientSideMediumPassword(pwd)) {
24328 } else if (this.ClientSideWeakPassword(pwd)) {
24334 Roo.log('strength1: ' + strength);
24336 //var pm = this.trigger.child('div/div/div').dom;
24337 var pm = this.trigger.child('div/div');
24338 pm.removeClass(this.meterClass);
24339 pm.addClass(this.meterClass[strength]);
24342 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24344 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24346 this._lastPwd = pwd;
24350 Roo.bootstrap.SecurePass.superclass.reset.call(this);
24352 this._lastPwd = '';
24354 var pm = this.trigger.child('div/div');
24355 pm.removeClass(this.meterClass);
24356 pm.addClass('roo-password-meter-grey');
24359 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24362 this.inputEl().dom.type='password';
24365 validateValue: function (value)
24367 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
24370 if (value.length == 0) {
24371 if (this.allowBlank) {
24372 this.clearInvalid();
24376 this.markInvalid(this.errors.PwdEmpty);
24377 this.errorMsg = this.errors.PwdEmpty;
24385 if (!value.match(/[\x21-\x7e]+/)) {
24386 this.markInvalid(this.errors.PwdBadChar);
24387 this.errorMsg = this.errors.PwdBadChar;
24390 if (value.length < 6) {
24391 this.markInvalid(this.errors.PwdShort);
24392 this.errorMsg = this.errors.PwdShort;
24395 if (value.length > 16) {
24396 this.markInvalid(this.errors.PwdLong);
24397 this.errorMsg = this.errors.PwdLong;
24401 if (this.ClientSideStrongPassword(value)) {
24403 } else if (this.ClientSideMediumPassword(value)) {
24405 } else if (this.ClientSideWeakPassword(value)) {
24412 if (strength < 2) {
24413 //this.markInvalid(this.errors.TooWeak);
24414 this.errorMsg = this.errors.TooWeak;
24419 console.log('strength2: ' + strength);
24421 //var pm = this.trigger.child('div/div/div').dom;
24423 var pm = this.trigger.child('div/div');
24424 pm.removeClass(this.meterClass);
24425 pm.addClass(this.meterClass[strength]);
24427 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24429 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24431 this.errorMsg = '';
24435 CharacterSetChecks: function (type)
24438 this.fResult = false;
24441 isctype: function (character, type)
24444 case this.kCapitalLetter:
24445 if (character >= 'A' && character <= 'Z') {
24450 case this.kSmallLetter:
24451 if (character >= 'a' && character <= 'z') {
24457 if (character >= '0' && character <= '9') {
24462 case this.kPunctuation:
24463 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24474 IsLongEnough: function (pwd, size)
24476 return !(pwd == null || isNaN(size) || pwd.length < size);
24479 SpansEnoughCharacterSets: function (word, nb)
24481 if (!this.IsLongEnough(word, nb))
24486 var characterSetChecks = new Array(
24487 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24488 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24491 for (var index = 0; index < word.length; ++index) {
24492 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24493 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24494 characterSetChecks[nCharSet].fResult = true;
24501 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24502 if (characterSetChecks[nCharSet].fResult) {
24507 if (nCharSets < nb) {
24513 ClientSideStrongPassword: function (pwd)
24515 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24518 ClientSideMediumPassword: function (pwd)
24520 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24523 ClientSideWeakPassword: function (pwd)
24525 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24528 })//<script type="text/javascript">
24531 * Based Ext JS Library 1.1.1
24532 * Copyright(c) 2006-2007, Ext JS, LLC.
24538 * @class Roo.HtmlEditorCore
24539 * @extends Roo.Component
24540 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24542 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24545 Roo.HtmlEditorCore = function(config){
24548 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24553 * @event initialize
24554 * Fires when the editor is fully initialized (including the iframe)
24555 * @param {Roo.HtmlEditorCore} this
24560 * Fires when the editor is first receives the focus. Any insertion must wait
24561 * until after this event.
24562 * @param {Roo.HtmlEditorCore} this
24566 * @event beforesync
24567 * Fires before the textarea is updated with content from the editor iframe. Return false
24568 * to cancel the sync.
24569 * @param {Roo.HtmlEditorCore} this
24570 * @param {String} html
24574 * @event beforepush
24575 * Fires before the iframe editor is updated with content from the textarea. Return false
24576 * to cancel the push.
24577 * @param {Roo.HtmlEditorCore} this
24578 * @param {String} html
24583 * Fires when the textarea is updated with content from the editor iframe.
24584 * @param {Roo.HtmlEditorCore} this
24585 * @param {String} html
24590 * Fires when the iframe editor is updated with content from the textarea.
24591 * @param {Roo.HtmlEditorCore} this
24592 * @param {String} html
24597 * @event editorevent
24598 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24599 * @param {Roo.HtmlEditorCore} this
24605 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24607 // defaults : white / black...
24608 this.applyBlacklists();
24615 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
24619 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
24625 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
24630 * @cfg {Number} height (in pixels)
24634 * @cfg {Number} width (in pixels)
24639 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24642 stylesheets: false,
24647 // private properties
24648 validationEvent : false,
24650 initialized : false,
24652 sourceEditMode : false,
24653 onFocus : Roo.emptyFn,
24655 hideMode:'offsets',
24659 // blacklist + whitelisted elements..
24666 * Protected method that will not generally be called directly. It
24667 * is called when the editor initializes the iframe with HTML contents. Override this method if you
24668 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24670 getDocMarkup : function(){
24674 // inherit styels from page...??
24675 if (this.stylesheets === false) {
24677 Roo.get(document.head).select('style').each(function(node) {
24678 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24681 Roo.get(document.head).select('link').each(function(node) {
24682 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24685 } else if (!this.stylesheets.length) {
24687 st = '<style type="text/css">' +
24688 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24691 for (var i in this.stylesheets) {
24692 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24697 st += '<style type="text/css">' +
24698 'IMG { cursor: pointer } ' +
24701 var cls = 'roo-htmleditor-body';
24703 if(this.bodyCls.length){
24704 cls += ' ' + this.bodyCls;
24707 return '<html><head>' + st +
24708 //<style type="text/css">' +
24709 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24711 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
24715 onRender : function(ct, position)
24718 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24719 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24722 this.el.dom.style.border = '0 none';
24723 this.el.dom.setAttribute('tabIndex', -1);
24724 this.el.addClass('x-hidden hide');
24728 if(Roo.isIE){ // fix IE 1px bogus margin
24729 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24733 this.frameId = Roo.id();
24737 var iframe = this.owner.wrap.createChild({
24739 cls: 'form-control', // bootstrap..
24741 name: this.frameId,
24742 frameBorder : 'no',
24743 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
24748 this.iframe = iframe.dom;
24750 this.assignDocWin();
24752 this.doc.designMode = 'on';
24755 this.doc.write(this.getDocMarkup());
24759 var task = { // must defer to wait for browser to be ready
24761 //console.log("run task?" + this.doc.readyState);
24762 this.assignDocWin();
24763 if(this.doc.body || this.doc.readyState == 'complete'){
24765 this.doc.designMode="on";
24769 Roo.TaskMgr.stop(task);
24770 this.initEditor.defer(10, this);
24777 Roo.TaskMgr.start(task);
24782 onResize : function(w, h)
24784 Roo.log('resize: ' +w + ',' + h );
24785 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24789 if(typeof w == 'number'){
24791 this.iframe.style.width = w + 'px';
24793 if(typeof h == 'number'){
24795 this.iframe.style.height = h + 'px';
24797 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24804 * Toggles the editor between standard and source edit mode.
24805 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24807 toggleSourceEdit : function(sourceEditMode){
24809 this.sourceEditMode = sourceEditMode === true;
24811 if(this.sourceEditMode){
24813 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
24816 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24817 //this.iframe.className = '';
24820 //this.setSize(this.owner.wrap.getSize());
24821 //this.fireEvent('editmodechange', this, this.sourceEditMode);
24828 * Protected method that will not generally be called directly. If you need/want
24829 * custom HTML cleanup, this is the method you should override.
24830 * @param {String} html The HTML to be cleaned
24831 * return {String} The cleaned HTML
24833 cleanHtml : function(html){
24834 html = String(html);
24835 if(html.length > 5){
24836 if(Roo.isSafari){ // strip safari nonsense
24837 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24840 if(html == ' '){
24847 * HTML Editor -> Textarea
24848 * Protected method that will not generally be called directly. Syncs the contents
24849 * of the editor iframe with the textarea.
24851 syncValue : function(){
24852 if(this.initialized){
24853 var bd = (this.doc.body || this.doc.documentElement);
24854 //this.cleanUpPaste(); -- this is done else where and causes havoc..
24855 var html = bd.innerHTML;
24857 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24858 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24860 html = '<div style="'+m[0]+'">' + html + '</div>';
24863 html = this.cleanHtml(html);
24864 // fix up the special chars.. normaly like back quotes in word...
24865 // however we do not want to do this with chinese..
24866 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24868 var cc = match.charCodeAt();
24870 // Get the character value, handling surrogate pairs
24871 if (match.length == 2) {
24872 // It's a surrogate pair, calculate the Unicode code point
24873 var high = match.charCodeAt(0) - 0xD800;
24874 var low = match.charCodeAt(1) - 0xDC00;
24875 cc = (high * 0x400) + low + 0x10000;
24877 (cc >= 0x4E00 && cc < 0xA000 ) ||
24878 (cc >= 0x3400 && cc < 0x4E00 ) ||
24879 (cc >= 0xf900 && cc < 0xfb00 )
24884 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24885 return "&#" + cc + ";";
24892 if(this.owner.fireEvent('beforesync', this, html) !== false){
24893 this.el.dom.value = html;
24894 this.owner.fireEvent('sync', this, html);
24900 * Protected method that will not generally be called directly. Pushes the value of the textarea
24901 * into the iframe editor.
24903 pushValue : function(){
24904 if(this.initialized){
24905 var v = this.el.dom.value.trim();
24907 // if(v.length < 1){
24911 if(this.owner.fireEvent('beforepush', this, v) !== false){
24912 var d = (this.doc.body || this.doc.documentElement);
24914 this.cleanUpPaste();
24915 this.el.dom.value = d.innerHTML;
24916 this.owner.fireEvent('push', this, v);
24922 deferFocus : function(){
24923 this.focus.defer(10, this);
24927 focus : function(){
24928 if(this.win && !this.sourceEditMode){
24935 assignDocWin: function()
24937 var iframe = this.iframe;
24940 this.doc = iframe.contentWindow.document;
24941 this.win = iframe.contentWindow;
24943 // if (!Roo.get(this.frameId)) {
24946 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24947 // this.win = Roo.get(this.frameId).dom.contentWindow;
24949 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24953 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24954 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24959 initEditor : function(){
24960 //console.log("INIT EDITOR");
24961 this.assignDocWin();
24965 this.doc.designMode="on";
24967 this.doc.write(this.getDocMarkup());
24970 var dbody = (this.doc.body || this.doc.documentElement);
24971 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24972 // this copies styles from the containing element into thsi one..
24973 // not sure why we need all of this..
24974 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24976 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24977 //ss['background-attachment'] = 'fixed'; // w3c
24978 dbody.bgProperties = 'fixed'; // ie
24979 //Roo.DomHelper.applyStyles(dbody, ss);
24980 Roo.EventManager.on(this.doc, {
24981 //'mousedown': this.onEditorEvent,
24982 'mouseup': this.onEditorEvent,
24983 'dblclick': this.onEditorEvent,
24984 'click': this.onEditorEvent,
24985 'keyup': this.onEditorEvent,
24990 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24992 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24993 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24995 this.initialized = true;
24997 this.owner.fireEvent('initialize', this);
25002 onDestroy : function(){
25008 //for (var i =0; i < this.toolbars.length;i++) {
25009 // // fixme - ask toolbars for heights?
25010 // this.toolbars[i].onDestroy();
25013 //this.wrap.dom.innerHTML = '';
25014 //this.wrap.remove();
25019 onFirstFocus : function(){
25021 this.assignDocWin();
25024 this.activated = true;
25027 if(Roo.isGecko){ // prevent silly gecko errors
25029 var s = this.win.getSelection();
25030 if(!s.focusNode || s.focusNode.nodeType != 3){
25031 var r = s.getRangeAt(0);
25032 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25037 this.execCmd('useCSS', true);
25038 this.execCmd('styleWithCSS', false);
25041 this.owner.fireEvent('activate', this);
25045 adjustFont: function(btn){
25046 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25047 //if(Roo.isSafari){ // safari
25050 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25051 if(Roo.isSafari){ // safari
25052 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25053 v = (v < 10) ? 10 : v;
25054 v = (v > 48) ? 48 : v;
25055 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25060 v = Math.max(1, v+adjust);
25062 this.execCmd('FontSize', v );
25065 onEditorEvent : function(e)
25067 this.owner.fireEvent('editorevent', this, e);
25068 // this.updateToolbar();
25069 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25072 insertTag : function(tg)
25074 // could be a bit smarter... -> wrap the current selected tRoo..
25075 if (tg.toLowerCase() == 'span' ||
25076 tg.toLowerCase() == 'code' ||
25077 tg.toLowerCase() == 'sup' ||
25078 tg.toLowerCase() == 'sub'
25081 range = this.createRange(this.getSelection());
25082 var wrappingNode = this.doc.createElement(tg.toLowerCase());
25083 wrappingNode.appendChild(range.extractContents());
25084 range.insertNode(wrappingNode);
25091 this.execCmd("formatblock", tg);
25095 insertText : function(txt)
25099 var range = this.createRange();
25100 range.deleteContents();
25101 //alert(Sender.getAttribute('label'));
25103 range.insertNode(this.doc.createTextNode(txt));
25109 * Executes a Midas editor command on the editor document and performs necessary focus and
25110 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25111 * @param {String} cmd The Midas command
25112 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25114 relayCmd : function(cmd, value){
25116 this.execCmd(cmd, value);
25117 this.owner.fireEvent('editorevent', this);
25118 //this.updateToolbar();
25119 this.owner.deferFocus();
25123 * Executes a Midas editor command directly on the editor document.
25124 * For visual commands, you should use {@link #relayCmd} instead.
25125 * <b>This should only be called after the editor is initialized.</b>
25126 * @param {String} cmd The Midas command
25127 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25129 execCmd : function(cmd, value){
25130 this.doc.execCommand(cmd, false, value === undefined ? null : value);
25137 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25139 * @param {String} text | dom node..
25141 insertAtCursor : function(text)
25144 if(!this.activated){
25150 var r = this.doc.selection.createRange();
25161 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25165 // from jquery ui (MIT licenced)
25167 var win = this.win;
25169 if (win.getSelection && win.getSelection().getRangeAt) {
25170 range = win.getSelection().getRangeAt(0);
25171 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25172 range.insertNode(node);
25173 } else if (win.document.selection && win.document.selection.createRange) {
25174 // no firefox support
25175 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25176 win.document.selection.createRange().pasteHTML(txt);
25178 // no firefox support
25179 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25180 this.execCmd('InsertHTML', txt);
25189 mozKeyPress : function(e){
25191 var c = e.getCharCode(), cmd;
25194 c = String.fromCharCode(c).toLowerCase();
25208 this.cleanUpPaste.defer(100, this);
25216 e.preventDefault();
25224 fixKeys : function(){ // load time branching for fastest keydown performance
25226 return function(e){
25227 var k = e.getKey(), r;
25230 r = this.doc.selection.createRange();
25233 r.pasteHTML('    ');
25240 r = this.doc.selection.createRange();
25242 var target = r.parentElement();
25243 if(!target || target.tagName.toLowerCase() != 'li'){
25245 r.pasteHTML('<br />');
25251 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25252 this.cleanUpPaste.defer(100, this);
25258 }else if(Roo.isOpera){
25259 return function(e){
25260 var k = e.getKey();
25264 this.execCmd('InsertHTML','    ');
25267 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25268 this.cleanUpPaste.defer(100, this);
25273 }else if(Roo.isSafari){
25274 return function(e){
25275 var k = e.getKey();
25279 this.execCmd('InsertText','\t');
25283 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25284 this.cleanUpPaste.defer(100, this);
25292 getAllAncestors: function()
25294 var p = this.getSelectedNode();
25297 a.push(p); // push blank onto stack..
25298 p = this.getParentElement();
25302 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25306 a.push(this.doc.body);
25310 lastSelNode : false,
25313 getSelection : function()
25315 this.assignDocWin();
25316 return Roo.isIE ? this.doc.selection : this.win.getSelection();
25319 getSelectedNode: function()
25321 // this may only work on Gecko!!!
25323 // should we cache this!!!!
25328 var range = this.createRange(this.getSelection()).cloneRange();
25331 var parent = range.parentElement();
25333 var testRange = range.duplicate();
25334 testRange.moveToElementText(parent);
25335 if (testRange.inRange(range)) {
25338 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25341 parent = parent.parentElement;
25346 // is ancestor a text element.
25347 var ac = range.commonAncestorContainer;
25348 if (ac.nodeType == 3) {
25349 ac = ac.parentNode;
25352 var ar = ac.childNodes;
25355 var other_nodes = [];
25356 var has_other_nodes = false;
25357 for (var i=0;i<ar.length;i++) {
25358 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
25361 // fullly contained node.
25363 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25368 // probably selected..
25369 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25370 other_nodes.push(ar[i]);
25374 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
25379 has_other_nodes = true;
25381 if (!nodes.length && other_nodes.length) {
25382 nodes= other_nodes;
25384 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25390 createRange: function(sel)
25392 // this has strange effects when using with
25393 // top toolbar - not sure if it's a great idea.
25394 //this.editor.contentWindow.focus();
25395 if (typeof sel != "undefined") {
25397 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25399 return this.doc.createRange();
25402 return this.doc.createRange();
25405 getParentElement: function()
25408 this.assignDocWin();
25409 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25411 var range = this.createRange(sel);
25414 var p = range.commonAncestorContainer;
25415 while (p.nodeType == 3) { // text node
25426 * Range intersection.. the hard stuff...
25430 * [ -- selected range --- ]
25434 * if end is before start or hits it. fail.
25435 * if start is after end or hits it fail.
25437 * if either hits (but other is outside. - then it's not
25443 // @see http://www.thismuchiknow.co.uk/?p=64.
25444 rangeIntersectsNode : function(range, node)
25446 var nodeRange = node.ownerDocument.createRange();
25448 nodeRange.selectNode(node);
25450 nodeRange.selectNodeContents(node);
25453 var rangeStartRange = range.cloneRange();
25454 rangeStartRange.collapse(true);
25456 var rangeEndRange = range.cloneRange();
25457 rangeEndRange.collapse(false);
25459 var nodeStartRange = nodeRange.cloneRange();
25460 nodeStartRange.collapse(true);
25462 var nodeEndRange = nodeRange.cloneRange();
25463 nodeEndRange.collapse(false);
25465 return rangeStartRange.compareBoundaryPoints(
25466 Range.START_TO_START, nodeEndRange) == -1 &&
25467 rangeEndRange.compareBoundaryPoints(
25468 Range.START_TO_START, nodeStartRange) == 1;
25472 rangeCompareNode : function(range, node)
25474 var nodeRange = node.ownerDocument.createRange();
25476 nodeRange.selectNode(node);
25478 nodeRange.selectNodeContents(node);
25482 range.collapse(true);
25484 nodeRange.collapse(true);
25486 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25487 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
25489 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25491 var nodeIsBefore = ss == 1;
25492 var nodeIsAfter = ee == -1;
25494 if (nodeIsBefore && nodeIsAfter) {
25497 if (!nodeIsBefore && nodeIsAfter) {
25498 return 1; //right trailed.
25501 if (nodeIsBefore && !nodeIsAfter) {
25502 return 2; // left trailed.
25508 // private? - in a new class?
25509 cleanUpPaste : function()
25511 // cleans up the whole document..
25512 Roo.log('cleanuppaste');
25514 this.cleanUpChildren(this.doc.body);
25515 var clean = this.cleanWordChars(this.doc.body.innerHTML);
25516 if (clean != this.doc.body.innerHTML) {
25517 this.doc.body.innerHTML = clean;
25522 cleanWordChars : function(input) {// change the chars to hex code
25523 var he = Roo.HtmlEditorCore;
25525 var output = input;
25526 Roo.each(he.swapCodes, function(sw) {
25527 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25529 output = output.replace(swapper, sw[1]);
25536 cleanUpChildren : function (n)
25538 if (!n.childNodes.length) {
25541 for (var i = n.childNodes.length-1; i > -1 ; i--) {
25542 this.cleanUpChild(n.childNodes[i]);
25549 cleanUpChild : function (node)
25552 //console.log(node);
25553 if (node.nodeName == "#text") {
25554 // clean up silly Windows -- stuff?
25557 if (node.nodeName == "#comment") {
25558 node.parentNode.removeChild(node);
25559 // clean up silly Windows -- stuff?
25562 var lcname = node.tagName.toLowerCase();
25563 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25564 // whitelist of tags..
25566 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25568 node.parentNode.removeChild(node);
25573 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25575 // spans with no attributes - just remove them..
25576 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
25577 remove_keep_children = true;
25580 // remove <a name=....> as rendering on yahoo mailer is borked with this.
25581 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25583 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25584 // remove_keep_children = true;
25587 if (remove_keep_children) {
25588 this.cleanUpChildren(node);
25589 // inserts everything just before this node...
25590 while (node.childNodes.length) {
25591 var cn = node.childNodes[0];
25592 node.removeChild(cn);
25593 node.parentNode.insertBefore(cn, node);
25595 node.parentNode.removeChild(node);
25599 if (!node.attributes || !node.attributes.length) {
25604 this.cleanUpChildren(node);
25608 function cleanAttr(n,v)
25611 if (v.match(/^\./) || v.match(/^\//)) {
25614 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25617 if (v.match(/^#/)) {
25620 if (v.match(/^\{/)) { // allow template editing.
25623 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25624 node.removeAttribute(n);
25628 var cwhite = this.cwhite;
25629 var cblack = this.cblack;
25631 function cleanStyle(n,v)
25633 if (v.match(/expression/)) { //XSS?? should we even bother..
25634 node.removeAttribute(n);
25638 var parts = v.split(/;/);
25641 Roo.each(parts, function(p) {
25642 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25646 var l = p.split(':').shift().replace(/\s+/g,'');
25647 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25649 if ( cwhite.length && cblack.indexOf(l) > -1) {
25650 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25651 //node.removeAttribute(n);
25655 // only allow 'c whitelisted system attributes'
25656 if ( cwhite.length && cwhite.indexOf(l) < 0) {
25657 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25658 //node.removeAttribute(n);
25668 if (clean.length) {
25669 node.setAttribute(n, clean.join(';'));
25671 node.removeAttribute(n);
25677 for (var i = node.attributes.length-1; i > -1 ; i--) {
25678 var a = node.attributes[i];
25681 if (a.name.toLowerCase().substr(0,2)=='on') {
25682 node.removeAttribute(a.name);
25685 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25686 node.removeAttribute(a.name);
25689 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25690 cleanAttr(a.name,a.value); // fixme..
25693 if (a.name == 'style') {
25694 cleanStyle(a.name,a.value);
25697 /// clean up MS crap..
25698 // tecnically this should be a list of valid class'es..
25701 if (a.name == 'class') {
25702 if (a.value.match(/^Mso/)) {
25703 node.removeAttribute('class');
25706 if (a.value.match(/^body$/)) {
25707 node.removeAttribute('class');
25718 this.cleanUpChildren(node);
25724 * Clean up MS wordisms...
25726 cleanWord : function(node)
25729 this.cleanWord(this.doc.body);
25734 node.nodeName == 'SPAN' &&
25735 !node.hasAttributes() &&
25736 node.childNodes.length == 1 &&
25737 node.firstChild.nodeName == "#text"
25739 var textNode = node.firstChild;
25740 node.removeChild(textNode);
25741 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25742 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25744 node.parentNode.insertBefore(textNode, node);
25745 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25746 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25748 node.parentNode.removeChild(node);
25751 if (node.nodeName == "#text") {
25752 // clean up silly Windows -- stuff?
25755 if (node.nodeName == "#comment") {
25756 node.parentNode.removeChild(node);
25757 // clean up silly Windows -- stuff?
25761 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25762 node.parentNode.removeChild(node);
25765 //Roo.log(node.tagName);
25766 // remove - but keep children..
25767 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25768 //Roo.log('-- removed');
25769 while (node.childNodes.length) {
25770 var cn = node.childNodes[0];
25771 node.removeChild(cn);
25772 node.parentNode.insertBefore(cn, node);
25773 // move node to parent - and clean it..
25774 this.cleanWord(cn);
25776 node.parentNode.removeChild(node);
25777 /// no need to iterate chidlren = it's got none..
25778 //this.iterateChildren(node, this.cleanWord);
25782 if (node.className.length) {
25784 var cn = node.className.split(/\W+/);
25786 Roo.each(cn, function(cls) {
25787 if (cls.match(/Mso[a-zA-Z]+/)) {
25792 node.className = cna.length ? cna.join(' ') : '';
25794 node.removeAttribute("class");
25798 if (node.hasAttribute("lang")) {
25799 node.removeAttribute("lang");
25802 if (node.hasAttribute("style")) {
25804 var styles = node.getAttribute("style").split(";");
25806 Roo.each(styles, function(s) {
25807 if (!s.match(/:/)) {
25810 var kv = s.split(":");
25811 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25814 // what ever is left... we allow.
25817 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25818 if (!nstyle.length) {
25819 node.removeAttribute('style');
25822 this.iterateChildren(node, this.cleanWord);
25828 * iterateChildren of a Node, calling fn each time, using this as the scole..
25829 * @param {DomNode} node node to iterate children of.
25830 * @param {Function} fn method of this class to call on each item.
25832 iterateChildren : function(node, fn)
25834 if (!node.childNodes.length) {
25837 for (var i = node.childNodes.length-1; i > -1 ; i--) {
25838 fn.call(this, node.childNodes[i])
25844 * cleanTableWidths.
25846 * Quite often pasting from word etc.. results in tables with column and widths.
25847 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25850 cleanTableWidths : function(node)
25855 this.cleanTableWidths(this.doc.body);
25860 if (node.nodeName == "#text" || node.nodeName == "#comment") {
25863 Roo.log(node.tagName);
25864 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25865 this.iterateChildren(node, this.cleanTableWidths);
25868 if (node.hasAttribute('width')) {
25869 node.removeAttribute('width');
25873 if (node.hasAttribute("style")) {
25876 var styles = node.getAttribute("style").split(";");
25878 Roo.each(styles, function(s) {
25879 if (!s.match(/:/)) {
25882 var kv = s.split(":");
25883 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25886 // what ever is left... we allow.
25889 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25890 if (!nstyle.length) {
25891 node.removeAttribute('style');
25895 this.iterateChildren(node, this.cleanTableWidths);
25903 domToHTML : function(currentElement, depth, nopadtext) {
25905 depth = depth || 0;
25906 nopadtext = nopadtext || false;
25908 if (!currentElement) {
25909 return this.domToHTML(this.doc.body);
25912 //Roo.log(currentElement);
25914 var allText = false;
25915 var nodeName = currentElement.nodeName;
25916 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25918 if (nodeName == '#text') {
25920 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25925 if (nodeName != 'BODY') {
25928 // Prints the node tagName, such as <A>, <IMG>, etc
25931 for(i = 0; i < currentElement.attributes.length;i++) {
25933 var aname = currentElement.attributes.item(i).name;
25934 if (!currentElement.attributes.item(i).value.length) {
25937 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25940 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25949 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25952 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25957 // Traverse the tree
25959 var currentElementChild = currentElement.childNodes.item(i);
25960 var allText = true;
25961 var innerHTML = '';
25963 while (currentElementChild) {
25964 // Formatting code (indent the tree so it looks nice on the screen)
25965 var nopad = nopadtext;
25966 if (lastnode == 'SPAN') {
25970 if (currentElementChild.nodeName == '#text') {
25971 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25972 toadd = nopadtext ? toadd : toadd.trim();
25973 if (!nopad && toadd.length > 80) {
25974 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
25976 innerHTML += toadd;
25979 currentElementChild = currentElement.childNodes.item(i);
25985 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
25987 // Recursively traverse the tree structure of the child node
25988 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
25989 lastnode = currentElementChild.nodeName;
25991 currentElementChild=currentElement.childNodes.item(i);
25997 // The remaining code is mostly for formatting the tree
25998 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
26003 ret+= "</"+tagName+">";
26009 applyBlacklists : function()
26011 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
26012 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
26016 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
26017 if (b.indexOf(tag) > -1) {
26020 this.white.push(tag);
26024 Roo.each(w, function(tag) {
26025 if (b.indexOf(tag) > -1) {
26028 if (this.white.indexOf(tag) > -1) {
26031 this.white.push(tag);
26036 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
26037 if (w.indexOf(tag) > -1) {
26040 this.black.push(tag);
26044 Roo.each(b, function(tag) {
26045 if (w.indexOf(tag) > -1) {
26048 if (this.black.indexOf(tag) > -1) {
26051 this.black.push(tag);
26056 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
26057 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
26061 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
26062 if (b.indexOf(tag) > -1) {
26065 this.cwhite.push(tag);
26069 Roo.each(w, function(tag) {
26070 if (b.indexOf(tag) > -1) {
26073 if (this.cwhite.indexOf(tag) > -1) {
26076 this.cwhite.push(tag);
26081 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
26082 if (w.indexOf(tag) > -1) {
26085 this.cblack.push(tag);
26089 Roo.each(b, function(tag) {
26090 if (w.indexOf(tag) > -1) {
26093 if (this.cblack.indexOf(tag) > -1) {
26096 this.cblack.push(tag);
26101 setStylesheets : function(stylesheets)
26103 if(typeof(stylesheets) == 'string'){
26104 Roo.get(this.iframe.contentDocument.head).createChild({
26106 rel : 'stylesheet',
26115 Roo.each(stylesheets, function(s) {
26120 Roo.get(_this.iframe.contentDocument.head).createChild({
26122 rel : 'stylesheet',
26131 removeStylesheets : function()
26135 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
26140 setStyle : function(style)
26142 Roo.get(this.iframe.contentDocument.head).createChild({
26151 // hide stuff that is not compatible
26165 * @event specialkey
26169 * @cfg {String} fieldClass @hide
26172 * @cfg {String} focusClass @hide
26175 * @cfg {String} autoCreate @hide
26178 * @cfg {String} inputType @hide
26181 * @cfg {String} invalidClass @hide
26184 * @cfg {String} invalidText @hide
26187 * @cfg {String} msgFx @hide
26190 * @cfg {String} validateOnBlur @hide
26194 Roo.HtmlEditorCore.white = [
26195 'area', 'br', 'img', 'input', 'hr', 'wbr',
26197 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
26198 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
26199 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
26200 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
26201 'table', 'ul', 'xmp',
26203 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
26206 'dir', 'menu', 'ol', 'ul', 'dl',
26212 Roo.HtmlEditorCore.black = [
26213 // 'embed', 'object', // enable - backend responsiblity to clean thiese
26215 'base', 'basefont', 'bgsound', 'blink', 'body',
26216 'frame', 'frameset', 'head', 'html', 'ilayer',
26217 'iframe', 'layer', 'link', 'meta', 'object',
26218 'script', 'style' ,'title', 'xml' // clean later..
26220 Roo.HtmlEditorCore.clean = [
26221 'script', 'style', 'title', 'xml'
26223 Roo.HtmlEditorCore.remove = [
26228 Roo.HtmlEditorCore.ablack = [
26232 Roo.HtmlEditorCore.aclean = [
26233 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
26237 Roo.HtmlEditorCore.pwhite= [
26238 'http', 'https', 'mailto'
26241 // white listed style attributes.
26242 Roo.HtmlEditorCore.cwhite= [
26243 // 'text-align', /// default is to allow most things..
26249 // black listed style attributes.
26250 Roo.HtmlEditorCore.cblack= [
26251 // 'font-size' -- this can be set by the project
26255 Roo.HtmlEditorCore.swapCodes =[
26256 [ 8211, "–" ],
26257 [ 8212, "—" ],
26274 * @class Roo.bootstrap.HtmlEditor
26275 * @extends Roo.bootstrap.TextArea
26276 * Bootstrap HtmlEditor class
26279 * Create a new HtmlEditor
26280 * @param {Object} config The config object
26283 Roo.bootstrap.HtmlEditor = function(config){
26284 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
26285 if (!this.toolbars) {
26286 this.toolbars = [];
26289 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26292 * @event initialize
26293 * Fires when the editor is fully initialized (including the iframe)
26294 * @param {HtmlEditor} this
26299 * Fires when the editor is first receives the focus. Any insertion must wait
26300 * until after this event.
26301 * @param {HtmlEditor} this
26305 * @event beforesync
26306 * Fires before the textarea is updated with content from the editor iframe. Return false
26307 * to cancel the sync.
26308 * @param {HtmlEditor} this
26309 * @param {String} html
26313 * @event beforepush
26314 * Fires before the iframe editor is updated with content from the textarea. Return false
26315 * to cancel the push.
26316 * @param {HtmlEditor} this
26317 * @param {String} html
26322 * Fires when the textarea is updated with content from the editor iframe.
26323 * @param {HtmlEditor} this
26324 * @param {String} html
26329 * Fires when the iframe editor is updated with content from the textarea.
26330 * @param {HtmlEditor} this
26331 * @param {String} html
26335 * @event editmodechange
26336 * Fires when the editor switches edit modes
26337 * @param {HtmlEditor} this
26338 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26340 editmodechange: true,
26342 * @event editorevent
26343 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26344 * @param {HtmlEditor} this
26348 * @event firstfocus
26349 * Fires when on first focus - needed by toolbars..
26350 * @param {HtmlEditor} this
26355 * Auto save the htmlEditor value as a file into Events
26356 * @param {HtmlEditor} this
26360 * @event savedpreview
26361 * preview the saved version of htmlEditor
26362 * @param {HtmlEditor} this
26369 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
26373 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26378 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
26383 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
26388 * @cfg {Number} height (in pixels)
26392 * @cfg {Number} width (in pixels)
26397 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26400 stylesheets: false,
26405 // private properties
26406 validationEvent : false,
26408 initialized : false,
26411 onFocus : Roo.emptyFn,
26413 hideMode:'offsets',
26415 tbContainer : false,
26419 toolbarContainer :function() {
26420 return this.wrap.select('.x-html-editor-tb',true).first();
26424 * Protected method that will not generally be called directly. It
26425 * is called when the editor creates its toolbar. Override this method if you need to
26426 * add custom toolbar buttons.
26427 * @param {HtmlEditor} editor
26429 createToolbar : function(){
26430 Roo.log('renewing');
26431 Roo.log("create toolbars");
26433 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26434 this.toolbars[0].render(this.toolbarContainer());
26438 // if (!editor.toolbars || !editor.toolbars.length) {
26439 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26442 // for (var i =0 ; i < editor.toolbars.length;i++) {
26443 // editor.toolbars[i] = Roo.factory(
26444 // typeof(editor.toolbars[i]) == 'string' ?
26445 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
26446 // Roo.bootstrap.HtmlEditor);
26447 // editor.toolbars[i].init(editor);
26453 onRender : function(ct, position)
26455 // Roo.log("Call onRender: " + this.xtype);
26457 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26459 this.wrap = this.inputEl().wrap({
26460 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26463 this.editorcore.onRender(ct, position);
26465 if (this.resizable) {
26466 this.resizeEl = new Roo.Resizable(this.wrap, {
26470 minHeight : this.height,
26471 height: this.height,
26472 handles : this.resizable,
26475 resize : function(r, w, h) {
26476 _t.onResize(w,h); // -something
26482 this.createToolbar(this);
26485 if(!this.width && this.resizable){
26486 this.setSize(this.wrap.getSize());
26488 if (this.resizeEl) {
26489 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26490 // should trigger onReize..
26496 onResize : function(w, h)
26498 Roo.log('resize: ' +w + ',' + h );
26499 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26503 if(this.inputEl() ){
26504 if(typeof w == 'number'){
26505 var aw = w - this.wrap.getFrameWidth('lr');
26506 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26509 if(typeof h == 'number'){
26510 var tbh = -11; // fixme it needs to tool bar size!
26511 for (var i =0; i < this.toolbars.length;i++) {
26512 // fixme - ask toolbars for heights?
26513 tbh += this.toolbars[i].el.getHeight();
26514 //if (this.toolbars[i].footer) {
26515 // tbh += this.toolbars[i].footer.el.getHeight();
26523 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26524 ah -= 5; // knock a few pixes off for look..
26525 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26529 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26530 this.editorcore.onResize(ew,eh);
26535 * Toggles the editor between standard and source edit mode.
26536 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26538 toggleSourceEdit : function(sourceEditMode)
26540 this.editorcore.toggleSourceEdit(sourceEditMode);
26542 if(this.editorcore.sourceEditMode){
26543 Roo.log('editor - showing textarea');
26546 // Roo.log(this.syncValue());
26548 this.inputEl().removeClass(['hide', 'x-hidden']);
26549 this.inputEl().dom.removeAttribute('tabIndex');
26550 this.inputEl().focus();
26552 Roo.log('editor - hiding textarea');
26554 // Roo.log(this.pushValue());
26557 this.inputEl().addClass(['hide', 'x-hidden']);
26558 this.inputEl().dom.setAttribute('tabIndex', -1);
26559 //this.deferFocus();
26562 if(this.resizable){
26563 this.setSize(this.wrap.getSize());
26566 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26569 // private (for BoxComponent)
26570 adjustSize : Roo.BoxComponent.prototype.adjustSize,
26572 // private (for BoxComponent)
26573 getResizeEl : function(){
26577 // private (for BoxComponent)
26578 getPositionEl : function(){
26583 initEvents : function(){
26584 this.originalValue = this.getValue();
26588 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26591 // markInvalid : Roo.emptyFn,
26593 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26596 // clearInvalid : Roo.emptyFn,
26598 setValue : function(v){
26599 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26600 this.editorcore.pushValue();
26605 deferFocus : function(){
26606 this.focus.defer(10, this);
26610 focus : function(){
26611 this.editorcore.focus();
26617 onDestroy : function(){
26623 for (var i =0; i < this.toolbars.length;i++) {
26624 // fixme - ask toolbars for heights?
26625 this.toolbars[i].onDestroy();
26628 this.wrap.dom.innerHTML = '';
26629 this.wrap.remove();
26634 onFirstFocus : function(){
26635 //Roo.log("onFirstFocus");
26636 this.editorcore.onFirstFocus();
26637 for (var i =0; i < this.toolbars.length;i++) {
26638 this.toolbars[i].onFirstFocus();
26644 syncValue : function()
26646 this.editorcore.syncValue();
26649 pushValue : function()
26651 this.editorcore.pushValue();
26655 // hide stuff that is not compatible
26669 * @event specialkey
26673 * @cfg {String} fieldClass @hide
26676 * @cfg {String} focusClass @hide
26679 * @cfg {String} autoCreate @hide
26682 * @cfg {String} inputType @hide
26686 * @cfg {String} invalidText @hide
26689 * @cfg {String} msgFx @hide
26692 * @cfg {String} validateOnBlur @hide
26701 Roo.namespace('Roo.bootstrap.htmleditor');
26703 * @class Roo.bootstrap.HtmlEditorToolbar1
26709 new Roo.bootstrap.HtmlEditor({
26712 new Roo.bootstrap.HtmlEditorToolbar1({
26713 disable : { fonts: 1 , format: 1, ..., ... , ...],
26719 * @cfg {Object} disable List of elements to disable..
26720 * @cfg {Array} btns List of additional buttons.
26724 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26727 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26730 Roo.apply(this, config);
26732 // default disabled, based on 'good practice'..
26733 this.disable = this.disable || {};
26734 Roo.applyIf(this.disable, {
26737 specialElements : true
26739 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26741 this.editor = config.editor;
26742 this.editorcore = config.editor.editorcore;
26744 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26746 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26747 // dont call parent... till later.
26749 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
26754 editorcore : false,
26759 "h1","h2","h3","h4","h5","h6",
26761 "abbr", "acronym", "address", "cite", "samp", "var",
26765 onRender : function(ct, position)
26767 // Roo.log("Call onRender: " + this.xtype);
26769 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26771 this.el.dom.style.marginBottom = '0';
26773 var editorcore = this.editorcore;
26774 var editor= this.editor;
26777 var btn = function(id,cmd , toggle, handler, html){
26779 var event = toggle ? 'toggle' : 'click';
26784 xns: Roo.bootstrap,
26788 enableToggle:toggle !== false,
26790 pressed : toggle ? false : null,
26793 a.listeners[toggle ? 'toggle' : 'click'] = function() {
26794 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
26800 // var cb_box = function...
26805 xns: Roo.bootstrap,
26810 xns: Roo.bootstrap,
26814 Roo.each(this.formats, function(f) {
26815 style.menu.items.push({
26817 xns: Roo.bootstrap,
26818 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26823 editorcore.insertTag(this.tagname);
26830 children.push(style);
26832 btn('bold',false,true);
26833 btn('italic',false,true);
26834 btn('align-left', 'justifyleft',true);
26835 btn('align-center', 'justifycenter',true);
26836 btn('align-right' , 'justifyright',true);
26837 btn('link', false, false, function(btn) {
26838 //Roo.log("create link?");
26839 var url = prompt(this.createLinkText, this.defaultLinkValue);
26840 if(url && url != 'http:/'+'/'){
26841 this.editorcore.relayCmd('createlink', url);
26844 btn('list','insertunorderedlist',true);
26845 btn('pencil', false,true, function(btn){
26847 this.toggleSourceEdit(btn.pressed);
26850 if (this.editor.btns.length > 0) {
26851 for (var i = 0; i<this.editor.btns.length; i++) {
26852 children.push(this.editor.btns[i]);
26860 xns: Roo.bootstrap,
26865 xns: Roo.bootstrap,
26870 cog.menu.items.push({
26872 xns: Roo.bootstrap,
26873 html : Clean styles,
26878 editorcore.insertTag(this.tagname);
26887 this.xtype = 'NavSimplebar';
26889 for(var i=0;i< children.length;i++) {
26891 this.buttons.add(this.addxtypeChild(children[i]));
26895 editor.on('editorevent', this.updateToolbar, this);
26897 onBtnClick : function(id)
26899 this.editorcore.relayCmd(id);
26900 this.editorcore.focus();
26904 * Protected method that will not generally be called directly. It triggers
26905 * a toolbar update by reading the markup state of the current selection in the editor.
26907 updateToolbar: function(){
26909 if(!this.editorcore.activated){
26910 this.editor.onFirstFocus(); // is this neeed?
26914 var btns = this.buttons;
26915 var doc = this.editorcore.doc;
26916 btns.get('bold').setActive(doc.queryCommandState('bold'));
26917 btns.get('italic').setActive(doc.queryCommandState('italic'));
26918 //btns.get('underline').setActive(doc.queryCommandState('underline'));
26920 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26921 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26922 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26924 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26925 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26928 var ans = this.editorcore.getAllAncestors();
26929 if (this.formatCombo) {
26932 var store = this.formatCombo.store;
26933 this.formatCombo.setValue("");
26934 for (var i =0; i < ans.length;i++) {
26935 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26937 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26945 // hides menus... - so this cant be on a menu...
26946 Roo.bootstrap.MenuMgr.hideAll();
26948 Roo.bootstrap.MenuMgr.hideAll();
26949 //this.editorsyncValue();
26951 onFirstFocus: function() {
26952 this.buttons.each(function(item){
26956 toggleSourceEdit : function(sourceEditMode){
26959 if(sourceEditMode){
26960 Roo.log("disabling buttons");
26961 this.buttons.each( function(item){
26962 if(item.cmd != 'pencil'){
26968 Roo.log("enabling buttons");
26969 if(this.editorcore.initialized){
26970 this.buttons.each( function(item){
26976 Roo.log("calling toggole on editor");
26977 // tell the editor that it's been pressed..
26978 this.editor.toggleSourceEdit(sourceEditMode);
26992 * @class Roo.bootstrap.Markdown
26993 * @extends Roo.bootstrap.TextArea
26994 * Bootstrap Showdown editable area
26995 * @cfg {string} content
26998 * Create a new Showdown
27001 Roo.bootstrap.Markdown = function(config){
27002 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
27006 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
27010 initEvents : function()
27013 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
27014 this.markdownEl = this.el.createChild({
27015 cls : 'roo-markdown-area'
27017 this.inputEl().addClass('d-none');
27018 if (this.getValue() == '') {
27019 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
27022 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
27024 this.markdownEl.on('click', this.toggleTextEdit, this);
27025 this.on('blur', this.toggleTextEdit, this);
27026 this.on('specialkey', this.resizeTextArea, this);
27029 toggleTextEdit : function()
27031 var sh = this.markdownEl.getHeight();
27032 this.inputEl().addClass('d-none');
27033 this.markdownEl.addClass('d-none');
27034 if (!this.editing) {
27036 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
27037 this.inputEl().removeClass('d-none');
27038 this.inputEl().focus();
27039 this.editing = true;
27042 // show showdown...
27043 this.updateMarkdown();
27044 this.markdownEl.removeClass('d-none');
27045 this.editing = false;
27048 updateMarkdown : function()
27050 if (this.getValue() == '') {
27051 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
27055 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
27058 resizeTextArea: function () {
27061 Roo.log([sh, this.getValue().split("\n").length * 30]);
27062 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
27064 setValue : function(val)
27066 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
27067 if (!this.editing) {
27068 this.updateMarkdown();
27074 if (!this.editing) {
27075 this.toggleTextEdit();
27083 * @class Roo.bootstrap.Table.AbstractSelectionModel
27084 * @extends Roo.util.Observable
27085 * Abstract base class for grid SelectionModels. It provides the interface that should be
27086 * implemented by descendant classes. This class should not be directly instantiated.
27089 Roo.bootstrap.Table.AbstractSelectionModel = function(){
27090 this.locked = false;
27091 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
27095 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
27096 /** @ignore Called by the grid automatically. Do not call directly. */
27097 init : function(grid){
27103 * Locks the selections.
27106 this.locked = true;
27110 * Unlocks the selections.
27112 unlock : function(){
27113 this.locked = false;
27117 * Returns true if the selections are locked.
27118 * @return {Boolean}
27120 isLocked : function(){
27121 return this.locked;
27125 initEvents : function ()
27131 * @extends Roo.bootstrap.Table.AbstractSelectionModel
27132 * @class Roo.bootstrap.Table.RowSelectionModel
27133 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
27134 * It supports multiple selections and keyboard selection/navigation.
27136 * @param {Object} config
27139 Roo.bootstrap.Table.RowSelectionModel = function(config){
27140 Roo.apply(this, config);
27141 this.selections = new Roo.util.MixedCollection(false, function(o){
27146 this.lastActive = false;
27150 * @event selectionchange
27151 * Fires when the selection changes
27152 * @param {SelectionModel} this
27154 "selectionchange" : true,
27156 * @event afterselectionchange
27157 * Fires after the selection changes (eg. by key press or clicking)
27158 * @param {SelectionModel} this
27160 "afterselectionchange" : true,
27162 * @event beforerowselect
27163 * Fires when a row is selected being selected, return false to cancel.
27164 * @param {SelectionModel} this
27165 * @param {Number} rowIndex The selected index
27166 * @param {Boolean} keepExisting False if other selections will be cleared
27168 "beforerowselect" : true,
27171 * Fires when a row is selected.
27172 * @param {SelectionModel} this
27173 * @param {Number} rowIndex The selected index
27174 * @param {Roo.data.Record} r The record
27176 "rowselect" : true,
27178 * @event rowdeselect
27179 * Fires when a row is deselected.
27180 * @param {SelectionModel} this
27181 * @param {Number} rowIndex The selected index
27183 "rowdeselect" : true
27185 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
27186 this.locked = false;
27189 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
27191 * @cfg {Boolean} singleSelect
27192 * True to allow selection of only one row at a time (defaults to false)
27194 singleSelect : false,
27197 initEvents : function()
27200 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
27201 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
27202 //}else{ // allow click to work like normal
27203 // this.grid.on("rowclick", this.handleDragableRowClick, this);
27205 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
27206 this.grid.on("rowclick", this.handleMouseDown, this);
27208 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
27209 "up" : function(e){
27211 this.selectPrevious(e.shiftKey);
27212 }else if(this.last !== false && this.lastActive !== false){
27213 var last = this.last;
27214 this.selectRange(this.last, this.lastActive-1);
27215 this.grid.getView().focusRow(this.lastActive);
27216 if(last !== false){
27220 this.selectFirstRow();
27222 this.fireEvent("afterselectionchange", this);
27224 "down" : function(e){
27226 this.selectNext(e.shiftKey);
27227 }else if(this.last !== false && this.lastActive !== false){
27228 var last = this.last;
27229 this.selectRange(this.last, this.lastActive+1);
27230 this.grid.getView().focusRow(this.lastActive);
27231 if(last !== false){
27235 this.selectFirstRow();
27237 this.fireEvent("afterselectionchange", this);
27241 this.grid.store.on('load', function(){
27242 this.selections.clear();
27245 var view = this.grid.view;
27246 view.on("refresh", this.onRefresh, this);
27247 view.on("rowupdated", this.onRowUpdated, this);
27248 view.on("rowremoved", this.onRemove, this);
27253 onRefresh : function()
27255 var ds = this.grid.store, i, v = this.grid.view;
27256 var s = this.selections;
27257 s.each(function(r){
27258 if((i = ds.indexOfId(r.id)) != -1){
27267 onRemove : function(v, index, r){
27268 this.selections.remove(r);
27272 onRowUpdated : function(v, index, r){
27273 if(this.isSelected(r)){
27274 v.onRowSelect(index);
27280 * @param {Array} records The records to select
27281 * @param {Boolean} keepExisting (optional) True to keep existing selections
27283 selectRecords : function(records, keepExisting)
27286 this.clearSelections();
27288 var ds = this.grid.store;
27289 for(var i = 0, len = records.length; i < len; i++){
27290 this.selectRow(ds.indexOf(records[i]), true);
27295 * Gets the number of selected rows.
27298 getCount : function(){
27299 return this.selections.length;
27303 * Selects the first row in the grid.
27305 selectFirstRow : function(){
27310 * Select the last row.
27311 * @param {Boolean} keepExisting (optional) True to keep existing selections
27313 selectLastRow : function(keepExisting){
27314 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
27315 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
27319 * Selects the row immediately following the last selected row.
27320 * @param {Boolean} keepExisting (optional) True to keep existing selections
27322 selectNext : function(keepExisting)
27324 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
27325 this.selectRow(this.last+1, keepExisting);
27326 this.grid.getView().focusRow(this.last);
27331 * Selects the row that precedes the last selected row.
27332 * @param {Boolean} keepExisting (optional) True to keep existing selections
27334 selectPrevious : function(keepExisting){
27336 this.selectRow(this.last-1, keepExisting);
27337 this.grid.getView().focusRow(this.last);
27342 * Returns the selected records
27343 * @return {Array} Array of selected records
27345 getSelections : function(){
27346 return [].concat(this.selections.items);
27350 * Returns the first selected record.
27353 getSelected : function(){
27354 return this.selections.itemAt(0);
27359 * Clears all selections.
27361 clearSelections : function(fast)
27367 var ds = this.grid.store;
27368 var s = this.selections;
27369 s.each(function(r){
27370 this.deselectRow(ds.indexOfId(r.id));
27374 this.selections.clear();
27381 * Selects all rows.
27383 selectAll : function(){
27387 this.selections.clear();
27388 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
27389 this.selectRow(i, true);
27394 * Returns True if there is a selection.
27395 * @return {Boolean}
27397 hasSelection : function(){
27398 return this.selections.length > 0;
27402 * Returns True if the specified row is selected.
27403 * @param {Number/Record} record The record or index of the record to check
27404 * @return {Boolean}
27406 isSelected : function(index){
27407 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
27408 return (r && this.selections.key(r.id) ? true : false);
27412 * Returns True if the specified record id is selected.
27413 * @param {String} id The id of record to check
27414 * @return {Boolean}
27416 isIdSelected : function(id){
27417 return (this.selections.key(id) ? true : false);
27422 handleMouseDBClick : function(e, t){
27426 handleMouseDown : function(e, t)
27428 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27429 if(this.isLocked() || rowIndex < 0 ){
27432 if(e.shiftKey && this.last !== false){
27433 var last = this.last;
27434 this.selectRange(last, rowIndex, e.ctrlKey);
27435 this.last = last; // reset the last
27439 var isSelected = this.isSelected(rowIndex);
27440 //Roo.log("select row:" + rowIndex);
27442 this.deselectRow(rowIndex);
27444 this.selectRow(rowIndex, true);
27448 if(e.button !== 0 && isSelected){
27449 alert('rowIndex 2: ' + rowIndex);
27450 view.focusRow(rowIndex);
27451 }else if(e.ctrlKey && isSelected){
27452 this.deselectRow(rowIndex);
27453 }else if(!isSelected){
27454 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27455 view.focusRow(rowIndex);
27459 this.fireEvent("afterselectionchange", this);
27462 handleDragableRowClick : function(grid, rowIndex, e)
27464 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27465 this.selectRow(rowIndex, false);
27466 grid.view.focusRow(rowIndex);
27467 this.fireEvent("afterselectionchange", this);
27472 * Selects multiple rows.
27473 * @param {Array} rows Array of the indexes of the row to select
27474 * @param {Boolean} keepExisting (optional) True to keep existing selections
27476 selectRows : function(rows, keepExisting){
27478 this.clearSelections();
27480 for(var i = 0, len = rows.length; i < len; i++){
27481 this.selectRow(rows[i], true);
27486 * Selects a range of rows. All rows in between startRow and endRow are also selected.
27487 * @param {Number} startRow The index of the first row in the range
27488 * @param {Number} endRow The index of the last row in the range
27489 * @param {Boolean} keepExisting (optional) True to retain existing selections
27491 selectRange : function(startRow, endRow, keepExisting){
27496 this.clearSelections();
27498 if(startRow <= endRow){
27499 for(var i = startRow; i <= endRow; i++){
27500 this.selectRow(i, true);
27503 for(var i = startRow; i >= endRow; i--){
27504 this.selectRow(i, true);
27510 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27511 * @param {Number} startRow The index of the first row in the range
27512 * @param {Number} endRow The index of the last row in the range
27514 deselectRange : function(startRow, endRow, preventViewNotify){
27518 for(var i = startRow; i <= endRow; i++){
27519 this.deselectRow(i, preventViewNotify);
27525 * @param {Number} row The index of the row to select
27526 * @param {Boolean} keepExisting (optional) True to keep existing selections
27528 selectRow : function(index, keepExisting, preventViewNotify)
27530 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27533 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27534 if(!keepExisting || this.singleSelect){
27535 this.clearSelections();
27538 var r = this.grid.store.getAt(index);
27539 //console.log('selectRow - record id :' + r.id);
27541 this.selections.add(r);
27542 this.last = this.lastActive = index;
27543 if(!preventViewNotify){
27544 var proxy = new Roo.Element(
27545 this.grid.getRowDom(index)
27547 proxy.addClass('bg-info info');
27549 this.fireEvent("rowselect", this, index, r);
27550 this.fireEvent("selectionchange", this);
27556 * @param {Number} row The index of the row to deselect
27558 deselectRow : function(index, preventViewNotify)
27563 if(this.last == index){
27566 if(this.lastActive == index){
27567 this.lastActive = false;
27570 var r = this.grid.store.getAt(index);
27575 this.selections.remove(r);
27576 //.console.log('deselectRow - record id :' + r.id);
27577 if(!preventViewNotify){
27579 var proxy = new Roo.Element(
27580 this.grid.getRowDom(index)
27582 proxy.removeClass('bg-info info');
27584 this.fireEvent("rowdeselect", this, index);
27585 this.fireEvent("selectionchange", this);
27589 restoreLast : function(){
27591 this.last = this._last;
27596 acceptsNav : function(row, col, cm){
27597 return !cm.isHidden(col) && cm.isCellEditable(col, row);
27601 onEditorKey : function(field, e){
27602 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27607 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27609 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27611 }else if(k == e.ENTER && !e.ctrlKey){
27615 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27617 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27619 }else if(k == e.ESC){
27623 g.startEditing(newCell[0], newCell[1]);
27629 * Ext JS Library 1.1.1
27630 * Copyright(c) 2006-2007, Ext JS, LLC.
27632 * Originally Released Under LGPL - original licence link has changed is not relivant.
27635 * <script type="text/javascript">
27639 * @class Roo.bootstrap.PagingToolbar
27640 * @extends Roo.bootstrap.NavSimplebar
27641 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27643 * Create a new PagingToolbar
27644 * @param {Object} config The config object
27645 * @param {Roo.data.Store} store
27647 Roo.bootstrap.PagingToolbar = function(config)
27649 // old args format still supported... - xtype is prefered..
27650 // created from xtype...
27652 this.ds = config.dataSource;
27654 if (config.store && !this.ds) {
27655 this.store= Roo.factory(config.store, Roo.data);
27656 this.ds = this.store;
27657 this.ds.xmodule = this.xmodule || false;
27660 this.toolbarItems = [];
27661 if (config.items) {
27662 this.toolbarItems = config.items;
27665 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27670 this.bind(this.ds);
27673 if (Roo.bootstrap.version == 4) {
27674 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27676 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27681 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27683 * @cfg {Roo.data.Store} dataSource
27684 * The underlying data store providing the paged data
27687 * @cfg {String/HTMLElement/Element} container
27688 * container The id or element that will contain the toolbar
27691 * @cfg {Boolean} displayInfo
27692 * True to display the displayMsg (defaults to false)
27695 * @cfg {Number} pageSize
27696 * The number of records to display per page (defaults to 20)
27700 * @cfg {String} displayMsg
27701 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27703 displayMsg : 'Displaying {0} - {1} of {2}',
27705 * @cfg {String} emptyMsg
27706 * The message to display when no records are found (defaults to "No data to display")
27708 emptyMsg : 'No data to display',
27710 * Customizable piece of the default paging text (defaults to "Page")
27713 beforePageText : "Page",
27715 * Customizable piece of the default paging text (defaults to "of %0")
27718 afterPageText : "of {0}",
27720 * Customizable piece of the default paging text (defaults to "First Page")
27723 firstText : "First Page",
27725 * Customizable piece of the default paging text (defaults to "Previous Page")
27728 prevText : "Previous Page",
27730 * Customizable piece of the default paging text (defaults to "Next Page")
27733 nextText : "Next Page",
27735 * Customizable piece of the default paging text (defaults to "Last Page")
27738 lastText : "Last Page",
27740 * Customizable piece of the default paging text (defaults to "Refresh")
27743 refreshText : "Refresh",
27747 onRender : function(ct, position)
27749 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27750 this.navgroup.parentId = this.id;
27751 this.navgroup.onRender(this.el, null);
27752 // add the buttons to the navgroup
27754 if(this.displayInfo){
27755 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27756 this.displayEl = this.el.select('.x-paging-info', true).first();
27757 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27758 // this.displayEl = navel.el.select('span',true).first();
27764 Roo.each(_this.buttons, function(e){ // this might need to use render????
27765 Roo.factory(e).render(_this.el);
27769 Roo.each(_this.toolbarItems, function(e) {
27770 _this.navgroup.addItem(e);
27774 this.first = this.navgroup.addItem({
27775 tooltip: this.firstText,
27776 cls: "prev btn-outline-secondary",
27777 html : ' <i class="fa fa-step-backward"></i>',
27779 preventDefault: true,
27780 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27783 this.prev = this.navgroup.addItem({
27784 tooltip: this.prevText,
27785 cls: "prev btn-outline-secondary",
27786 html : ' <i class="fa fa-backward"></i>',
27788 preventDefault: true,
27789 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
27791 //this.addSeparator();
27794 var field = this.navgroup.addItem( {
27796 cls : 'x-paging-position btn-outline-secondary',
27798 html : this.beforePageText +
27799 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27800 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
27803 this.field = field.el.select('input', true).first();
27804 this.field.on("keydown", this.onPagingKeydown, this);
27805 this.field.on("focus", function(){this.dom.select();});
27808 this.afterTextEl = field.el.select('.x-paging-after',true).first();
27809 //this.field.setHeight(18);
27810 //this.addSeparator();
27811 this.next = this.navgroup.addItem({
27812 tooltip: this.nextText,
27813 cls: "next btn-outline-secondary",
27814 html : ' <i class="fa fa-forward"></i>',
27816 preventDefault: true,
27817 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
27819 this.last = this.navgroup.addItem({
27820 tooltip: this.lastText,
27821 html : ' <i class="fa fa-step-forward"></i>',
27822 cls: "next btn-outline-secondary",
27824 preventDefault: true,
27825 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
27827 //this.addSeparator();
27828 this.loading = this.navgroup.addItem({
27829 tooltip: this.refreshText,
27830 cls: "btn-outline-secondary",
27831 html : ' <i class="fa fa-refresh"></i>',
27832 preventDefault: true,
27833 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27839 updateInfo : function(){
27840 if(this.displayEl){
27841 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27842 var msg = count == 0 ?
27846 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
27848 this.displayEl.update(msg);
27853 onLoad : function(ds, r, o)
27855 this.cursor = o.params && o.params.start ? o.params.start : 0;
27857 var d = this.getPageData(),
27862 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27863 this.field.dom.value = ap;
27864 this.first.setDisabled(ap == 1);
27865 this.prev.setDisabled(ap == 1);
27866 this.next.setDisabled(ap == ps);
27867 this.last.setDisabled(ap == ps);
27868 this.loading.enable();
27873 getPageData : function(){
27874 var total = this.ds.getTotalCount();
27877 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27878 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27883 onLoadError : function(){
27884 this.loading.enable();
27888 onPagingKeydown : function(e){
27889 var k = e.getKey();
27890 var d = this.getPageData();
27892 var v = this.field.dom.value, pageNum;
27893 if(!v || isNaN(pageNum = parseInt(v, 10))){
27894 this.field.dom.value = d.activePage;
27897 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27898 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27901 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))
27903 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27904 this.field.dom.value = pageNum;
27905 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27908 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27910 var v = this.field.dom.value, pageNum;
27911 var increment = (e.shiftKey) ? 10 : 1;
27912 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27915 if(!v || isNaN(pageNum = parseInt(v, 10))) {
27916 this.field.dom.value = d.activePage;
27919 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27921 this.field.dom.value = parseInt(v, 10) + increment;
27922 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27923 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27930 beforeLoad : function(){
27932 this.loading.disable();
27937 onClick : function(which){
27946 ds.load({params:{start: 0, limit: this.pageSize}});
27949 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27952 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27955 var total = ds.getTotalCount();
27956 var extra = total % this.pageSize;
27957 var lastStart = extra ? (total - extra) : total-this.pageSize;
27958 ds.load({params:{start: lastStart, limit: this.pageSize}});
27961 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27967 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27968 * @param {Roo.data.Store} store The data store to unbind
27970 unbind : function(ds){
27971 ds.un("beforeload", this.beforeLoad, this);
27972 ds.un("load", this.onLoad, this);
27973 ds.un("loadexception", this.onLoadError, this);
27974 ds.un("remove", this.updateInfo, this);
27975 ds.un("add", this.updateInfo, this);
27976 this.ds = undefined;
27980 * Binds the paging toolbar to the specified {@link Roo.data.Store}
27981 * @param {Roo.data.Store} store The data store to bind
27983 bind : function(ds){
27984 ds.on("beforeload", this.beforeLoad, this);
27985 ds.on("load", this.onLoad, this);
27986 ds.on("loadexception", this.onLoadError, this);
27987 ds.on("remove", this.updateInfo, this);
27988 ds.on("add", this.updateInfo, this);
27999 * @class Roo.bootstrap.MessageBar
28000 * @extends Roo.bootstrap.Component
28001 * Bootstrap MessageBar class
28002 * @cfg {String} html contents of the MessageBar
28003 * @cfg {String} weight (info | success | warning | danger) default info
28004 * @cfg {String} beforeClass insert the bar before the given class
28005 * @cfg {Boolean} closable (true | false) default false
28006 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28009 * Create a new Element
28010 * @param {Object} config The config object
28013 Roo.bootstrap.MessageBar = function(config){
28014 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28017 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
28023 beforeClass: 'bootstrap-sticky-wrap',
28025 getAutoCreate : function(){
28029 cls: 'alert alert-dismissable alert-' + this.weight,
28034 html: this.html || ''
28040 cfg.cls += ' alert-messages-fixed';
28054 onRender : function(ct, position)
28056 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28059 var cfg = Roo.apply({}, this.getAutoCreate());
28063 cfg.cls += ' ' + this.cls;
28066 cfg.style = this.style;
28068 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28070 this.el.setVisibilityMode(Roo.Element.DISPLAY);
28073 this.el.select('>button.close').on('click', this.hide, this);
28079 if (!this.rendered) {
28085 this.fireEvent('show', this);
28091 if (!this.rendered) {
28097 this.fireEvent('hide', this);
28100 update : function()
28102 // var e = this.el.dom.firstChild;
28104 // if(this.closable){
28105 // e = e.nextSibling;
28108 // e.data = this.html || '';
28110 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28126 * @class Roo.bootstrap.Graph
28127 * @extends Roo.bootstrap.Component
28128 * Bootstrap Graph class
28132 @cfg {String} graphtype bar | vbar | pie
28133 @cfg {number} g_x coodinator | centre x (pie)
28134 @cfg {number} g_y coodinator | centre y (pie)
28135 @cfg {number} g_r radius (pie)
28136 @cfg {number} g_height height of the chart (respected by all elements in the set)
28137 @cfg {number} g_width width of the chart (respected by all elements in the set)
28138 @cfg {Object} title The title of the chart
28141 -opts (object) options for the chart
28143 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28144 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28146 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.
28147 o stacked (boolean) whether or not to tread values as in a stacked bar chart
28149 o stretch (boolean)
28151 -opts (object) options for the pie
28154 o startAngle (number)
28155 o endAngle (number)
28159 * Create a new Input
28160 * @param {Object} config The config object
28163 Roo.bootstrap.Graph = function(config){
28164 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28170 * The img click event for the img.
28171 * @param {Roo.EventObject} e
28177 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
28188 //g_colors: this.colors,
28195 getAutoCreate : function(){
28206 onRender : function(ct,position){
28209 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28211 if (typeof(Raphael) == 'undefined') {
28212 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28216 this.raphael = Raphael(this.el.dom);
28218 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28219 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28220 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28221 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28223 r.text(160, 10, "Single Series Chart").attr(txtattr);
28224 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28225 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28226 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28228 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28229 r.barchart(330, 10, 300, 220, data1);
28230 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28231 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28234 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28235 // r.barchart(30, 30, 560, 250, xdata, {
28236 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28237 // axis : "0 0 1 1",
28238 // axisxlabels : xdata
28239 // //yvalues : cols,
28242 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28244 // this.load(null,xdata,{
28245 // axis : "0 0 1 1",
28246 // axisxlabels : xdata
28251 load : function(graphtype,xdata,opts)
28253 this.raphael.clear();
28255 graphtype = this.graphtype;
28260 var r = this.raphael,
28261 fin = function () {
28262 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28264 fout = function () {
28265 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28267 pfin = function() {
28268 this.sector.stop();
28269 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28272 this.label[0].stop();
28273 this.label[0].attr({ r: 7.5 });
28274 this.label[1].attr({ "font-weight": 800 });
28277 pfout = function() {
28278 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28281 this.label[0].animate({ r: 5 }, 500, "bounce");
28282 this.label[1].attr({ "font-weight": 400 });
28288 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28291 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28294 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
28295 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28297 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28304 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28309 setTitle: function(o)
28314 initEvents: function() {
28317 this.el.on('click', this.onClick, this);
28321 onClick : function(e)
28323 Roo.log('img onclick');
28324 this.fireEvent('click', this, e);
28336 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28339 * @class Roo.bootstrap.dash.NumberBox
28340 * @extends Roo.bootstrap.Component
28341 * Bootstrap NumberBox class
28342 * @cfg {String} headline Box headline
28343 * @cfg {String} content Box content
28344 * @cfg {String} icon Box icon
28345 * @cfg {String} footer Footer text
28346 * @cfg {String} fhref Footer href
28349 * Create a new NumberBox
28350 * @param {Object} config The config object
28354 Roo.bootstrap.dash.NumberBox = function(config){
28355 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28359 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
28368 getAutoCreate : function(){
28372 cls : 'small-box ',
28380 cls : 'roo-headline',
28381 html : this.headline
28385 cls : 'roo-content',
28386 html : this.content
28400 cls : 'ion ' + this.icon
28409 cls : 'small-box-footer',
28410 href : this.fhref || '#',
28414 cfg.cn.push(footer);
28421 onRender : function(ct,position){
28422 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28429 setHeadline: function (value)
28431 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28434 setFooter: function (value, href)
28436 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28439 this.el.select('a.small-box-footer',true).first().attr('href', href);
28444 setContent: function (value)
28446 this.el.select('.roo-content',true).first().dom.innerHTML = value;
28449 initEvents: function()
28463 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28466 * @class Roo.bootstrap.dash.TabBox
28467 * @extends Roo.bootstrap.Component
28468 * Bootstrap TabBox class
28469 * @cfg {String} title Title of the TabBox
28470 * @cfg {String} icon Icon of the TabBox
28471 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28472 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28475 * Create a new TabBox
28476 * @param {Object} config The config object
28480 Roo.bootstrap.dash.TabBox = function(config){
28481 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28486 * When a pane is added
28487 * @param {Roo.bootstrap.dash.TabPane} pane
28491 * @event activatepane
28492 * When a pane is activated
28493 * @param {Roo.bootstrap.dash.TabPane} pane
28495 "activatepane" : true
28503 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28508 tabScrollable : false,
28510 getChildContainer : function()
28512 return this.el.select('.tab-content', true).first();
28515 getAutoCreate : function(){
28519 cls: 'pull-left header',
28527 cls: 'fa ' + this.icon
28533 cls: 'nav nav-tabs pull-right',
28539 if(this.tabScrollable){
28546 cls: 'nav nav-tabs pull-right',
28557 cls: 'nav-tabs-custom',
28562 cls: 'tab-content no-padding',
28570 initEvents : function()
28572 //Roo.log('add add pane handler');
28573 this.on('addpane', this.onAddPane, this);
28576 * Updates the box title
28577 * @param {String} html to set the title to.
28579 setTitle : function(value)
28581 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28583 onAddPane : function(pane)
28585 this.panes.push(pane);
28586 //Roo.log('addpane');
28588 // tabs are rendere left to right..
28589 if(!this.showtabs){
28593 var ctr = this.el.select('.nav-tabs', true).first();
28596 var existing = ctr.select('.nav-tab',true);
28597 var qty = existing.getCount();;
28600 var tab = ctr.createChild({
28602 cls : 'nav-tab' + (qty ? '' : ' active'),
28610 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28613 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28615 pane.el.addClass('active');
28620 onTabClick : function(ev,un,ob,pane)
28622 //Roo.log('tab - prev default');
28623 ev.preventDefault();
28626 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28627 pane.tab.addClass('active');
28628 //Roo.log(pane.title);
28629 this.getChildContainer().select('.tab-pane',true).removeClass('active');
28630 // technically we should have a deactivate event.. but maybe add later.
28631 // and it should not de-activate the selected tab...
28632 this.fireEvent('activatepane', pane);
28633 pane.el.addClass('active');
28634 pane.fireEvent('activate');
28639 getActivePane : function()
28642 Roo.each(this.panes, function(p) {
28643 if(p.el.hasClass('active')){
28664 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28666 * @class Roo.bootstrap.TabPane
28667 * @extends Roo.bootstrap.Component
28668 * Bootstrap TabPane class
28669 * @cfg {Boolean} active (false | true) Default false
28670 * @cfg {String} title title of panel
28674 * Create a new TabPane
28675 * @param {Object} config The config object
28678 Roo.bootstrap.dash.TabPane = function(config){
28679 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28685 * When a pane is activated
28686 * @param {Roo.bootstrap.dash.TabPane} pane
28693 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
28698 // the tabBox that this is attached to.
28701 getAutoCreate : function()
28709 cfg.cls += ' active';
28714 initEvents : function()
28716 //Roo.log('trigger add pane handler');
28717 this.parent().fireEvent('addpane', this)
28721 * Updates the tab title
28722 * @param {String} html to set the title to.
28724 setTitle: function(str)
28730 this.tab.select('a', true).first().dom.innerHTML = str;
28747 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28750 * @class Roo.bootstrap.menu.Menu
28751 * @extends Roo.bootstrap.Component
28752 * Bootstrap Menu class - container for Menu
28753 * @cfg {String} html Text of the menu
28754 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28755 * @cfg {String} icon Font awesome icon
28756 * @cfg {String} pos Menu align to (top | bottom) default bottom
28760 * Create a new Menu
28761 * @param {Object} config The config object
28765 Roo.bootstrap.menu.Menu = function(config){
28766 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28770 * @event beforeshow
28771 * Fires before this menu is displayed
28772 * @param {Roo.bootstrap.menu.Menu} this
28776 * @event beforehide
28777 * Fires before this menu is hidden
28778 * @param {Roo.bootstrap.menu.Menu} this
28783 * Fires after this menu is displayed
28784 * @param {Roo.bootstrap.menu.Menu} this
28789 * Fires after this menu is hidden
28790 * @param {Roo.bootstrap.menu.Menu} this
28795 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28796 * @param {Roo.bootstrap.menu.Menu} this
28797 * @param {Roo.EventObject} e
28804 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
28808 weight : 'default',
28813 getChildContainer : function() {
28814 if(this.isSubMenu){
28818 return this.el.select('ul.dropdown-menu', true).first();
28821 getAutoCreate : function()
28826 cls : 'roo-menu-text',
28834 cls : 'fa ' + this.icon
28845 cls : 'dropdown-button btn btn-' + this.weight,
28850 cls : 'dropdown-toggle btn btn-' + this.weight,
28860 cls : 'dropdown-menu'
28866 if(this.pos == 'top'){
28867 cfg.cls += ' dropup';
28870 if(this.isSubMenu){
28873 cls : 'dropdown-menu'
28880 onRender : function(ct, position)
28882 this.isSubMenu = ct.hasClass('dropdown-submenu');
28884 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28887 initEvents : function()
28889 if(this.isSubMenu){
28893 this.hidden = true;
28895 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28896 this.triggerEl.on('click', this.onTriggerPress, this);
28898 this.buttonEl = this.el.select('button.dropdown-button', true).first();
28899 this.buttonEl.on('click', this.onClick, this);
28905 if(this.isSubMenu){
28909 return this.el.select('ul.dropdown-menu', true).first();
28912 onClick : function(e)
28914 this.fireEvent("click", this, e);
28917 onTriggerPress : function(e)
28919 if (this.isVisible()) {
28926 isVisible : function(){
28927 return !this.hidden;
28932 this.fireEvent("beforeshow", this);
28934 this.hidden = false;
28935 this.el.addClass('open');
28937 Roo.get(document).on("mouseup", this.onMouseUp, this);
28939 this.fireEvent("show", this);
28946 this.fireEvent("beforehide", this);
28948 this.hidden = true;
28949 this.el.removeClass('open');
28951 Roo.get(document).un("mouseup", this.onMouseUp);
28953 this.fireEvent("hide", this);
28956 onMouseUp : function()
28970 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28973 * @class Roo.bootstrap.menu.Item
28974 * @extends Roo.bootstrap.Component
28975 * Bootstrap MenuItem class
28976 * @cfg {Boolean} submenu (true | false) default false
28977 * @cfg {String} html text of the item
28978 * @cfg {String} href the link
28979 * @cfg {Boolean} disable (true | false) default false
28980 * @cfg {Boolean} preventDefault (true | false) default true
28981 * @cfg {String} icon Font awesome icon
28982 * @cfg {String} pos Submenu align to (left | right) default right
28986 * Create a new Item
28987 * @param {Object} config The config object
28991 Roo.bootstrap.menu.Item = function(config){
28992 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28996 * Fires when the mouse is hovering over this menu
28997 * @param {Roo.bootstrap.menu.Item} this
28998 * @param {Roo.EventObject} e
29003 * Fires when the mouse exits this menu
29004 * @param {Roo.bootstrap.menu.Item} this
29005 * @param {Roo.EventObject} e
29011 * The raw click event for the entire grid.
29012 * @param {Roo.EventObject} e
29018 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
29023 preventDefault: true,
29028 getAutoCreate : function()
29033 cls : 'roo-menu-item-text',
29041 cls : 'fa ' + this.icon
29050 href : this.href || '#',
29057 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
29061 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
29063 if(this.pos == 'left'){
29064 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
29071 initEvents : function()
29073 this.el.on('mouseover', this.onMouseOver, this);
29074 this.el.on('mouseout', this.onMouseOut, this);
29076 this.el.select('a', true).first().on('click', this.onClick, this);
29080 onClick : function(e)
29082 if(this.preventDefault){
29083 e.preventDefault();
29086 this.fireEvent("click", this, e);
29089 onMouseOver : function(e)
29091 if(this.submenu && this.pos == 'left'){
29092 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29095 this.fireEvent("mouseover", this, e);
29098 onMouseOut : function(e)
29100 this.fireEvent("mouseout", this, e);
29112 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29115 * @class Roo.bootstrap.menu.Separator
29116 * @extends Roo.bootstrap.Component
29117 * Bootstrap Separator class
29120 * Create a new Separator
29121 * @param {Object} config The config object
29125 Roo.bootstrap.menu.Separator = function(config){
29126 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29129 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
29131 getAutoCreate : function(){
29134 cls: 'dropdown-divider divider'
29152 * @class Roo.bootstrap.Tooltip
29153 * Bootstrap Tooltip class
29154 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29155 * to determine which dom element triggers the tooltip.
29157 * It needs to add support for additional attributes like tooltip-position
29160 * Create a new Toolti
29161 * @param {Object} config The config object
29164 Roo.bootstrap.Tooltip = function(config){
29165 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29167 this.alignment = Roo.bootstrap.Tooltip.alignment;
29169 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29170 this.alignment = config.alignment;
29175 Roo.apply(Roo.bootstrap.Tooltip, {
29177 * @function init initialize tooltip monitoring.
29181 currentTip : false,
29182 currentRegion : false,
29188 Roo.get(document).on('mouseover', this.enter ,this);
29189 Roo.get(document).on('mouseout', this.leave, this);
29192 this.currentTip = new Roo.bootstrap.Tooltip();
29195 enter : function(ev)
29197 var dom = ev.getTarget();
29199 //Roo.log(['enter',dom]);
29200 var el = Roo.fly(dom);
29201 if (this.currentEl) {
29203 //Roo.log(this.currentEl);
29204 //Roo.log(this.currentEl.contains(dom));
29205 if (this.currentEl == el) {
29208 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29214 if (this.currentTip.el) {
29215 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29219 if(!el || el.dom == document){
29225 if (!el.attr('tooltip')) {
29226 pel = el.findParent("[tooltip]");
29228 bindEl = Roo.get(pel);
29234 // you can not look for children, as if el is the body.. then everythign is the child..
29235 if (!pel && !el.attr('tooltip')) { //
29236 if (!el.select("[tooltip]").elements.length) {
29239 // is the mouse over this child...?
29240 bindEl = el.select("[tooltip]").first();
29241 var xy = ev.getXY();
29242 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29243 //Roo.log("not in region.");
29246 //Roo.log("child element over..");
29249 this.currentEl = el;
29250 this.currentTip.bind(bindEl);
29251 this.currentRegion = Roo.lib.Region.getRegion(dom);
29252 this.currentTip.enter();
29255 leave : function(ev)
29257 var dom = ev.getTarget();
29258 //Roo.log(['leave',dom]);
29259 if (!this.currentEl) {
29264 if (dom != this.currentEl.dom) {
29267 var xy = ev.getXY();
29268 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
29271 // only activate leave if mouse cursor is outside... bounding box..
29276 if (this.currentTip) {
29277 this.currentTip.leave();
29279 //Roo.log('clear currentEl');
29280 this.currentEl = false;
29285 'left' : ['r-l', [-2,0], 'right'],
29286 'right' : ['l-r', [2,0], 'left'],
29287 'bottom' : ['t-b', [0,2], 'top'],
29288 'top' : [ 'b-t', [0,-2], 'bottom']
29294 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
29299 delay : null, // can be { show : 300 , hide: 500}
29303 hoverState : null, //???
29305 placement : 'bottom',
29309 getAutoCreate : function(){
29316 cls : 'tooltip-arrow arrow'
29319 cls : 'tooltip-inner'
29326 bind : function(el)
29331 initEvents : function()
29333 this.arrowEl = this.el.select('.arrow', true).first();
29334 this.innerEl = this.el.select('.tooltip-inner', true).first();
29337 enter : function () {
29339 if (this.timeout != null) {
29340 clearTimeout(this.timeout);
29343 this.hoverState = 'in';
29344 //Roo.log("enter - show");
29345 if (!this.delay || !this.delay.show) {
29350 this.timeout = setTimeout(function () {
29351 if (_t.hoverState == 'in') {
29354 }, this.delay.show);
29358 clearTimeout(this.timeout);
29360 this.hoverState = 'out';
29361 if (!this.delay || !this.delay.hide) {
29367 this.timeout = setTimeout(function () {
29368 //Roo.log("leave - timeout");
29370 if (_t.hoverState == 'out') {
29372 Roo.bootstrap.Tooltip.currentEl = false;
29377 show : function (msg)
29380 this.render(document.body);
29383 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29385 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29387 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29389 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29390 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29392 var placement = typeof this.placement == 'function' ?
29393 this.placement.call(this, this.el, on_el) :
29396 var autoToken = /\s?auto?\s?/i;
29397 var autoPlace = autoToken.test(placement);
29399 placement = placement.replace(autoToken, '') || 'top';
29403 //this.el.setXY([0,0]);
29405 //this.el.dom.style.display='block';
29407 //this.el.appendTo(on_el);
29409 var p = this.getPosition();
29410 var box = this.el.getBox();
29416 var align = this.alignment[placement];
29418 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29420 if(placement == 'top' || placement == 'bottom'){
29422 placement = 'right';
29425 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29426 placement = 'left';
29429 var scroll = Roo.select('body', true).first().getScroll();
29431 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29435 align = this.alignment[placement];
29437 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29441 var elems = document.getElementsByTagName('div');
29442 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29443 for (var i = 0; i < elems.length; i++) {
29444 var zindex = Number.parseInt(
29445 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29448 if (zindex > highest) {
29455 this.el.dom.style.zIndex = highest;
29457 this.el.alignTo(this.bindEl, align[0],align[1]);
29458 //var arrow = this.el.select('.arrow',true).first();
29459 //arrow.set(align[2],
29461 this.el.addClass(placement);
29462 this.el.addClass("bs-tooltip-"+ placement);
29464 this.el.addClass('in fade show');
29466 this.hoverState = null;
29468 if (this.el.hasClass('fade')) {
29483 //this.el.setXY([0,0]);
29484 this.el.removeClass(['show', 'in']);
29500 * @class Roo.bootstrap.LocationPicker
29501 * @extends Roo.bootstrap.Component
29502 * Bootstrap LocationPicker class
29503 * @cfg {Number} latitude Position when init default 0
29504 * @cfg {Number} longitude Position when init default 0
29505 * @cfg {Number} zoom default 15
29506 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29507 * @cfg {Boolean} mapTypeControl default false
29508 * @cfg {Boolean} disableDoubleClickZoom default false
29509 * @cfg {Boolean} scrollwheel default true
29510 * @cfg {Boolean} streetViewControl default false
29511 * @cfg {Number} radius default 0
29512 * @cfg {String} locationName
29513 * @cfg {Boolean} draggable default true
29514 * @cfg {Boolean} enableAutocomplete default false
29515 * @cfg {Boolean} enableReverseGeocode default true
29516 * @cfg {String} markerTitle
29519 * Create a new LocationPicker
29520 * @param {Object} config The config object
29524 Roo.bootstrap.LocationPicker = function(config){
29526 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29531 * Fires when the picker initialized.
29532 * @param {Roo.bootstrap.LocationPicker} this
29533 * @param {Google Location} location
29537 * @event positionchanged
29538 * Fires when the picker position changed.
29539 * @param {Roo.bootstrap.LocationPicker} this
29540 * @param {Google Location} location
29542 positionchanged : true,
29545 * Fires when the map resize.
29546 * @param {Roo.bootstrap.LocationPicker} this
29551 * Fires when the map show.
29552 * @param {Roo.bootstrap.LocationPicker} this
29557 * Fires when the map hide.
29558 * @param {Roo.bootstrap.LocationPicker} this
29563 * Fires when click the map.
29564 * @param {Roo.bootstrap.LocationPicker} this
29565 * @param {Map event} e
29569 * @event mapRightClick
29570 * Fires when right click the map.
29571 * @param {Roo.bootstrap.LocationPicker} this
29572 * @param {Map event} e
29574 mapRightClick : true,
29576 * @event markerClick
29577 * Fires when click the marker.
29578 * @param {Roo.bootstrap.LocationPicker} this
29579 * @param {Map event} e
29581 markerClick : true,
29583 * @event markerRightClick
29584 * Fires when right click the marker.
29585 * @param {Roo.bootstrap.LocationPicker} this
29586 * @param {Map event} e
29588 markerRightClick : true,
29590 * @event OverlayViewDraw
29591 * Fires when OverlayView Draw
29592 * @param {Roo.bootstrap.LocationPicker} this
29594 OverlayViewDraw : true,
29596 * @event OverlayViewOnAdd
29597 * Fires when OverlayView Draw
29598 * @param {Roo.bootstrap.LocationPicker} this
29600 OverlayViewOnAdd : true,
29602 * @event OverlayViewOnRemove
29603 * Fires when OverlayView Draw
29604 * @param {Roo.bootstrap.LocationPicker} this
29606 OverlayViewOnRemove : true,
29608 * @event OverlayViewShow
29609 * Fires when OverlayView Draw
29610 * @param {Roo.bootstrap.LocationPicker} this
29611 * @param {Pixel} cpx
29613 OverlayViewShow : true,
29615 * @event OverlayViewHide
29616 * Fires when OverlayView Draw
29617 * @param {Roo.bootstrap.LocationPicker} this
29619 OverlayViewHide : true,
29621 * @event loadexception
29622 * Fires when load google lib failed.
29623 * @param {Roo.bootstrap.LocationPicker} this
29625 loadexception : true
29630 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
29632 gMapContext: false,
29638 mapTypeControl: false,
29639 disableDoubleClickZoom: false,
29641 streetViewControl: false,
29645 enableAutocomplete: false,
29646 enableReverseGeocode: true,
29649 getAutoCreate: function()
29654 cls: 'roo-location-picker'
29660 initEvents: function(ct, position)
29662 if(!this.el.getWidth() || this.isApplied()){
29666 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29671 initial: function()
29673 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29674 this.fireEvent('loadexception', this);
29678 if(!this.mapTypeId){
29679 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29682 this.gMapContext = this.GMapContext();
29684 this.initOverlayView();
29686 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29690 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29691 _this.setPosition(_this.gMapContext.marker.position);
29694 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29695 _this.fireEvent('mapClick', this, event);
29699 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29700 _this.fireEvent('mapRightClick', this, event);
29704 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29705 _this.fireEvent('markerClick', this, event);
29709 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29710 _this.fireEvent('markerRightClick', this, event);
29714 this.setPosition(this.gMapContext.location);
29716 this.fireEvent('initial', this, this.gMapContext.location);
29719 initOverlayView: function()
29723 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29727 _this.fireEvent('OverlayViewDraw', _this);
29732 _this.fireEvent('OverlayViewOnAdd', _this);
29735 onRemove: function()
29737 _this.fireEvent('OverlayViewOnRemove', _this);
29740 show: function(cpx)
29742 _this.fireEvent('OverlayViewShow', _this, cpx);
29747 _this.fireEvent('OverlayViewHide', _this);
29753 fromLatLngToContainerPixel: function(event)
29755 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29758 isApplied: function()
29760 return this.getGmapContext() == false ? false : true;
29763 getGmapContext: function()
29765 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29768 GMapContext: function()
29770 var position = new google.maps.LatLng(this.latitude, this.longitude);
29772 var _map = new google.maps.Map(this.el.dom, {
29775 mapTypeId: this.mapTypeId,
29776 mapTypeControl: this.mapTypeControl,
29777 disableDoubleClickZoom: this.disableDoubleClickZoom,
29778 scrollwheel: this.scrollwheel,
29779 streetViewControl: this.streetViewControl,
29780 locationName: this.locationName,
29781 draggable: this.draggable,
29782 enableAutocomplete: this.enableAutocomplete,
29783 enableReverseGeocode: this.enableReverseGeocode
29786 var _marker = new google.maps.Marker({
29787 position: position,
29789 title: this.markerTitle,
29790 draggable: this.draggable
29797 location: position,
29798 radius: this.radius,
29799 locationName: this.locationName,
29800 addressComponents: {
29801 formatted_address: null,
29802 addressLine1: null,
29803 addressLine2: null,
29805 streetNumber: null,
29809 stateOrProvince: null
29812 domContainer: this.el.dom,
29813 geodecoder: new google.maps.Geocoder()
29817 drawCircle: function(center, radius, options)
29819 if (this.gMapContext.circle != null) {
29820 this.gMapContext.circle.setMap(null);
29824 options = Roo.apply({}, options, {
29825 strokeColor: "#0000FF",
29826 strokeOpacity: .35,
29828 fillColor: "#0000FF",
29832 options.map = this.gMapContext.map;
29833 options.radius = radius;
29834 options.center = center;
29835 this.gMapContext.circle = new google.maps.Circle(options);
29836 return this.gMapContext.circle;
29842 setPosition: function(location)
29844 this.gMapContext.location = location;
29845 this.gMapContext.marker.setPosition(location);
29846 this.gMapContext.map.panTo(location);
29847 this.drawCircle(location, this.gMapContext.radius, {});
29851 if (this.gMapContext.settings.enableReverseGeocode) {
29852 this.gMapContext.geodecoder.geocode({
29853 latLng: this.gMapContext.location
29854 }, function(results, status) {
29856 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29857 _this.gMapContext.locationName = results[0].formatted_address;
29858 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29860 _this.fireEvent('positionchanged', this, location);
29867 this.fireEvent('positionchanged', this, location);
29872 google.maps.event.trigger(this.gMapContext.map, "resize");
29874 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29876 this.fireEvent('resize', this);
29879 setPositionByLatLng: function(latitude, longitude)
29881 this.setPosition(new google.maps.LatLng(latitude, longitude));
29884 getCurrentPosition: function()
29887 latitude: this.gMapContext.location.lat(),
29888 longitude: this.gMapContext.location.lng()
29892 getAddressName: function()
29894 return this.gMapContext.locationName;
29897 getAddressComponents: function()
29899 return this.gMapContext.addressComponents;
29902 address_component_from_google_geocode: function(address_components)
29906 for (var i = 0; i < address_components.length; i++) {
29907 var component = address_components[i];
29908 if (component.types.indexOf("postal_code") >= 0) {
29909 result.postalCode = component.short_name;
29910 } else if (component.types.indexOf("street_number") >= 0) {
29911 result.streetNumber = component.short_name;
29912 } else if (component.types.indexOf("route") >= 0) {
29913 result.streetName = component.short_name;
29914 } else if (component.types.indexOf("neighborhood") >= 0) {
29915 result.city = component.short_name;
29916 } else if (component.types.indexOf("locality") >= 0) {
29917 result.city = component.short_name;
29918 } else if (component.types.indexOf("sublocality") >= 0) {
29919 result.district = component.short_name;
29920 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29921 result.stateOrProvince = component.short_name;
29922 } else if (component.types.indexOf("country") >= 0) {
29923 result.country = component.short_name;
29927 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29928 result.addressLine2 = "";
29932 setZoomLevel: function(zoom)
29934 this.gMapContext.map.setZoom(zoom);
29947 this.fireEvent('show', this);
29958 this.fireEvent('hide', this);
29963 Roo.apply(Roo.bootstrap.LocationPicker, {
29965 OverlayView : function(map, options)
29967 options = options || {};
29974 * @class Roo.bootstrap.Alert
29975 * @extends Roo.bootstrap.Component
29976 * Bootstrap Alert class - shows an alert area box
29978 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29979 Enter a valid email address
29982 * @cfg {String} title The title of alert
29983 * @cfg {String} html The content of alert
29984 * @cfg {String} weight (success|info|warning|danger) Weight of the message
29985 * @cfg {String} fa font-awesomeicon
29986 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
29987 * @cfg {Boolean} close true to show a x closer
29991 * Create a new alert
29992 * @param {Object} config The config object
29996 Roo.bootstrap.Alert = function(config){
29997 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30001 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
30007 faicon: false, // BC
30011 getAutoCreate : function()
30023 style : this.close ? '' : 'display:none'
30027 cls : 'roo-alert-icon'
30032 cls : 'roo-alert-title',
30037 cls : 'roo-alert-text',
30044 cfg.cn[0].cls += ' fa ' + this.faicon;
30047 cfg.cn[0].cls += ' fa ' + this.fa;
30051 cfg.cls += ' alert-' + this.weight;
30057 initEvents: function()
30059 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30060 this.titleEl = this.el.select('.roo-alert-title',true).first();
30061 this.iconEl = this.el.select('.roo-alert-icon',true).first();
30062 this.htmlEl = this.el.select('.roo-alert-text',true).first();
30063 if (this.seconds > 0) {
30064 this.hide.defer(this.seconds, this);
30068 * Set the Title Message HTML
30069 * @param {String} html
30071 setTitle : function(str)
30073 this.titleEl.dom.innerHTML = str;
30077 * Set the Body Message HTML
30078 * @param {String} html
30080 setHtml : function(str)
30082 this.htmlEl.dom.innerHTML = str;
30085 * Set the Weight of the alert
30086 * @param {String} (success|info|warning|danger) weight
30089 setWeight : function(weight)
30092 this.el.removeClass('alert-' + this.weight);
30095 this.weight = weight;
30097 this.el.addClass('alert-' + this.weight);
30100 * Set the Icon of the alert
30101 * @param {String} see fontawsome names (name without the 'fa-' bit)
30103 setIcon : function(icon)
30106 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30109 this.faicon = icon;
30111 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30136 * @class Roo.bootstrap.UploadCropbox
30137 * @extends Roo.bootstrap.Component
30138 * Bootstrap UploadCropbox class
30139 * @cfg {String} emptyText show when image has been loaded
30140 * @cfg {String} rotateNotify show when image too small to rotate
30141 * @cfg {Number} errorTimeout default 3000
30142 * @cfg {Number} minWidth default 300
30143 * @cfg {Number} minHeight default 300
30144 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30145 * @cfg {Boolean} isDocument (true|false) default false
30146 * @cfg {String} url action url
30147 * @cfg {String} paramName default 'imageUpload'
30148 * @cfg {String} method default POST
30149 * @cfg {Boolean} loadMask (true|false) default true
30150 * @cfg {Boolean} loadingText default 'Loading...'
30153 * Create a new UploadCropbox
30154 * @param {Object} config The config object
30157 Roo.bootstrap.UploadCropbox = function(config){
30158 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30162 * @event beforeselectfile
30163 * Fire before select file
30164 * @param {Roo.bootstrap.UploadCropbox} this
30166 "beforeselectfile" : true,
30169 * Fire after initEvent
30170 * @param {Roo.bootstrap.UploadCropbox} this
30175 * Fire after initEvent
30176 * @param {Roo.bootstrap.UploadCropbox} this
30177 * @param {String} data
30182 * Fire when preparing the file data
30183 * @param {Roo.bootstrap.UploadCropbox} this
30184 * @param {Object} file
30189 * Fire when get exception
30190 * @param {Roo.bootstrap.UploadCropbox} this
30191 * @param {XMLHttpRequest} xhr
30193 "exception" : true,
30195 * @event beforeloadcanvas
30196 * Fire before load the canvas
30197 * @param {Roo.bootstrap.UploadCropbox} this
30198 * @param {String} src
30200 "beforeloadcanvas" : true,
30203 * Fire when trash image
30204 * @param {Roo.bootstrap.UploadCropbox} this
30209 * Fire when download the image
30210 * @param {Roo.bootstrap.UploadCropbox} this
30214 * @event footerbuttonclick
30215 * Fire when footerbuttonclick
30216 * @param {Roo.bootstrap.UploadCropbox} this
30217 * @param {String} type
30219 "footerbuttonclick" : true,
30223 * @param {Roo.bootstrap.UploadCropbox} this
30228 * Fire when rotate the image
30229 * @param {Roo.bootstrap.UploadCropbox} this
30230 * @param {String} pos
30235 * Fire when inspect the file
30236 * @param {Roo.bootstrap.UploadCropbox} this
30237 * @param {Object} file
30242 * Fire when xhr upload the file
30243 * @param {Roo.bootstrap.UploadCropbox} this
30244 * @param {Object} data
30249 * Fire when arrange the file data
30250 * @param {Roo.bootstrap.UploadCropbox} this
30251 * @param {Object} formData
30256 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30259 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
30261 emptyText : 'Click to upload image',
30262 rotateNotify : 'Image is too small to rotate',
30263 errorTimeout : 3000,
30277 cropType : 'image/jpeg',
30279 canvasLoaded : false,
30280 isDocument : false,
30282 paramName : 'imageUpload',
30284 loadingText : 'Loading...',
30287 getAutoCreate : function()
30291 cls : 'roo-upload-cropbox',
30295 cls : 'roo-upload-cropbox-selector',
30300 cls : 'roo-upload-cropbox-body',
30301 style : 'cursor:pointer',
30305 cls : 'roo-upload-cropbox-preview'
30309 cls : 'roo-upload-cropbox-thumb'
30313 cls : 'roo-upload-cropbox-empty-notify',
30314 html : this.emptyText
30318 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30319 html : this.rotateNotify
30325 cls : 'roo-upload-cropbox-footer',
30328 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30338 onRender : function(ct, position)
30340 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30342 if (this.buttons.length) {
30344 Roo.each(this.buttons, function(bb) {
30346 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30348 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30354 this.maskEl = this.el;
30358 initEvents : function()
30360 this.urlAPI = (window.createObjectURL && window) ||
30361 (window.URL && URL.revokeObjectURL && URL) ||
30362 (window.webkitURL && webkitURL);
30364 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30365 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30367 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30368 this.selectorEl.hide();
30370 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30371 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30373 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30374 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30375 this.thumbEl.hide();
30377 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30378 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30380 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30381 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30382 this.errorEl.hide();
30384 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30385 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30386 this.footerEl.hide();
30388 this.setThumbBoxSize();
30394 this.fireEvent('initial', this);
30401 window.addEventListener("resize", function() { _this.resize(); } );
30403 this.bodyEl.on('click', this.beforeSelectFile, this);
30406 this.bodyEl.on('touchstart', this.onTouchStart, this);
30407 this.bodyEl.on('touchmove', this.onTouchMove, this);
30408 this.bodyEl.on('touchend', this.onTouchEnd, this);
30412 this.bodyEl.on('mousedown', this.onMouseDown, this);
30413 this.bodyEl.on('mousemove', this.onMouseMove, this);
30414 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30415 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30416 Roo.get(document).on('mouseup', this.onMouseUp, this);
30419 this.selectorEl.on('change', this.onFileSelected, this);
30425 this.baseScale = 1;
30427 this.baseRotate = 1;
30428 this.dragable = false;
30429 this.pinching = false;
30432 this.cropData = false;
30433 this.notifyEl.dom.innerHTML = this.emptyText;
30435 this.selectorEl.dom.value = '';
30439 resize : function()
30441 if(this.fireEvent('resize', this) != false){
30442 this.setThumbBoxPosition();
30443 this.setCanvasPosition();
30447 onFooterButtonClick : function(e, el, o, type)
30450 case 'rotate-left' :
30451 this.onRotateLeft(e);
30453 case 'rotate-right' :
30454 this.onRotateRight(e);
30457 this.beforeSelectFile(e);
30472 this.fireEvent('footerbuttonclick', this, type);
30475 beforeSelectFile : function(e)
30477 e.preventDefault();
30479 if(this.fireEvent('beforeselectfile', this) != false){
30480 this.selectorEl.dom.click();
30484 onFileSelected : function(e)
30486 e.preventDefault();
30488 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30492 var file = this.selectorEl.dom.files[0];
30494 if(this.fireEvent('inspect', this, file) != false){
30495 this.prepare(file);
30500 trash : function(e)
30502 this.fireEvent('trash', this);
30505 download : function(e)
30507 this.fireEvent('download', this);
30510 loadCanvas : function(src)
30512 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30516 this.imageEl = document.createElement('img');
30520 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30522 this.imageEl.src = src;
30526 onLoadCanvas : function()
30528 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30529 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30531 this.bodyEl.un('click', this.beforeSelectFile, this);
30533 this.notifyEl.hide();
30534 this.thumbEl.show();
30535 this.footerEl.show();
30537 this.baseRotateLevel();
30539 if(this.isDocument){
30540 this.setThumbBoxSize();
30543 this.setThumbBoxPosition();
30545 this.baseScaleLevel();
30551 this.canvasLoaded = true;
30554 this.maskEl.unmask();
30559 setCanvasPosition : function()
30561 if(!this.canvasEl){
30565 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30566 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30568 this.previewEl.setLeft(pw);
30569 this.previewEl.setTop(ph);
30573 onMouseDown : function(e)
30577 this.dragable = true;
30578 this.pinching = false;
30580 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30581 this.dragable = false;
30585 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30586 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30590 onMouseMove : function(e)
30594 if(!this.canvasLoaded){
30598 if (!this.dragable){
30602 var minX = Math.ceil(this.thumbEl.getLeft(true));
30603 var minY = Math.ceil(this.thumbEl.getTop(true));
30605 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30606 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30608 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30609 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30611 x = x - this.mouseX;
30612 y = y - this.mouseY;
30614 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30615 var bgY = Math.ceil(y + this.previewEl.getTop(true));
30617 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30618 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30620 this.previewEl.setLeft(bgX);
30621 this.previewEl.setTop(bgY);
30623 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30624 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30627 onMouseUp : function(e)
30631 this.dragable = false;
30634 onMouseWheel : function(e)
30638 this.startScale = this.scale;
30640 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30642 if(!this.zoomable()){
30643 this.scale = this.startScale;
30652 zoomable : function()
30654 var minScale = this.thumbEl.getWidth() / this.minWidth;
30656 if(this.minWidth < this.minHeight){
30657 minScale = this.thumbEl.getHeight() / this.minHeight;
30660 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30661 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30665 (this.rotate == 0 || this.rotate == 180) &&
30667 width > this.imageEl.OriginWidth ||
30668 height > this.imageEl.OriginHeight ||
30669 (width < this.minWidth && height < this.minHeight)
30677 (this.rotate == 90 || this.rotate == 270) &&
30679 width > this.imageEl.OriginWidth ||
30680 height > this.imageEl.OriginHeight ||
30681 (width < this.minHeight && height < this.minWidth)
30688 !this.isDocument &&
30689 (this.rotate == 0 || this.rotate == 180) &&
30691 width < this.minWidth ||
30692 width > this.imageEl.OriginWidth ||
30693 height < this.minHeight ||
30694 height > this.imageEl.OriginHeight
30701 !this.isDocument &&
30702 (this.rotate == 90 || this.rotate == 270) &&
30704 width < this.minHeight ||
30705 width > this.imageEl.OriginWidth ||
30706 height < this.minWidth ||
30707 height > this.imageEl.OriginHeight
30717 onRotateLeft : function(e)
30719 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30721 var minScale = this.thumbEl.getWidth() / this.minWidth;
30723 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30724 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30726 this.startScale = this.scale;
30728 while (this.getScaleLevel() < minScale){
30730 this.scale = this.scale + 1;
30732 if(!this.zoomable()){
30737 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30738 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30743 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30750 this.scale = this.startScale;
30752 this.onRotateFail();
30757 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30759 if(this.isDocument){
30760 this.setThumbBoxSize();
30761 this.setThumbBoxPosition();
30762 this.setCanvasPosition();
30767 this.fireEvent('rotate', this, 'left');
30771 onRotateRight : function(e)
30773 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30775 var minScale = this.thumbEl.getWidth() / this.minWidth;
30777 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30778 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30780 this.startScale = this.scale;
30782 while (this.getScaleLevel() < minScale){
30784 this.scale = this.scale + 1;
30786 if(!this.zoomable()){
30791 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30792 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30797 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30804 this.scale = this.startScale;
30806 this.onRotateFail();
30811 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30813 if(this.isDocument){
30814 this.setThumbBoxSize();
30815 this.setThumbBoxPosition();
30816 this.setCanvasPosition();
30821 this.fireEvent('rotate', this, 'right');
30824 onRotateFail : function()
30826 this.errorEl.show(true);
30830 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30835 this.previewEl.dom.innerHTML = '';
30837 var canvasEl = document.createElement("canvas");
30839 var contextEl = canvasEl.getContext("2d");
30841 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30842 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30843 var center = this.imageEl.OriginWidth / 2;
30845 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30846 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30847 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30848 center = this.imageEl.OriginHeight / 2;
30851 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30853 contextEl.translate(center, center);
30854 contextEl.rotate(this.rotate * Math.PI / 180);
30856 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30858 this.canvasEl = document.createElement("canvas");
30860 this.contextEl = this.canvasEl.getContext("2d");
30862 switch (this.rotate) {
30865 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30866 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30868 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30873 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30874 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30876 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30877 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);
30881 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30886 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30887 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30889 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30890 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);
30894 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);
30899 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30900 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30902 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30903 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30907 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);
30914 this.previewEl.appendChild(this.canvasEl);
30916 this.setCanvasPosition();
30921 if(!this.canvasLoaded){
30925 var imageCanvas = document.createElement("canvas");
30927 var imageContext = imageCanvas.getContext("2d");
30929 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30930 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30932 var center = imageCanvas.width / 2;
30934 imageContext.translate(center, center);
30936 imageContext.rotate(this.rotate * Math.PI / 180);
30938 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30940 var canvas = document.createElement("canvas");
30942 var context = canvas.getContext("2d");
30944 canvas.width = this.minWidth;
30945 canvas.height = this.minHeight;
30947 switch (this.rotate) {
30950 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30951 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30953 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30954 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30956 var targetWidth = this.minWidth - 2 * x;
30957 var targetHeight = this.minHeight - 2 * y;
30961 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30962 scale = targetWidth / width;
30965 if(x > 0 && y == 0){
30966 scale = targetHeight / height;
30969 if(x > 0 && y > 0){
30970 scale = targetWidth / width;
30972 if(width < height){
30973 scale = targetHeight / height;
30977 context.scale(scale, scale);
30979 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30980 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30982 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30983 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30985 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30990 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30991 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30993 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30994 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30996 var targetWidth = this.minWidth - 2 * x;
30997 var targetHeight = this.minHeight - 2 * y;
31001 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31002 scale = targetWidth / width;
31005 if(x > 0 && y == 0){
31006 scale = targetHeight / height;
31009 if(x > 0 && y > 0){
31010 scale = targetWidth / width;
31012 if(width < height){
31013 scale = targetHeight / height;
31017 context.scale(scale, scale);
31019 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31020 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31022 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31023 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31025 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31027 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31032 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31033 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31035 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31036 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31038 var targetWidth = this.minWidth - 2 * x;
31039 var targetHeight = this.minHeight - 2 * y;
31043 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31044 scale = targetWidth / width;
31047 if(x > 0 && y == 0){
31048 scale = targetHeight / height;
31051 if(x > 0 && y > 0){
31052 scale = targetWidth / width;
31054 if(width < height){
31055 scale = targetHeight / height;
31059 context.scale(scale, scale);
31061 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31062 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31064 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31065 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31067 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31068 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31070 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31075 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31076 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31078 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31079 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31081 var targetWidth = this.minWidth - 2 * x;
31082 var targetHeight = this.minHeight - 2 * y;
31086 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31087 scale = targetWidth / width;
31090 if(x > 0 && y == 0){
31091 scale = targetHeight / height;
31094 if(x > 0 && y > 0){
31095 scale = targetWidth / width;
31097 if(width < height){
31098 scale = targetHeight / height;
31102 context.scale(scale, scale);
31104 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31105 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31107 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31108 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31110 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31112 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31119 this.cropData = canvas.toDataURL(this.cropType);
31121 if(this.fireEvent('crop', this, this.cropData) !== false){
31122 this.process(this.file, this.cropData);
31129 setThumbBoxSize : function()
31133 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31134 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31135 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31137 this.minWidth = width;
31138 this.minHeight = height;
31140 if(this.rotate == 90 || this.rotate == 270){
31141 this.minWidth = height;
31142 this.minHeight = width;
31147 width = Math.ceil(this.minWidth * height / this.minHeight);
31149 if(this.minWidth > this.minHeight){
31151 height = Math.ceil(this.minHeight * width / this.minWidth);
31154 this.thumbEl.setStyle({
31155 width : width + 'px',
31156 height : height + 'px'
31163 setThumbBoxPosition : function()
31165 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31166 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31168 this.thumbEl.setLeft(x);
31169 this.thumbEl.setTop(y);
31173 baseRotateLevel : function()
31175 this.baseRotate = 1;
31178 typeof(this.exif) != 'undefined' &&
31179 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31180 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31182 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31185 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31189 baseScaleLevel : function()
31193 if(this.isDocument){
31195 if(this.baseRotate == 6 || this.baseRotate == 8){
31197 height = this.thumbEl.getHeight();
31198 this.baseScale = height / this.imageEl.OriginWidth;
31200 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31201 width = this.thumbEl.getWidth();
31202 this.baseScale = width / this.imageEl.OriginHeight;
31208 height = this.thumbEl.getHeight();
31209 this.baseScale = height / this.imageEl.OriginHeight;
31211 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31212 width = this.thumbEl.getWidth();
31213 this.baseScale = width / this.imageEl.OriginWidth;
31219 if(this.baseRotate == 6 || this.baseRotate == 8){
31221 width = this.thumbEl.getHeight();
31222 this.baseScale = width / this.imageEl.OriginHeight;
31224 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31225 height = this.thumbEl.getWidth();
31226 this.baseScale = height / this.imageEl.OriginHeight;
31229 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31230 height = this.thumbEl.getWidth();
31231 this.baseScale = height / this.imageEl.OriginHeight;
31233 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31234 width = this.thumbEl.getHeight();
31235 this.baseScale = width / this.imageEl.OriginWidth;
31242 width = this.thumbEl.getWidth();
31243 this.baseScale = width / this.imageEl.OriginWidth;
31245 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31246 height = this.thumbEl.getHeight();
31247 this.baseScale = height / this.imageEl.OriginHeight;
31250 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31252 height = this.thumbEl.getHeight();
31253 this.baseScale = height / this.imageEl.OriginHeight;
31255 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31256 width = this.thumbEl.getWidth();
31257 this.baseScale = width / this.imageEl.OriginWidth;
31265 getScaleLevel : function()
31267 return this.baseScale * Math.pow(1.1, this.scale);
31270 onTouchStart : function(e)
31272 if(!this.canvasLoaded){
31273 this.beforeSelectFile(e);
31277 var touches = e.browserEvent.touches;
31283 if(touches.length == 1){
31284 this.onMouseDown(e);
31288 if(touches.length != 2){
31294 for(var i = 0, finger; finger = touches[i]; i++){
31295 coords.push(finger.pageX, finger.pageY);
31298 var x = Math.pow(coords[0] - coords[2], 2);
31299 var y = Math.pow(coords[1] - coords[3], 2);
31301 this.startDistance = Math.sqrt(x + y);
31303 this.startScale = this.scale;
31305 this.pinching = true;
31306 this.dragable = false;
31310 onTouchMove : function(e)
31312 if(!this.pinching && !this.dragable){
31316 var touches = e.browserEvent.touches;
31323 this.onMouseMove(e);
31329 for(var i = 0, finger; finger = touches[i]; i++){
31330 coords.push(finger.pageX, finger.pageY);
31333 var x = Math.pow(coords[0] - coords[2], 2);
31334 var y = Math.pow(coords[1] - coords[3], 2);
31336 this.endDistance = Math.sqrt(x + y);
31338 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31340 if(!this.zoomable()){
31341 this.scale = this.startScale;
31349 onTouchEnd : function(e)
31351 this.pinching = false;
31352 this.dragable = false;
31356 process : function(file, crop)
31359 this.maskEl.mask(this.loadingText);
31362 this.xhr = new XMLHttpRequest();
31364 file.xhr = this.xhr;
31366 this.xhr.open(this.method, this.url, true);
31369 "Accept": "application/json",
31370 "Cache-Control": "no-cache",
31371 "X-Requested-With": "XMLHttpRequest"
31374 for (var headerName in headers) {
31375 var headerValue = headers[headerName];
31377 this.xhr.setRequestHeader(headerName, headerValue);
31383 this.xhr.onload = function()
31385 _this.xhrOnLoad(_this.xhr);
31388 this.xhr.onerror = function()
31390 _this.xhrOnError(_this.xhr);
31393 var formData = new FormData();
31395 formData.append('returnHTML', 'NO');
31398 formData.append('crop', crop);
31401 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31402 formData.append(this.paramName, file, file.name);
31405 if(typeof(file.filename) != 'undefined'){
31406 formData.append('filename', file.filename);
31409 if(typeof(file.mimetype) != 'undefined'){
31410 formData.append('mimetype', file.mimetype);
31413 if(this.fireEvent('arrange', this, formData) != false){
31414 this.xhr.send(formData);
31418 xhrOnLoad : function(xhr)
31421 this.maskEl.unmask();
31424 if (xhr.readyState !== 4) {
31425 this.fireEvent('exception', this, xhr);
31429 var response = Roo.decode(xhr.responseText);
31431 if(!response.success){
31432 this.fireEvent('exception', this, xhr);
31436 var response = Roo.decode(xhr.responseText);
31438 this.fireEvent('upload', this, response);
31442 xhrOnError : function()
31445 this.maskEl.unmask();
31448 Roo.log('xhr on error');
31450 var response = Roo.decode(xhr.responseText);
31456 prepare : function(file)
31459 this.maskEl.mask(this.loadingText);
31465 if(typeof(file) === 'string'){
31466 this.loadCanvas(file);
31470 if(!file || !this.urlAPI){
31475 this.cropType = file.type;
31479 if(this.fireEvent('prepare', this, this.file) != false){
31481 var reader = new FileReader();
31483 reader.onload = function (e) {
31484 if (e.target.error) {
31485 Roo.log(e.target.error);
31489 var buffer = e.target.result,
31490 dataView = new DataView(buffer),
31492 maxOffset = dataView.byteLength - 4,
31496 if (dataView.getUint16(0) === 0xffd8) {
31497 while (offset < maxOffset) {
31498 markerBytes = dataView.getUint16(offset);
31500 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31501 markerLength = dataView.getUint16(offset + 2) + 2;
31502 if (offset + markerLength > dataView.byteLength) {
31503 Roo.log('Invalid meta data: Invalid segment size.');
31507 if(markerBytes == 0xffe1){
31508 _this.parseExifData(
31515 offset += markerLength;
31525 var url = _this.urlAPI.createObjectURL(_this.file);
31527 _this.loadCanvas(url);
31532 reader.readAsArrayBuffer(this.file);
31538 parseExifData : function(dataView, offset, length)
31540 var tiffOffset = offset + 10,
31544 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31545 // No Exif data, might be XMP data instead
31549 // Check for the ASCII code for "Exif" (0x45786966):
31550 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31551 // No Exif data, might be XMP data instead
31554 if (tiffOffset + 8 > dataView.byteLength) {
31555 Roo.log('Invalid Exif data: Invalid segment size.');
31558 // Check for the two null bytes:
31559 if (dataView.getUint16(offset + 8) !== 0x0000) {
31560 Roo.log('Invalid Exif data: Missing byte alignment offset.');
31563 // Check the byte alignment:
31564 switch (dataView.getUint16(tiffOffset)) {
31566 littleEndian = true;
31569 littleEndian = false;
31572 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31575 // Check for the TIFF tag marker (0x002A):
31576 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31577 Roo.log('Invalid Exif data: Missing TIFF marker.');
31580 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31581 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31583 this.parseExifTags(
31586 tiffOffset + dirOffset,
31591 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31596 if (dirOffset + 6 > dataView.byteLength) {
31597 Roo.log('Invalid Exif data: Invalid directory offset.');
31600 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31601 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31602 if (dirEndOffset + 4 > dataView.byteLength) {
31603 Roo.log('Invalid Exif data: Invalid directory size.');
31606 for (i = 0; i < tagsNumber; i += 1) {
31610 dirOffset + 2 + 12 * i, // tag offset
31614 // Return the offset to the next directory:
31615 return dataView.getUint32(dirEndOffset, littleEndian);
31618 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
31620 var tag = dataView.getUint16(offset, littleEndian);
31622 this.exif[tag] = this.getExifValue(
31626 dataView.getUint16(offset + 2, littleEndian), // tag type
31627 dataView.getUint32(offset + 4, littleEndian), // tag length
31632 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31634 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31643 Roo.log('Invalid Exif data: Invalid tag type.');
31647 tagSize = tagType.size * length;
31648 // Determine if the value is contained in the dataOffset bytes,
31649 // or if the value at the dataOffset is a pointer to the actual data:
31650 dataOffset = tagSize > 4 ?
31651 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31652 if (dataOffset + tagSize > dataView.byteLength) {
31653 Roo.log('Invalid Exif data: Invalid data offset.');
31656 if (length === 1) {
31657 return tagType.getValue(dataView, dataOffset, littleEndian);
31660 for (i = 0; i < length; i += 1) {
31661 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31664 if (tagType.ascii) {
31666 // Concatenate the chars:
31667 for (i = 0; i < values.length; i += 1) {
31669 // Ignore the terminating NULL byte(s):
31670 if (c === '\u0000') {
31682 Roo.apply(Roo.bootstrap.UploadCropbox, {
31684 'Orientation': 0x0112
31688 1: 0, //'top-left',
31690 3: 180, //'bottom-right',
31691 // 4: 'bottom-left',
31693 6: 90, //'right-top',
31694 // 7: 'right-bottom',
31695 8: 270 //'left-bottom'
31699 // byte, 8-bit unsigned int:
31701 getValue: function (dataView, dataOffset) {
31702 return dataView.getUint8(dataOffset);
31706 // ascii, 8-bit byte:
31708 getValue: function (dataView, dataOffset) {
31709 return String.fromCharCode(dataView.getUint8(dataOffset));
31714 // short, 16 bit int:
31716 getValue: function (dataView, dataOffset, littleEndian) {
31717 return dataView.getUint16(dataOffset, littleEndian);
31721 // long, 32 bit int:
31723 getValue: function (dataView, dataOffset, littleEndian) {
31724 return dataView.getUint32(dataOffset, littleEndian);
31728 // rational = two long values, first is numerator, second is denominator:
31730 getValue: function (dataView, dataOffset, littleEndian) {
31731 return dataView.getUint32(dataOffset, littleEndian) /
31732 dataView.getUint32(dataOffset + 4, littleEndian);
31736 // slong, 32 bit signed int:
31738 getValue: function (dataView, dataOffset, littleEndian) {
31739 return dataView.getInt32(dataOffset, littleEndian);
31743 // srational, two slongs, first is numerator, second is denominator:
31745 getValue: function (dataView, dataOffset, littleEndian) {
31746 return dataView.getInt32(dataOffset, littleEndian) /
31747 dataView.getInt32(dataOffset + 4, littleEndian);
31757 cls : 'btn-group roo-upload-cropbox-rotate-left',
31758 action : 'rotate-left',
31762 cls : 'btn btn-default',
31763 html : '<i class="fa fa-undo"></i>'
31769 cls : 'btn-group roo-upload-cropbox-picture',
31770 action : 'picture',
31774 cls : 'btn btn-default',
31775 html : '<i class="fa fa-picture-o"></i>'
31781 cls : 'btn-group roo-upload-cropbox-rotate-right',
31782 action : 'rotate-right',
31786 cls : 'btn btn-default',
31787 html : '<i class="fa fa-repeat"></i>'
31795 cls : 'btn-group roo-upload-cropbox-rotate-left',
31796 action : 'rotate-left',
31800 cls : 'btn btn-default',
31801 html : '<i class="fa fa-undo"></i>'
31807 cls : 'btn-group roo-upload-cropbox-download',
31808 action : 'download',
31812 cls : 'btn btn-default',
31813 html : '<i class="fa fa-download"></i>'
31819 cls : 'btn-group roo-upload-cropbox-crop',
31824 cls : 'btn btn-default',
31825 html : '<i class="fa fa-crop"></i>'
31831 cls : 'btn-group roo-upload-cropbox-trash',
31836 cls : 'btn btn-default',
31837 html : '<i class="fa fa-trash"></i>'
31843 cls : 'btn-group roo-upload-cropbox-rotate-right',
31844 action : 'rotate-right',
31848 cls : 'btn btn-default',
31849 html : '<i class="fa fa-repeat"></i>'
31857 cls : 'btn-group roo-upload-cropbox-rotate-left',
31858 action : 'rotate-left',
31862 cls : 'btn btn-default',
31863 html : '<i class="fa fa-undo"></i>'
31869 cls : 'btn-group roo-upload-cropbox-rotate-right',
31870 action : 'rotate-right',
31874 cls : 'btn btn-default',
31875 html : '<i class="fa fa-repeat"></i>'
31888 * @class Roo.bootstrap.DocumentManager
31889 * @extends Roo.bootstrap.Component
31890 * Bootstrap DocumentManager class
31891 * @cfg {String} paramName default 'imageUpload'
31892 * @cfg {String} toolTipName default 'filename'
31893 * @cfg {String} method default POST
31894 * @cfg {String} url action url
31895 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31896 * @cfg {Boolean} multiple multiple upload default true
31897 * @cfg {Number} thumbSize default 300
31898 * @cfg {String} fieldLabel
31899 * @cfg {Number} labelWidth default 4
31900 * @cfg {String} labelAlign (left|top) default left
31901 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31902 * @cfg {Number} labellg set the width of label (1-12)
31903 * @cfg {Number} labelmd set the width of label (1-12)
31904 * @cfg {Number} labelsm set the width of label (1-12)
31905 * @cfg {Number} labelxs set the width of label (1-12)
31908 * Create a new DocumentManager
31909 * @param {Object} config The config object
31912 Roo.bootstrap.DocumentManager = function(config){
31913 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31916 this.delegates = [];
31921 * Fire when initial the DocumentManager
31922 * @param {Roo.bootstrap.DocumentManager} this
31927 * inspect selected file
31928 * @param {Roo.bootstrap.DocumentManager} this
31929 * @param {File} file
31934 * Fire when xhr load exception
31935 * @param {Roo.bootstrap.DocumentManager} this
31936 * @param {XMLHttpRequest} xhr
31938 "exception" : true,
31940 * @event afterupload
31941 * Fire when xhr load exception
31942 * @param {Roo.bootstrap.DocumentManager} this
31943 * @param {XMLHttpRequest} xhr
31945 "afterupload" : true,
31948 * prepare the form data
31949 * @param {Roo.bootstrap.DocumentManager} this
31950 * @param {Object} formData
31955 * Fire when remove the file
31956 * @param {Roo.bootstrap.DocumentManager} this
31957 * @param {Object} file
31962 * Fire after refresh the file
31963 * @param {Roo.bootstrap.DocumentManager} this
31968 * Fire after click the image
31969 * @param {Roo.bootstrap.DocumentManager} this
31970 * @param {Object} file
31975 * Fire when upload a image and editable set to true
31976 * @param {Roo.bootstrap.DocumentManager} this
31977 * @param {Object} file
31981 * @event beforeselectfile
31982 * Fire before select file
31983 * @param {Roo.bootstrap.DocumentManager} this
31985 "beforeselectfile" : true,
31988 * Fire before process file
31989 * @param {Roo.bootstrap.DocumentManager} this
31990 * @param {Object} file
31994 * @event previewrendered
31995 * Fire when preview rendered
31996 * @param {Roo.bootstrap.DocumentManager} this
31997 * @param {Object} file
31999 "previewrendered" : true,
32002 "previewResize" : true
32007 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
32016 paramName : 'imageUpload',
32017 toolTipName : 'filename',
32020 labelAlign : 'left',
32030 getAutoCreate : function()
32032 var managerWidget = {
32034 cls : 'roo-document-manager',
32038 cls : 'roo-document-manager-selector',
32043 cls : 'roo-document-manager-uploader',
32047 cls : 'roo-document-manager-upload-btn',
32048 html : '<i class="fa fa-plus"></i>'
32059 cls : 'column col-md-12',
32064 if(this.fieldLabel.length){
32069 cls : 'column col-md-12',
32070 html : this.fieldLabel
32074 cls : 'column col-md-12',
32079 if(this.labelAlign == 'left'){
32084 html : this.fieldLabel
32093 if(this.labelWidth > 12){
32094 content[0].style = "width: " + this.labelWidth + 'px';
32097 if(this.labelWidth < 13 && this.labelmd == 0){
32098 this.labelmd = this.labelWidth;
32101 if(this.labellg > 0){
32102 content[0].cls += ' col-lg-' + this.labellg;
32103 content[1].cls += ' col-lg-' + (12 - this.labellg);
32106 if(this.labelmd > 0){
32107 content[0].cls += ' col-md-' + this.labelmd;
32108 content[1].cls += ' col-md-' + (12 - this.labelmd);
32111 if(this.labelsm > 0){
32112 content[0].cls += ' col-sm-' + this.labelsm;
32113 content[1].cls += ' col-sm-' + (12 - this.labelsm);
32116 if(this.labelxs > 0){
32117 content[0].cls += ' col-xs-' + this.labelxs;
32118 content[1].cls += ' col-xs-' + (12 - this.labelxs);
32126 cls : 'row clearfix',
32134 initEvents : function()
32136 this.managerEl = this.el.select('.roo-document-manager', true).first();
32137 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32139 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32140 this.selectorEl.hide();
32143 this.selectorEl.attr('multiple', 'multiple');
32146 this.selectorEl.on('change', this.onFileSelected, this);
32148 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32149 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32151 this.uploader.on('click', this.onUploaderClick, this);
32153 this.renderProgressDialog();
32157 window.addEventListener("resize", function() { _this.refresh(); } );
32159 this.fireEvent('initial', this);
32162 renderProgressDialog : function()
32166 this.progressDialog = new Roo.bootstrap.Modal({
32167 cls : 'roo-document-manager-progress-dialog',
32168 allow_close : false,
32179 btnclick : function() {
32180 _this.uploadCancel();
32186 this.progressDialog.render(Roo.get(document.body));
32188 this.progress = new Roo.bootstrap.Progress({
32189 cls : 'roo-document-manager-progress',
32194 this.progress.render(this.progressDialog.getChildContainer());
32196 this.progressBar = new Roo.bootstrap.ProgressBar({
32197 cls : 'roo-document-manager-progress-bar',
32200 aria_valuemax : 12,
32204 this.progressBar.render(this.progress.getChildContainer());
32207 onUploaderClick : function(e)
32209 e.preventDefault();
32211 if(this.fireEvent('beforeselectfile', this) != false){
32212 this.selectorEl.dom.click();
32217 onFileSelected : function(e)
32219 e.preventDefault();
32221 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32225 Roo.each(this.selectorEl.dom.files, function(file){
32226 if(this.fireEvent('inspect', this, file) != false){
32227 this.files.push(file);
32237 this.selectorEl.dom.value = '';
32239 if(!this.files || !this.files.length){
32243 if(this.boxes > 0 && this.files.length > this.boxes){
32244 this.files = this.files.slice(0, this.boxes);
32247 this.uploader.show();
32249 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32250 this.uploader.hide();
32259 Roo.each(this.files, function(file){
32261 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32262 var f = this.renderPreview(file);
32267 if(file.type.indexOf('image') != -1){
32268 this.delegates.push(
32270 _this.process(file);
32271 }).createDelegate(this)
32279 _this.process(file);
32280 }).createDelegate(this)
32285 this.files = files;
32287 this.delegates = this.delegates.concat(docs);
32289 if(!this.delegates.length){
32294 this.progressBar.aria_valuemax = this.delegates.length;
32301 arrange : function()
32303 if(!this.delegates.length){
32304 this.progressDialog.hide();
32309 var delegate = this.delegates.shift();
32311 this.progressDialog.show();
32313 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32315 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32320 refresh : function()
32322 this.uploader.show();
32324 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32325 this.uploader.hide();
32328 Roo.isTouch ? this.closable(false) : this.closable(true);
32330 this.fireEvent('refresh', this);
32333 onRemove : function(e, el, o)
32335 e.preventDefault();
32337 this.fireEvent('remove', this, o);
32341 remove : function(o)
32345 Roo.each(this.files, function(file){
32346 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32355 this.files = files;
32362 Roo.each(this.files, function(file){
32367 file.target.remove();
32376 onClick : function(e, el, o)
32378 e.preventDefault();
32380 this.fireEvent('click', this, o);
32384 closable : function(closable)
32386 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32388 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32400 xhrOnLoad : function(xhr)
32402 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32406 if (xhr.readyState !== 4) {
32408 this.fireEvent('exception', this, xhr);
32412 var response = Roo.decode(xhr.responseText);
32414 if(!response.success){
32416 this.fireEvent('exception', this, xhr);
32420 var file = this.renderPreview(response.data);
32422 this.files.push(file);
32426 this.fireEvent('afterupload', this, xhr);
32430 xhrOnError : function(xhr)
32432 Roo.log('xhr on error');
32434 var response = Roo.decode(xhr.responseText);
32441 process : function(file)
32443 if(this.fireEvent('process', this, file) !== false){
32444 if(this.editable && file.type.indexOf('image') != -1){
32445 this.fireEvent('edit', this, file);
32449 this.uploadStart(file, false);
32456 uploadStart : function(file, crop)
32458 this.xhr = new XMLHttpRequest();
32460 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32465 file.xhr = this.xhr;
32467 this.managerEl.createChild({
32469 cls : 'roo-document-manager-loading',
32473 tooltip : file.name,
32474 cls : 'roo-document-manager-thumb',
32475 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32481 this.xhr.open(this.method, this.url, true);
32484 "Accept": "application/json",
32485 "Cache-Control": "no-cache",
32486 "X-Requested-With": "XMLHttpRequest"
32489 for (var headerName in headers) {
32490 var headerValue = headers[headerName];
32492 this.xhr.setRequestHeader(headerName, headerValue);
32498 this.xhr.onload = function()
32500 _this.xhrOnLoad(_this.xhr);
32503 this.xhr.onerror = function()
32505 _this.xhrOnError(_this.xhr);
32508 var formData = new FormData();
32510 formData.append('returnHTML', 'NO');
32513 formData.append('crop', crop);
32516 formData.append(this.paramName, file, file.name);
32523 if(this.fireEvent('prepare', this, formData, options) != false){
32525 if(options.manually){
32529 this.xhr.send(formData);
32533 this.uploadCancel();
32536 uploadCancel : function()
32542 this.delegates = [];
32544 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32551 renderPreview : function(file)
32553 if(typeof(file.target) != 'undefined' && file.target){
32557 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32559 var previewEl = this.managerEl.createChild({
32561 cls : 'roo-document-manager-preview',
32565 tooltip : file[this.toolTipName],
32566 cls : 'roo-document-manager-thumb',
32567 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32572 html : '<i class="fa fa-times-circle"></i>'
32577 var close = previewEl.select('button.close', true).first();
32579 close.on('click', this.onRemove, this, file);
32581 file.target = previewEl;
32583 var image = previewEl.select('img', true).first();
32587 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32589 image.on('click', this.onClick, this, file);
32591 this.fireEvent('previewrendered', this, file);
32597 onPreviewLoad : function(file, image)
32599 if(typeof(file.target) == 'undefined' || !file.target){
32603 var width = image.dom.naturalWidth || image.dom.width;
32604 var height = image.dom.naturalHeight || image.dom.height;
32606 if(!this.previewResize) {
32610 if(width > height){
32611 file.target.addClass('wide');
32615 file.target.addClass('tall');
32620 uploadFromSource : function(file, crop)
32622 this.xhr = new XMLHttpRequest();
32624 this.managerEl.createChild({
32626 cls : 'roo-document-manager-loading',
32630 tooltip : file.name,
32631 cls : 'roo-document-manager-thumb',
32632 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32638 this.xhr.open(this.method, this.url, true);
32641 "Accept": "application/json",
32642 "Cache-Control": "no-cache",
32643 "X-Requested-With": "XMLHttpRequest"
32646 for (var headerName in headers) {
32647 var headerValue = headers[headerName];
32649 this.xhr.setRequestHeader(headerName, headerValue);
32655 this.xhr.onload = function()
32657 _this.xhrOnLoad(_this.xhr);
32660 this.xhr.onerror = function()
32662 _this.xhrOnError(_this.xhr);
32665 var formData = new FormData();
32667 formData.append('returnHTML', 'NO');
32669 formData.append('crop', crop);
32671 if(typeof(file.filename) != 'undefined'){
32672 formData.append('filename', file.filename);
32675 if(typeof(file.mimetype) != 'undefined'){
32676 formData.append('mimetype', file.mimetype);
32681 if(this.fireEvent('prepare', this, formData) != false){
32682 this.xhr.send(formData);
32692 * @class Roo.bootstrap.DocumentViewer
32693 * @extends Roo.bootstrap.Component
32694 * Bootstrap DocumentViewer class
32695 * @cfg {Boolean} showDownload (true|false) show download button (default true)
32696 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32699 * Create a new DocumentViewer
32700 * @param {Object} config The config object
32703 Roo.bootstrap.DocumentViewer = function(config){
32704 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32709 * Fire after initEvent
32710 * @param {Roo.bootstrap.DocumentViewer} this
32716 * @param {Roo.bootstrap.DocumentViewer} this
32721 * Fire after download button
32722 * @param {Roo.bootstrap.DocumentViewer} this
32727 * Fire after trash button
32728 * @param {Roo.bootstrap.DocumentViewer} this
32735 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
32737 showDownload : true,
32741 getAutoCreate : function()
32745 cls : 'roo-document-viewer',
32749 cls : 'roo-document-viewer-body',
32753 cls : 'roo-document-viewer-thumb',
32757 cls : 'roo-document-viewer-image'
32765 cls : 'roo-document-viewer-footer',
32768 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32772 cls : 'btn-group roo-document-viewer-download',
32776 cls : 'btn btn-default',
32777 html : '<i class="fa fa-download"></i>'
32783 cls : 'btn-group roo-document-viewer-trash',
32787 cls : 'btn btn-default',
32788 html : '<i class="fa fa-trash"></i>'
32801 initEvents : function()
32803 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32804 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32806 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32807 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32809 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32810 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32812 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32813 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32815 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32816 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32818 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32819 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32821 this.bodyEl.on('click', this.onClick, this);
32822 this.downloadBtn.on('click', this.onDownload, this);
32823 this.trashBtn.on('click', this.onTrash, this);
32825 this.downloadBtn.hide();
32826 this.trashBtn.hide();
32828 if(this.showDownload){
32829 this.downloadBtn.show();
32832 if(this.showTrash){
32833 this.trashBtn.show();
32836 if(!this.showDownload && !this.showTrash) {
32837 this.footerEl.hide();
32842 initial : function()
32844 this.fireEvent('initial', this);
32848 onClick : function(e)
32850 e.preventDefault();
32852 this.fireEvent('click', this);
32855 onDownload : function(e)
32857 e.preventDefault();
32859 this.fireEvent('download', this);
32862 onTrash : function(e)
32864 e.preventDefault();
32866 this.fireEvent('trash', this);
32878 * @class Roo.bootstrap.NavProgressBar
32879 * @extends Roo.bootstrap.Component
32880 * Bootstrap NavProgressBar class
32883 * Create a new nav progress bar
32884 * @param {Object} config The config object
32887 Roo.bootstrap.NavProgressBar = function(config){
32888 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32890 this.bullets = this.bullets || [];
32892 // Roo.bootstrap.NavProgressBar.register(this);
32896 * Fires when the active item changes
32897 * @param {Roo.bootstrap.NavProgressBar} this
32898 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32899 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
32906 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
32911 getAutoCreate : function()
32913 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32917 cls : 'roo-navigation-bar-group',
32921 cls : 'roo-navigation-top-bar'
32925 cls : 'roo-navigation-bullets-bar',
32929 cls : 'roo-navigation-bar'
32936 cls : 'roo-navigation-bottom-bar'
32946 initEvents: function()
32951 onRender : function(ct, position)
32953 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32955 if(this.bullets.length){
32956 Roo.each(this.bullets, function(b){
32965 addItem : function(cfg)
32967 var item = new Roo.bootstrap.NavProgressItem(cfg);
32969 item.parentId = this.id;
32970 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32973 var top = new Roo.bootstrap.Element({
32975 cls : 'roo-navigation-bar-text'
32978 var bottom = new Roo.bootstrap.Element({
32980 cls : 'roo-navigation-bar-text'
32983 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32984 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32986 var topText = new Roo.bootstrap.Element({
32988 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32991 var bottomText = new Roo.bootstrap.Element({
32993 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32996 topText.onRender(top.el, null);
32997 bottomText.onRender(bottom.el, null);
33000 item.bottomEl = bottom;
33003 this.barItems.push(item);
33008 getActive : function()
33010 var active = false;
33012 Roo.each(this.barItems, function(v){
33014 if (!v.isActive()) {
33026 setActiveItem : function(item)
33030 Roo.each(this.barItems, function(v){
33031 if (v.rid == item.rid) {
33035 if (v.isActive()) {
33036 v.setActive(false);
33041 item.setActive(true);
33043 this.fireEvent('changed', this, item, prev);
33046 getBarItem: function(rid)
33050 Roo.each(this.barItems, function(e) {
33051 if (e.rid != rid) {
33062 indexOfItem : function(item)
33066 Roo.each(this.barItems, function(v, i){
33068 if (v.rid != item.rid) {
33079 setActiveNext : function()
33081 var i = this.indexOfItem(this.getActive());
33083 if (i > this.barItems.length) {
33087 this.setActiveItem(this.barItems[i+1]);
33090 setActivePrev : function()
33092 var i = this.indexOfItem(this.getActive());
33098 this.setActiveItem(this.barItems[i-1]);
33101 format : function()
33103 if(!this.barItems.length){
33107 var width = 100 / this.barItems.length;
33109 Roo.each(this.barItems, function(i){
33110 i.el.setStyle('width', width + '%');
33111 i.topEl.el.setStyle('width', width + '%');
33112 i.bottomEl.el.setStyle('width', width + '%');
33121 * Nav Progress Item
33126 * @class Roo.bootstrap.NavProgressItem
33127 * @extends Roo.bootstrap.Component
33128 * Bootstrap NavProgressItem class
33129 * @cfg {String} rid the reference id
33130 * @cfg {Boolean} active (true|false) Is item active default false
33131 * @cfg {Boolean} disabled (true|false) Is item active default false
33132 * @cfg {String} html
33133 * @cfg {String} position (top|bottom) text position default bottom
33134 * @cfg {String} icon show icon instead of number
33137 * Create a new NavProgressItem
33138 * @param {Object} config The config object
33140 Roo.bootstrap.NavProgressItem = function(config){
33141 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33146 * The raw click event for the entire grid.
33147 * @param {Roo.bootstrap.NavProgressItem} this
33148 * @param {Roo.EventObject} e
33155 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
33161 position : 'bottom',
33164 getAutoCreate : function()
33166 var iconCls = 'roo-navigation-bar-item-icon';
33168 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33172 cls: 'roo-navigation-bar-item',
33182 cfg.cls += ' active';
33185 cfg.cls += ' disabled';
33191 disable : function()
33193 this.setDisabled(true);
33196 enable : function()
33198 this.setDisabled(false);
33201 initEvents: function()
33203 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33205 this.iconEl.on('click', this.onClick, this);
33208 onClick : function(e)
33210 e.preventDefault();
33216 if(this.fireEvent('click', this, e) === false){
33220 this.parent().setActiveItem(this);
33223 isActive: function ()
33225 return this.active;
33228 setActive : function(state)
33230 if(this.active == state){
33234 this.active = state;
33237 this.el.addClass('active');
33241 this.el.removeClass('active');
33246 setDisabled : function(state)
33248 if(this.disabled == state){
33252 this.disabled = state;
33255 this.el.addClass('disabled');
33259 this.el.removeClass('disabled');
33262 tooltipEl : function()
33264 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33277 * @class Roo.bootstrap.FieldLabel
33278 * @extends Roo.bootstrap.Component
33279 * Bootstrap FieldLabel class
33280 * @cfg {String} html contents of the element
33281 * @cfg {String} tag tag of the element default label
33282 * @cfg {String} cls class of the element
33283 * @cfg {String} target label target
33284 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33285 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33286 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33287 * @cfg {String} iconTooltip default "This field is required"
33288 * @cfg {String} indicatorpos (left|right) default left
33291 * Create a new FieldLabel
33292 * @param {Object} config The config object
33295 Roo.bootstrap.FieldLabel = function(config){
33296 Roo.bootstrap.Element.superclass.constructor.call(this, config);
33301 * Fires after the field has been marked as invalid.
33302 * @param {Roo.form.FieldLabel} this
33303 * @param {String} msg The validation message
33308 * Fires after the field has been validated with no errors.
33309 * @param {Roo.form.FieldLabel} this
33315 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
33322 invalidClass : 'has-warning',
33323 validClass : 'has-success',
33324 iconTooltip : 'This field is required',
33325 indicatorpos : 'left',
33327 getAutoCreate : function(){
33330 if (!this.allowBlank) {
33336 cls : 'roo-bootstrap-field-label ' + this.cls,
33341 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33342 tooltip : this.iconTooltip
33351 if(this.indicatorpos == 'right'){
33354 cls : 'roo-bootstrap-field-label ' + this.cls,
33363 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33364 tooltip : this.iconTooltip
33373 initEvents: function()
33375 Roo.bootstrap.Element.superclass.initEvents.call(this);
33377 this.indicator = this.indicatorEl();
33379 if(this.indicator){
33380 this.indicator.removeClass('visible');
33381 this.indicator.addClass('invisible');
33384 Roo.bootstrap.FieldLabel.register(this);
33387 indicatorEl : function()
33389 var indicator = this.el.select('i.roo-required-indicator',true).first();
33400 * Mark this field as valid
33402 markValid : function()
33404 if(this.indicator){
33405 this.indicator.removeClass('visible');
33406 this.indicator.addClass('invisible');
33408 if (Roo.bootstrap.version == 3) {
33409 this.el.removeClass(this.invalidClass);
33410 this.el.addClass(this.validClass);
33412 this.el.removeClass('is-invalid');
33413 this.el.addClass('is-valid');
33417 this.fireEvent('valid', this);
33421 * Mark this field as invalid
33422 * @param {String} msg The validation message
33424 markInvalid : function(msg)
33426 if(this.indicator){
33427 this.indicator.removeClass('invisible');
33428 this.indicator.addClass('visible');
33430 if (Roo.bootstrap.version == 3) {
33431 this.el.removeClass(this.validClass);
33432 this.el.addClass(this.invalidClass);
33434 this.el.removeClass('is-valid');
33435 this.el.addClass('is-invalid');
33439 this.fireEvent('invalid', this, msg);
33445 Roo.apply(Roo.bootstrap.FieldLabel, {
33450 * register a FieldLabel Group
33451 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33453 register : function(label)
33455 if(this.groups.hasOwnProperty(label.target)){
33459 this.groups[label.target] = label;
33463 * fetch a FieldLabel Group based on the target
33464 * @param {string} target
33465 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33467 get: function(target) {
33468 if (typeof(this.groups[target]) == 'undefined') {
33472 return this.groups[target] ;
33481 * page DateSplitField.
33487 * @class Roo.bootstrap.DateSplitField
33488 * @extends Roo.bootstrap.Component
33489 * Bootstrap DateSplitField class
33490 * @cfg {string} fieldLabel - the label associated
33491 * @cfg {Number} labelWidth set the width of label (0-12)
33492 * @cfg {String} labelAlign (top|left)
33493 * @cfg {Boolean} dayAllowBlank (true|false) default false
33494 * @cfg {Boolean} monthAllowBlank (true|false) default false
33495 * @cfg {Boolean} yearAllowBlank (true|false) default false
33496 * @cfg {string} dayPlaceholder
33497 * @cfg {string} monthPlaceholder
33498 * @cfg {string} yearPlaceholder
33499 * @cfg {string} dayFormat default 'd'
33500 * @cfg {string} monthFormat default 'm'
33501 * @cfg {string} yearFormat default 'Y'
33502 * @cfg {Number} labellg set the width of label (1-12)
33503 * @cfg {Number} labelmd set the width of label (1-12)
33504 * @cfg {Number} labelsm set the width of label (1-12)
33505 * @cfg {Number} labelxs set the width of label (1-12)
33509 * Create a new DateSplitField
33510 * @param {Object} config The config object
33513 Roo.bootstrap.DateSplitField = function(config){
33514 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33520 * getting the data of years
33521 * @param {Roo.bootstrap.DateSplitField} this
33522 * @param {Object} years
33527 * getting the data of days
33528 * @param {Roo.bootstrap.DateSplitField} this
33529 * @param {Object} days
33534 * Fires after the field has been marked as invalid.
33535 * @param {Roo.form.Field} this
33536 * @param {String} msg The validation message
33541 * Fires after the field has been validated with no errors.
33542 * @param {Roo.form.Field} this
33548 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33551 labelAlign : 'top',
33553 dayAllowBlank : false,
33554 monthAllowBlank : false,
33555 yearAllowBlank : false,
33556 dayPlaceholder : '',
33557 monthPlaceholder : '',
33558 yearPlaceholder : '',
33562 isFormField : true,
33568 getAutoCreate : function()
33572 cls : 'row roo-date-split-field-group',
33577 cls : 'form-hidden-field roo-date-split-field-group-value',
33583 var labelCls = 'col-md-12';
33584 var contentCls = 'col-md-4';
33586 if(this.fieldLabel){
33590 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33594 html : this.fieldLabel
33599 if(this.labelAlign == 'left'){
33601 if(this.labelWidth > 12){
33602 label.style = "width: " + this.labelWidth + 'px';
33605 if(this.labelWidth < 13 && this.labelmd == 0){
33606 this.labelmd = this.labelWidth;
33609 if(this.labellg > 0){
33610 labelCls = ' col-lg-' + this.labellg;
33611 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33614 if(this.labelmd > 0){
33615 labelCls = ' col-md-' + this.labelmd;
33616 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33619 if(this.labelsm > 0){
33620 labelCls = ' col-sm-' + this.labelsm;
33621 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33624 if(this.labelxs > 0){
33625 labelCls = ' col-xs-' + this.labelxs;
33626 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33630 label.cls += ' ' + labelCls;
33632 cfg.cn.push(label);
33635 Roo.each(['day', 'month', 'year'], function(t){
33638 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33645 inputEl: function ()
33647 return this.el.select('.roo-date-split-field-group-value', true).first();
33650 onRender : function(ct, position)
33654 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33656 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33658 this.dayField = new Roo.bootstrap.ComboBox({
33659 allowBlank : this.dayAllowBlank,
33660 alwaysQuery : true,
33661 displayField : 'value',
33664 forceSelection : true,
33666 placeholder : this.dayPlaceholder,
33667 selectOnFocus : true,
33668 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33669 triggerAction : 'all',
33671 valueField : 'value',
33672 store : new Roo.data.SimpleStore({
33673 data : (function() {
33675 _this.fireEvent('days', _this, days);
33678 fields : [ 'value' ]
33681 select : function (_self, record, index)
33683 _this.setValue(_this.getValue());
33688 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33690 this.monthField = new Roo.bootstrap.MonthField({
33691 after : '<i class=\"fa fa-calendar\"></i>',
33692 allowBlank : this.monthAllowBlank,
33693 placeholder : this.monthPlaceholder,
33696 render : function (_self)
33698 this.el.select('span.input-group-addon', true).first().on('click', function(e){
33699 e.preventDefault();
33703 select : function (_self, oldvalue, newvalue)
33705 _this.setValue(_this.getValue());
33710 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33712 this.yearField = new Roo.bootstrap.ComboBox({
33713 allowBlank : this.yearAllowBlank,
33714 alwaysQuery : true,
33715 displayField : 'value',
33718 forceSelection : true,
33720 placeholder : this.yearPlaceholder,
33721 selectOnFocus : true,
33722 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33723 triggerAction : 'all',
33725 valueField : 'value',
33726 store : new Roo.data.SimpleStore({
33727 data : (function() {
33729 _this.fireEvent('years', _this, years);
33732 fields : [ 'value' ]
33735 select : function (_self, record, index)
33737 _this.setValue(_this.getValue());
33742 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33745 setValue : function(v, format)
33747 this.inputEl.dom.value = v;
33749 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33751 var d = Date.parseDate(v, f);
33758 this.setDay(d.format(this.dayFormat));
33759 this.setMonth(d.format(this.monthFormat));
33760 this.setYear(d.format(this.yearFormat));
33767 setDay : function(v)
33769 this.dayField.setValue(v);
33770 this.inputEl.dom.value = this.getValue();
33775 setMonth : function(v)
33777 this.monthField.setValue(v, true);
33778 this.inputEl.dom.value = this.getValue();
33783 setYear : function(v)
33785 this.yearField.setValue(v);
33786 this.inputEl.dom.value = this.getValue();
33791 getDay : function()
33793 return this.dayField.getValue();
33796 getMonth : function()
33798 return this.monthField.getValue();
33801 getYear : function()
33803 return this.yearField.getValue();
33806 getValue : function()
33808 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33810 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33820 this.inputEl.dom.value = '';
33825 validate : function()
33827 var d = this.dayField.validate();
33828 var m = this.monthField.validate();
33829 var y = this.yearField.validate();
33834 (!this.dayAllowBlank && !d) ||
33835 (!this.monthAllowBlank && !m) ||
33836 (!this.yearAllowBlank && !y)
33841 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33850 this.markInvalid();
33855 markValid : 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);
33869 * Mark this field as invalid
33870 * @param {String} msg The validation message
33872 markInvalid : function(msg)
33875 var label = this.el.select('label', true).first();
33876 var icon = this.el.select('i.fa-star', true).first();
33878 if(label && !icon){
33879 this.el.select('.roo-date-split-field-label', true).createChild({
33881 cls : 'text-danger fa fa-lg fa-star',
33882 tooltip : 'This field is required',
33883 style : 'margin-right:5px;'
33887 this.fireEvent('invalid', this, msg);
33890 clearInvalid : function()
33892 var label = this.el.select('label', true).first();
33893 var icon = this.el.select('i.fa-star', true).first();
33899 this.fireEvent('valid', this);
33902 getName: function()
33912 * http://masonry.desandro.com
33914 * The idea is to render all the bricks based on vertical width...
33916 * The original code extends 'outlayer' - we might need to use that....
33922 * @class Roo.bootstrap.LayoutMasonry
33923 * @extends Roo.bootstrap.Component
33924 * Bootstrap Layout Masonry class
33927 * Create a new Element
33928 * @param {Object} config The config object
33931 Roo.bootstrap.LayoutMasonry = function(config){
33933 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33937 Roo.bootstrap.LayoutMasonry.register(this);
33943 * Fire after layout the items
33944 * @param {Roo.bootstrap.LayoutMasonry} this
33945 * @param {Roo.EventObject} e
33952 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
33955 * @cfg {Boolean} isLayoutInstant = no animation?
33957 isLayoutInstant : false, // needed?
33960 * @cfg {Number} boxWidth width of the columns
33965 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
33970 * @cfg {Number} padWidth padding below box..
33975 * @cfg {Number} gutter gutter width..
33980 * @cfg {Number} maxCols maximum number of columns
33986 * @cfg {Boolean} isAutoInitial defalut true
33988 isAutoInitial : true,
33993 * @cfg {Boolean} isHorizontal defalut false
33995 isHorizontal : false,
33997 currentSize : null,
34003 bricks: null, //CompositeElement
34007 _isLayoutInited : false,
34009 // isAlternative : false, // only use for vertical layout...
34012 * @cfg {Number} alternativePadWidth padding below box..
34014 alternativePadWidth : 50,
34016 selectedBrick : [],
34018 getAutoCreate : function(){
34020 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34024 cls: 'blog-masonary-wrapper ' + this.cls,
34026 cls : 'mas-boxes masonary'
34033 getChildContainer: function( )
34035 if (this.boxesEl) {
34036 return this.boxesEl;
34039 this.boxesEl = this.el.select('.mas-boxes').first();
34041 return this.boxesEl;
34045 initEvents : function()
34049 if(this.isAutoInitial){
34050 Roo.log('hook children rendered');
34051 this.on('childrenrendered', function() {
34052 Roo.log('children rendered');
34058 initial : function()
34060 this.selectedBrick = [];
34062 this.currentSize = this.el.getBox(true);
34064 Roo.EventManager.onWindowResize(this.resize, this);
34066 if(!this.isAutoInitial){
34074 //this.layout.defer(500,this);
34078 resize : function()
34080 var cs = this.el.getBox(true);
34083 this.currentSize.width == cs.width &&
34084 this.currentSize.x == cs.x &&
34085 this.currentSize.height == cs.height &&
34086 this.currentSize.y == cs.y
34088 Roo.log("no change in with or X or Y");
34092 this.currentSize = cs;
34098 layout : function()
34100 this._resetLayout();
34102 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34104 this.layoutItems( isInstant );
34106 this._isLayoutInited = true;
34108 this.fireEvent('layout', this);
34112 _resetLayout : function()
34114 if(this.isHorizontal){
34115 this.horizontalMeasureColumns();
34119 this.verticalMeasureColumns();
34123 verticalMeasureColumns : function()
34125 this.getContainerWidth();
34127 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34128 // this.colWidth = Math.floor(this.containerWidth * 0.8);
34132 var boxWidth = this.boxWidth + this.padWidth;
34134 if(this.containerWidth < this.boxWidth){
34135 boxWidth = this.containerWidth
34138 var containerWidth = this.containerWidth;
34140 var cols = Math.floor(containerWidth / boxWidth);
34142 this.cols = Math.max( cols, 1 );
34144 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34146 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34148 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34150 this.colWidth = boxWidth + avail - this.padWidth;
34152 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34153 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
34156 horizontalMeasureColumns : function()
34158 this.getContainerWidth();
34160 var boxWidth = this.boxWidth;
34162 if(this.containerWidth < boxWidth){
34163 boxWidth = this.containerWidth;
34166 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34168 this.el.setHeight(boxWidth);
34172 getContainerWidth : function()
34174 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34177 layoutItems : function( isInstant )
34179 Roo.log(this.bricks);
34181 var items = Roo.apply([], this.bricks);
34183 if(this.isHorizontal){
34184 this._horizontalLayoutItems( items , isInstant );
34188 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34189 // this._verticalAlternativeLayoutItems( items , isInstant );
34193 this._verticalLayoutItems( items , isInstant );
34197 _verticalLayoutItems : function ( items , isInstant)
34199 if ( !items || !items.length ) {
34204 ['xs', 'xs', 'xs', 'tall'],
34205 ['xs', 'xs', 'tall'],
34206 ['xs', 'xs', 'sm'],
34207 ['xs', 'xs', 'xs'],
34213 ['sm', 'xs', 'xs'],
34217 ['tall', 'xs', 'xs', 'xs'],
34218 ['tall', 'xs', 'xs'],
34230 Roo.each(items, function(item, k){
34232 switch (item.size) {
34233 // these layouts take up a full box,
34244 boxes.push([item]);
34267 var filterPattern = function(box, length)
34275 var pattern = box.slice(0, length);
34279 Roo.each(pattern, function(i){
34280 format.push(i.size);
34283 Roo.each(standard, function(s){
34285 if(String(s) != String(format)){
34294 if(!match && length == 1){
34299 filterPattern(box, length - 1);
34303 queue.push(pattern);
34305 box = box.slice(length, box.length);
34307 filterPattern(box, 4);
34313 Roo.each(boxes, function(box, k){
34319 if(box.length == 1){
34324 filterPattern(box, 4);
34328 this._processVerticalLayoutQueue( queue, isInstant );
34332 // _verticalAlternativeLayoutItems : function( items , isInstant )
34334 // if ( !items || !items.length ) {
34338 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
34342 _horizontalLayoutItems : function ( items , isInstant)
34344 if ( !items || !items.length || items.length < 3) {
34350 var eItems = items.slice(0, 3);
34352 items = items.slice(3, items.length);
34355 ['xs', 'xs', 'xs', 'wide'],
34356 ['xs', 'xs', 'wide'],
34357 ['xs', 'xs', 'sm'],
34358 ['xs', 'xs', 'xs'],
34364 ['sm', 'xs', 'xs'],
34368 ['wide', 'xs', 'xs', 'xs'],
34369 ['wide', 'xs', 'xs'],
34382 Roo.each(items, function(item, k){
34384 switch (item.size) {
34395 boxes.push([item]);
34419 var filterPattern = function(box, length)
34427 var pattern = box.slice(0, length);
34431 Roo.each(pattern, function(i){
34432 format.push(i.size);
34435 Roo.each(standard, function(s){
34437 if(String(s) != String(format)){
34446 if(!match && length == 1){
34451 filterPattern(box, length - 1);
34455 queue.push(pattern);
34457 box = box.slice(length, box.length);
34459 filterPattern(box, 4);
34465 Roo.each(boxes, function(box, k){
34471 if(box.length == 1){
34476 filterPattern(box, 4);
34483 var pos = this.el.getBox(true);
34487 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34489 var hit_end = false;
34491 Roo.each(queue, function(box){
34495 Roo.each(box, function(b){
34497 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34507 Roo.each(box, function(b){
34509 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34512 mx = Math.max(mx, b.x);
34516 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34520 Roo.each(box, function(b){
34522 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34536 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34539 /** Sets position of item in DOM
34540 * @param {Element} item
34541 * @param {Number} x - horizontal position
34542 * @param {Number} y - vertical position
34543 * @param {Boolean} isInstant - disables transitions
34545 _processVerticalLayoutQueue : function( queue, isInstant )
34547 var pos = this.el.getBox(true);
34552 for (var i = 0; i < this.cols; i++){
34556 Roo.each(queue, function(box, k){
34558 var col = k % this.cols;
34560 Roo.each(box, function(b,kk){
34562 b.el.position('absolute');
34564 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34565 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34567 if(b.size == 'md-left' || b.size == 'md-right'){
34568 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34569 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34572 b.el.setWidth(width);
34573 b.el.setHeight(height);
34575 b.el.select('iframe',true).setSize(width,height);
34579 for (var i = 0; i < this.cols; i++){
34581 if(maxY[i] < maxY[col]){
34586 col = Math.min(col, i);
34590 x = pos.x + col * (this.colWidth + this.padWidth);
34594 var positions = [];
34596 switch (box.length){
34598 positions = this.getVerticalOneBoxColPositions(x, y, box);
34601 positions = this.getVerticalTwoBoxColPositions(x, y, box);
34604 positions = this.getVerticalThreeBoxColPositions(x, y, box);
34607 positions = this.getVerticalFourBoxColPositions(x, y, box);
34613 Roo.each(box, function(b,kk){
34615 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34617 var sz = b.el.getSize();
34619 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34627 for (var i = 0; i < this.cols; i++){
34628 mY = Math.max(mY, maxY[i]);
34631 this.el.setHeight(mY - pos.y);
34635 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34637 // var pos = this.el.getBox(true);
34640 // var maxX = pos.right;
34642 // var maxHeight = 0;
34644 // Roo.each(items, function(item, k){
34648 // item.el.position('absolute');
34650 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34652 // item.el.setWidth(width);
34654 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34656 // item.el.setHeight(height);
34659 // item.el.setXY([x, y], isInstant ? false : true);
34661 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34664 // y = y + height + this.alternativePadWidth;
34666 // maxHeight = maxHeight + height + this.alternativePadWidth;
34670 // this.el.setHeight(maxHeight);
34674 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34676 var pos = this.el.getBox(true);
34681 var maxX = pos.right;
34683 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34685 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34687 Roo.each(queue, function(box, k){
34689 Roo.each(box, function(b, kk){
34691 b.el.position('absolute');
34693 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34694 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34696 if(b.size == 'md-left' || b.size == 'md-right'){
34697 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34698 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34701 b.el.setWidth(width);
34702 b.el.setHeight(height);
34710 var positions = [];
34712 switch (box.length){
34714 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34717 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34720 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34723 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34729 Roo.each(box, function(b,kk){
34731 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34733 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34741 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34743 Roo.each(eItems, function(b,k){
34745 b.size = (k == 0) ? 'sm' : 'xs';
34746 b.x = (k == 0) ? 2 : 1;
34747 b.y = (k == 0) ? 2 : 1;
34749 b.el.position('absolute');
34751 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34753 b.el.setWidth(width);
34755 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34757 b.el.setHeight(height);
34761 var positions = [];
34764 x : maxX - this.unitWidth * 2 - this.gutter,
34769 x : maxX - this.unitWidth,
34770 y : minY + (this.unitWidth + this.gutter) * 2
34774 x : maxX - this.unitWidth * 3 - this.gutter * 2,
34778 Roo.each(eItems, function(b,k){
34780 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34786 getVerticalOneBoxColPositions : function(x, y, box)
34790 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34792 if(box[0].size == 'md-left'){
34796 if(box[0].size == 'md-right'){
34801 x : x + (this.unitWidth + this.gutter) * rand,
34808 getVerticalTwoBoxColPositions : function(x, y, box)
34812 if(box[0].size == 'xs'){
34816 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34820 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34834 x : x + (this.unitWidth + this.gutter) * 2,
34835 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34842 getVerticalThreeBoxColPositions : function(x, y, box)
34846 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34854 x : x + (this.unitWidth + this.gutter) * 1,
34859 x : x + (this.unitWidth + this.gutter) * 2,
34867 if(box[0].size == 'xs' && box[1].size == 'xs'){
34876 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34880 x : x + (this.unitWidth + this.gutter) * 1,
34894 x : x + (this.unitWidth + this.gutter) * 2,
34899 x : x + (this.unitWidth + this.gutter) * 2,
34900 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34907 getVerticalFourBoxColPositions : function(x, y, box)
34911 if(box[0].size == 'xs'){
34920 y : y + (this.unitHeight + this.gutter) * 1
34925 y : y + (this.unitHeight + this.gutter) * 2
34929 x : x + (this.unitWidth + this.gutter) * 1,
34943 x : x + (this.unitWidth + this.gutter) * 2,
34948 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34949 y : y + (this.unitHeight + this.gutter) * 1
34953 x : x + (this.unitWidth + this.gutter) * 2,
34954 y : y + (this.unitWidth + this.gutter) * 2
34961 getHorizontalOneBoxColPositions : function(maxX, minY, box)
34965 if(box[0].size == 'md-left'){
34967 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34974 if(box[0].size == 'md-right'){
34976 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34977 y : minY + (this.unitWidth + this.gutter) * 1
34983 var rand = Math.floor(Math.random() * (4 - box[0].y));
34986 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34987 y : minY + (this.unitWidth + this.gutter) * rand
34994 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34998 if(box[0].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) * (3 - box[1].y)
35015 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35020 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35021 y : minY + (this.unitWidth + this.gutter) * 2
35028 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35032 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35035 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35040 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35041 y : minY + (this.unitWidth + this.gutter) * 1
35045 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35046 y : minY + (this.unitWidth + this.gutter) * 2
35053 if(box[0].size == 'xs' && box[1].size == 'xs'){
35056 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35061 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35066 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35067 y : minY + (this.unitWidth + this.gutter) * 1
35075 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35080 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35081 y : minY + (this.unitWidth + this.gutter) * 2
35085 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35086 y : minY + (this.unitWidth + this.gutter) * 2
35093 getHorizontalFourBoxColPositions : function(maxX, minY, box)
35097 if(box[0].size == 'xs'){
35100 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35105 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35110 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),
35115 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35116 y : minY + (this.unitWidth + this.gutter) * 1
35124 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35129 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35130 y : minY + (this.unitWidth + this.gutter) * 2
35134 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35135 y : minY + (this.unitWidth + this.gutter) * 2
35139 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),
35140 y : minY + (this.unitWidth + this.gutter) * 2
35148 * remove a Masonry Brick
35149 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35151 removeBrick : function(brick_id)
35157 for (var i = 0; i<this.bricks.length; i++) {
35158 if (this.bricks[i].id == brick_id) {
35159 this.bricks.splice(i,1);
35160 this.el.dom.removeChild(Roo.get(brick_id).dom);
35167 * adds a Masonry Brick
35168 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35170 addBrick : function(cfg)
35172 var cn = new Roo.bootstrap.MasonryBrick(cfg);
35173 //this.register(cn);
35174 cn.parentId = this.id;
35175 cn.render(this.el);
35180 * register a Masonry Brick
35181 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35184 register : function(brick)
35186 this.bricks.push(brick);
35187 brick.masonryId = this.id;
35191 * clear all the Masonry Brick
35193 clearAll : function()
35196 //this.getChildContainer().dom.innerHTML = "";
35197 this.el.dom.innerHTML = '';
35200 getSelected : function()
35202 if (!this.selectedBrick) {
35206 return this.selectedBrick;
35210 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35214 * register a Masonry Layout
35215 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35218 register : function(layout)
35220 this.groups[layout.id] = layout;
35223 * fetch a Masonry Layout based on the masonry layout ID
35224 * @param {string} the masonry layout to add
35225 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35228 get: function(layout_id) {
35229 if (typeof(this.groups[layout_id]) == 'undefined') {
35232 return this.groups[layout_id] ;
35244 * http://masonry.desandro.com
35246 * The idea is to render all the bricks based on vertical width...
35248 * The original code extends 'outlayer' - we might need to use that....
35254 * @class Roo.bootstrap.LayoutMasonryAuto
35255 * @extends Roo.bootstrap.Component
35256 * Bootstrap Layout Masonry class
35259 * Create a new Element
35260 * @param {Object} config The config object
35263 Roo.bootstrap.LayoutMasonryAuto = function(config){
35264 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35267 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
35270 * @cfg {Boolean} isFitWidth - resize the width..
35272 isFitWidth : false, // options..
35274 * @cfg {Boolean} isOriginLeft = left align?
35276 isOriginLeft : true,
35278 * @cfg {Boolean} isOriginTop = top align?
35280 isOriginTop : false,
35282 * @cfg {Boolean} isLayoutInstant = no animation?
35284 isLayoutInstant : false, // needed?
35286 * @cfg {Boolean} isResizingContainer = not sure if this is used..
35288 isResizingContainer : true,
35290 * @cfg {Number} columnWidth width of the columns
35296 * @cfg {Number} maxCols maximum number of columns
35301 * @cfg {Number} padHeight padding below box..
35307 * @cfg {Boolean} isAutoInitial defalut true
35310 isAutoInitial : true,
35316 initialColumnWidth : 0,
35317 currentSize : null,
35319 colYs : null, // array.
35326 bricks: null, //CompositeElement
35327 cols : 0, // array?
35328 // element : null, // wrapped now this.el
35329 _isLayoutInited : null,
35332 getAutoCreate : function(){
35336 cls: 'blog-masonary-wrapper ' + this.cls,
35338 cls : 'mas-boxes masonary'
35345 getChildContainer: function( )
35347 if (this.boxesEl) {
35348 return this.boxesEl;
35351 this.boxesEl = this.el.select('.mas-boxes').first();
35353 return this.boxesEl;
35357 initEvents : function()
35361 if(this.isAutoInitial){
35362 Roo.log('hook children rendered');
35363 this.on('childrenrendered', function() {
35364 Roo.log('children rendered');
35371 initial : function()
35373 this.reloadItems();
35375 this.currentSize = this.el.getBox(true);
35377 /// was window resize... - let's see if this works..
35378 Roo.EventManager.onWindowResize(this.resize, this);
35380 if(!this.isAutoInitial){
35385 this.layout.defer(500,this);
35388 reloadItems: function()
35390 this.bricks = this.el.select('.masonry-brick', true);
35392 this.bricks.each(function(b) {
35393 //Roo.log(b.getSize());
35394 if (!b.attr('originalwidth')) {
35395 b.attr('originalwidth', b.getSize().width);
35400 Roo.log(this.bricks.elements.length);
35403 resize : function()
35406 var cs = this.el.getBox(true);
35408 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35409 Roo.log("no change in with or X");
35412 this.currentSize = cs;
35416 layout : function()
35419 this._resetLayout();
35420 //this._manageStamps();
35422 // don't animate first layout
35423 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35424 this.layoutItems( isInstant );
35426 // flag for initalized
35427 this._isLayoutInited = true;
35430 layoutItems : function( isInstant )
35432 //var items = this._getItemsForLayout( this.items );
35433 // original code supports filtering layout items.. we just ignore it..
35435 this._layoutItems( this.bricks , isInstant );
35437 this._postLayout();
35439 _layoutItems : function ( items , isInstant)
35441 //this.fireEvent( 'layout', this, items );
35444 if ( !items || !items.elements.length ) {
35445 // no items, emit event with empty array
35450 items.each(function(item) {
35451 Roo.log("layout item");
35453 // get x/y object from method
35454 var position = this._getItemLayoutPosition( item );
35456 position.item = item;
35457 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35458 queue.push( position );
35461 this._processLayoutQueue( queue );
35463 /** Sets position of item in DOM
35464 * @param {Element} item
35465 * @param {Number} x - horizontal position
35466 * @param {Number} y - vertical position
35467 * @param {Boolean} isInstant - disables transitions
35469 _processLayoutQueue : function( queue )
35471 for ( var i=0, len = queue.length; i < len; i++ ) {
35472 var obj = queue[i];
35473 obj.item.position('absolute');
35474 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35480 * Any logic you want to do after each layout,
35481 * i.e. size the container
35483 _postLayout : function()
35485 this.resizeContainer();
35488 resizeContainer : function()
35490 if ( !this.isResizingContainer ) {
35493 var size = this._getContainerSize();
35495 this.el.setSize(size.width,size.height);
35496 this.boxesEl.setSize(size.width,size.height);
35502 _resetLayout : function()
35504 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35505 this.colWidth = this.el.getWidth();
35506 //this.gutter = this.el.getWidth();
35508 this.measureColumns();
35514 this.colYs.push( 0 );
35520 measureColumns : function()
35522 this.getContainerWidth();
35523 // if columnWidth is 0, default to outerWidth of first item
35524 if ( !this.columnWidth ) {
35525 var firstItem = this.bricks.first();
35526 Roo.log(firstItem);
35527 this.columnWidth = this.containerWidth;
35528 if (firstItem && firstItem.attr('originalwidth') ) {
35529 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35531 // columnWidth fall back to item of first element
35532 Roo.log("set column width?");
35533 this.initialColumnWidth = this.columnWidth ;
35535 // if first elem has no width, default to size of container
35540 if (this.initialColumnWidth) {
35541 this.columnWidth = this.initialColumnWidth;
35546 // column width is fixed at the top - however if container width get's smaller we should
35549 // this bit calcs how man columns..
35551 var columnWidth = this.columnWidth += this.gutter;
35553 // calculate columns
35554 var containerWidth = this.containerWidth + this.gutter;
35556 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35557 // fix rounding errors, typically with gutters
35558 var excess = columnWidth - containerWidth % columnWidth;
35561 // if overshoot is less than a pixel, round up, otherwise floor it
35562 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35563 cols = Math[ mathMethod ]( cols );
35564 this.cols = Math.max( cols, 1 );
35565 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35567 // padding positioning..
35568 var totalColWidth = this.cols * this.columnWidth;
35569 var padavail = this.containerWidth - totalColWidth;
35570 // so for 2 columns - we need 3 'pads'
35572 var padNeeded = (1+this.cols) * this.padWidth;
35574 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35576 this.columnWidth += padExtra
35577 //this.padWidth = Math.floor(padavail / ( this.cols));
35579 // adjust colum width so that padding is fixed??
35581 // we have 3 columns ... total = width * 3
35582 // we have X left over... that should be used by
35584 //if (this.expandC) {
35592 getContainerWidth : function()
35594 /* // container is parent if fit width
35595 var container = this.isFitWidth ? this.element.parentNode : this.element;
35596 // check that this.size and size are there
35597 // IE8 triggers resize on body size change, so they might not be
35599 var size = getSize( container ); //FIXME
35600 this.containerWidth = size && size.innerWidth; //FIXME
35603 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
35607 _getItemLayoutPosition : function( item ) // what is item?
35609 // we resize the item to our columnWidth..
35611 item.setWidth(this.columnWidth);
35612 item.autoBoxAdjust = false;
35614 var sz = item.getSize();
35616 // how many columns does this brick span
35617 var remainder = this.containerWidth % this.columnWidth;
35619 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35620 // round if off by 1 pixel, otherwise use ceil
35621 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
35622 colSpan = Math.min( colSpan, this.cols );
35624 // normally this should be '1' as we dont' currently allow multi width columns..
35626 var colGroup = this._getColGroup( colSpan );
35627 // get the minimum Y value from the columns
35628 var minimumY = Math.min.apply( Math, colGroup );
35629 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35631 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35633 // position the brick
35635 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35636 y: this.currentSize.y + minimumY + this.padHeight
35640 // apply setHeight to necessary columns
35641 var setHeight = minimumY + sz.height + this.padHeight;
35642 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35644 var setSpan = this.cols + 1 - colGroup.length;
35645 for ( var i = 0; i < setSpan; i++ ) {
35646 this.colYs[ shortColIndex + i ] = setHeight ;
35653 * @param {Number} colSpan - number of columns the element spans
35654 * @returns {Array} colGroup
35656 _getColGroup : function( colSpan )
35658 if ( colSpan < 2 ) {
35659 // if brick spans only one column, use all the column Ys
35664 // how many different places could this brick fit horizontally
35665 var groupCount = this.cols + 1 - colSpan;
35666 // for each group potential horizontal position
35667 for ( var i = 0; i < groupCount; i++ ) {
35668 // make an array of colY values for that one group
35669 var groupColYs = this.colYs.slice( i, i + colSpan );
35670 // and get the max value of the array
35671 colGroup[i] = Math.max.apply( Math, groupColYs );
35676 _manageStamp : function( stamp )
35678 var stampSize = stamp.getSize();
35679 var offset = stamp.getBox();
35680 // get the columns that this stamp affects
35681 var firstX = this.isOriginLeft ? offset.x : offset.right;
35682 var lastX = firstX + stampSize.width;
35683 var firstCol = Math.floor( firstX / this.columnWidth );
35684 firstCol = Math.max( 0, firstCol );
35686 var lastCol = Math.floor( lastX / this.columnWidth );
35687 // lastCol should not go over if multiple of columnWidth #425
35688 lastCol -= lastX % this.columnWidth ? 0 : 1;
35689 lastCol = Math.min( this.cols - 1, lastCol );
35691 // set colYs to bottom of the stamp
35692 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35695 for ( var i = firstCol; i <= lastCol; i++ ) {
35696 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35701 _getContainerSize : function()
35703 this.maxY = Math.max.apply( Math, this.colYs );
35708 if ( this.isFitWidth ) {
35709 size.width = this._getContainerFitWidth();
35715 _getContainerFitWidth : function()
35717 var unusedCols = 0;
35718 // count unused columns
35721 if ( this.colYs[i] !== 0 ) {
35726 // fit container to columns that have been used
35727 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35730 needsResizeLayout : function()
35732 var previousWidth = this.containerWidth;
35733 this.getContainerWidth();
35734 return previousWidth !== this.containerWidth;
35749 * @class Roo.bootstrap.MasonryBrick
35750 * @extends Roo.bootstrap.Component
35751 * Bootstrap MasonryBrick class
35754 * Create a new MasonryBrick
35755 * @param {Object} config The config object
35758 Roo.bootstrap.MasonryBrick = function(config){
35760 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35762 Roo.bootstrap.MasonryBrick.register(this);
35768 * When a MasonryBrick is clcik
35769 * @param {Roo.bootstrap.MasonryBrick} this
35770 * @param {Roo.EventObject} e
35776 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
35779 * @cfg {String} title
35783 * @cfg {String} html
35787 * @cfg {String} bgimage
35791 * @cfg {String} videourl
35795 * @cfg {String} cls
35799 * @cfg {String} href
35803 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35808 * @cfg {String} placetitle (center|bottom)
35813 * @cfg {Boolean} isFitContainer defalut true
35815 isFitContainer : true,
35818 * @cfg {Boolean} preventDefault defalut false
35820 preventDefault : false,
35823 * @cfg {Boolean} inverse defalut false
35825 maskInverse : false,
35827 getAutoCreate : function()
35829 if(!this.isFitContainer){
35830 return this.getSplitAutoCreate();
35833 var cls = 'masonry-brick masonry-brick-full';
35835 if(this.href.length){
35836 cls += ' masonry-brick-link';
35839 if(this.bgimage.length){
35840 cls += ' masonry-brick-image';
35843 if(this.maskInverse){
35844 cls += ' mask-inverse';
35847 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35848 cls += ' enable-mask';
35852 cls += ' masonry-' + this.size + '-brick';
35855 if(this.placetitle.length){
35857 switch (this.placetitle) {
35859 cls += ' masonry-center-title';
35862 cls += ' masonry-bottom-title';
35869 if(!this.html.length && !this.bgimage.length){
35870 cls += ' masonry-center-title';
35873 if(!this.html.length && this.bgimage.length){
35874 cls += ' masonry-bottom-title';
35879 cls += ' ' + this.cls;
35883 tag: (this.href.length) ? 'a' : 'div',
35888 cls: 'masonry-brick-mask'
35892 cls: 'masonry-brick-paragraph',
35898 if(this.href.length){
35899 cfg.href = this.href;
35902 var cn = cfg.cn[1].cn;
35904 if(this.title.length){
35907 cls: 'masonry-brick-title',
35912 if(this.html.length){
35915 cls: 'masonry-brick-text',
35920 if (!this.title.length && !this.html.length) {
35921 cfg.cn[1].cls += ' hide';
35924 if(this.bgimage.length){
35927 cls: 'masonry-brick-image-view',
35932 if(this.videourl.length){
35933 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35934 // youtube support only?
35937 cls: 'masonry-brick-image-view',
35940 allowfullscreen : true
35948 getSplitAutoCreate : function()
35950 var cls = 'masonry-brick masonry-brick-split';
35952 if(this.href.length){
35953 cls += ' masonry-brick-link';
35956 if(this.bgimage.length){
35957 cls += ' masonry-brick-image';
35961 cls += ' masonry-' + this.size + '-brick';
35964 switch (this.placetitle) {
35966 cls += ' masonry-center-title';
35969 cls += ' masonry-bottom-title';
35972 if(!this.bgimage.length){
35973 cls += ' masonry-center-title';
35976 if(this.bgimage.length){
35977 cls += ' masonry-bottom-title';
35983 cls += ' ' + this.cls;
35987 tag: (this.href.length) ? 'a' : 'div',
35992 cls: 'masonry-brick-split-head',
35996 cls: 'masonry-brick-paragraph',
36003 cls: 'masonry-brick-split-body',
36009 if(this.href.length){
36010 cfg.href = this.href;
36013 if(this.title.length){
36014 cfg.cn[0].cn[0].cn.push({
36016 cls: 'masonry-brick-title',
36021 if(this.html.length){
36022 cfg.cn[1].cn.push({
36024 cls: 'masonry-brick-text',
36029 if(this.bgimage.length){
36030 cfg.cn[0].cn.push({
36032 cls: 'masonry-brick-image-view',
36037 if(this.videourl.length){
36038 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36039 // youtube support only?
36040 cfg.cn[0].cn.cn.push({
36042 cls: 'masonry-brick-image-view',
36045 allowfullscreen : true
36052 initEvents: function()
36054 switch (this.size) {
36087 this.el.on('touchstart', this.onTouchStart, this);
36088 this.el.on('touchmove', this.onTouchMove, this);
36089 this.el.on('touchend', this.onTouchEnd, this);
36090 this.el.on('contextmenu', this.onContextMenu, this);
36092 this.el.on('mouseenter' ,this.enter, this);
36093 this.el.on('mouseleave', this.leave, this);
36094 this.el.on('click', this.onClick, this);
36097 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36098 this.parent().bricks.push(this);
36103 onClick: function(e, el)
36105 var time = this.endTimer - this.startTimer;
36106 // Roo.log(e.preventDefault());
36109 e.preventDefault();
36114 if(!this.preventDefault){
36118 e.preventDefault();
36120 if (this.activeClass != '') {
36121 this.selectBrick();
36124 this.fireEvent('click', this, e);
36127 enter: function(e, el)
36129 e.preventDefault();
36131 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36135 if(this.bgimage.length && this.html.length){
36136 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36140 leave: function(e, el)
36142 e.preventDefault();
36144 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36148 if(this.bgimage.length && this.html.length){
36149 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36153 onTouchStart: function(e, el)
36155 // e.preventDefault();
36157 this.touchmoved = false;
36159 if(!this.isFitContainer){
36163 if(!this.bgimage.length || !this.html.length){
36167 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36169 this.timer = new Date().getTime();
36173 onTouchMove: function(e, el)
36175 this.touchmoved = true;
36178 onContextMenu : function(e,el)
36180 e.preventDefault();
36181 e.stopPropagation();
36185 onTouchEnd: function(e, el)
36187 // e.preventDefault();
36189 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36196 if(!this.bgimage.length || !this.html.length){
36198 if(this.href.length){
36199 window.location.href = this.href;
36205 if(!this.isFitContainer){
36209 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36211 window.location.href = this.href;
36214 //selection on single brick only
36215 selectBrick : function() {
36217 if (!this.parentId) {
36221 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36222 var index = m.selectedBrick.indexOf(this.id);
36225 m.selectedBrick.splice(index,1);
36226 this.el.removeClass(this.activeClass);
36230 for(var i = 0; i < m.selectedBrick.length; i++) {
36231 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36232 b.el.removeClass(b.activeClass);
36235 m.selectedBrick = [];
36237 m.selectedBrick.push(this.id);
36238 this.el.addClass(this.activeClass);
36242 isSelected : function(){
36243 return this.el.hasClass(this.activeClass);
36248 Roo.apply(Roo.bootstrap.MasonryBrick, {
36251 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36253 * register a Masonry Brick
36254 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36257 register : function(brick)
36259 //this.groups[brick.id] = brick;
36260 this.groups.add(brick.id, brick);
36263 * fetch a masonry brick based on the masonry brick ID
36264 * @param {string} the masonry brick to add
36265 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36268 get: function(brick_id)
36270 // if (typeof(this.groups[brick_id]) == 'undefined') {
36273 // return this.groups[brick_id] ;
36275 if(this.groups.key(brick_id)) {
36276 return this.groups.key(brick_id);
36294 * @class Roo.bootstrap.Brick
36295 * @extends Roo.bootstrap.Component
36296 * Bootstrap Brick class
36299 * Create a new Brick
36300 * @param {Object} config The config object
36303 Roo.bootstrap.Brick = function(config){
36304 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36310 * When a Brick is click
36311 * @param {Roo.bootstrap.Brick} this
36312 * @param {Roo.EventObject} e
36318 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
36321 * @cfg {String} title
36325 * @cfg {String} html
36329 * @cfg {String} bgimage
36333 * @cfg {String} cls
36337 * @cfg {String} href
36341 * @cfg {String} video
36345 * @cfg {Boolean} square
36349 getAutoCreate : function()
36351 var cls = 'roo-brick';
36353 if(this.href.length){
36354 cls += ' roo-brick-link';
36357 if(this.bgimage.length){
36358 cls += ' roo-brick-image';
36361 if(!this.html.length && !this.bgimage.length){
36362 cls += ' roo-brick-center-title';
36365 if(!this.html.length && this.bgimage.length){
36366 cls += ' roo-brick-bottom-title';
36370 cls += ' ' + this.cls;
36374 tag: (this.href.length) ? 'a' : 'div',
36379 cls: 'roo-brick-paragraph',
36385 if(this.href.length){
36386 cfg.href = this.href;
36389 var cn = cfg.cn[0].cn;
36391 if(this.title.length){
36394 cls: 'roo-brick-title',
36399 if(this.html.length){
36402 cls: 'roo-brick-text',
36409 if(this.bgimage.length){
36412 cls: 'roo-brick-image-view',
36420 initEvents: function()
36422 if(this.title.length || this.html.length){
36423 this.el.on('mouseenter' ,this.enter, this);
36424 this.el.on('mouseleave', this.leave, this);
36427 Roo.EventManager.onWindowResize(this.resize, this);
36429 if(this.bgimage.length){
36430 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36431 this.imageEl.on('load', this.onImageLoad, this);
36438 onImageLoad : function()
36443 resize : function()
36445 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36447 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36449 if(this.bgimage.length){
36450 var image = this.el.select('.roo-brick-image-view', true).first();
36452 image.setWidth(paragraph.getWidth());
36455 image.setHeight(paragraph.getWidth());
36458 this.el.setHeight(image.getHeight());
36459 paragraph.setHeight(image.getHeight());
36465 enter: function(e, el)
36467 e.preventDefault();
36469 if(this.bgimage.length){
36470 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36471 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36475 leave: function(e, el)
36477 e.preventDefault();
36479 if(this.bgimage.length){
36480 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36481 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36496 * @class Roo.bootstrap.NumberField
36497 * @extends Roo.bootstrap.Input
36498 * Bootstrap NumberField class
36504 * Create a new NumberField
36505 * @param {Object} config The config object
36508 Roo.bootstrap.NumberField = function(config){
36509 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36512 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36515 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36517 allowDecimals : true,
36519 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36521 decimalSeparator : ".",
36523 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36525 decimalPrecision : 2,
36527 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36529 allowNegative : true,
36532 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36536 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36538 minValue : Number.NEGATIVE_INFINITY,
36540 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36542 maxValue : Number.MAX_VALUE,
36544 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36546 minText : "The minimum value for this field is {0}",
36548 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36550 maxText : "The maximum value for this field is {0}",
36552 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36553 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36555 nanText : "{0} is not a valid number",
36557 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36559 thousandsDelimiter : false,
36561 * @cfg {String} valueAlign alignment of value
36563 valueAlign : "left",
36565 getAutoCreate : function()
36567 var hiddenInput = {
36571 cls: 'hidden-number-input'
36575 hiddenInput.name = this.name;
36580 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36582 this.name = hiddenInput.name;
36584 if(cfg.cn.length > 0) {
36585 cfg.cn.push(hiddenInput);
36592 initEvents : function()
36594 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36596 var allowed = "0123456789";
36598 if(this.allowDecimals){
36599 allowed += this.decimalSeparator;
36602 if(this.allowNegative){
36606 if(this.thousandsDelimiter) {
36610 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36612 var keyPress = function(e){
36614 var k = e.getKey();
36616 var c = e.getCharCode();
36619 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36620 allowed.indexOf(String.fromCharCode(c)) === -1
36626 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36630 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36635 this.el.on("keypress", keyPress, this);
36638 validateValue : function(value)
36641 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36645 var num = this.parseValue(value);
36648 this.markInvalid(String.format(this.nanText, value));
36652 if(num < this.minValue){
36653 this.markInvalid(String.format(this.minText, this.minValue));
36657 if(num > this.maxValue){
36658 this.markInvalid(String.format(this.maxText, this.maxValue));
36665 getValue : function()
36667 var v = this.hiddenEl().getValue();
36669 return this.fixPrecision(this.parseValue(v));
36672 parseValue : function(value)
36674 if(this.thousandsDelimiter) {
36676 r = new RegExp(",", "g");
36677 value = value.replace(r, "");
36680 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36681 return isNaN(value) ? '' : value;
36684 fixPrecision : function(value)
36686 if(this.thousandsDelimiter) {
36688 r = new RegExp(",", "g");
36689 value = value.replace(r, "");
36692 var nan = isNaN(value);
36694 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36695 return nan ? '' : value;
36697 return parseFloat(value).toFixed(this.decimalPrecision);
36700 setValue : function(v)
36702 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36708 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36710 this.inputEl().dom.value = (v == '') ? '' :
36711 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36713 if(!this.allowZero && v === '0') {
36714 this.hiddenEl().dom.value = '';
36715 this.inputEl().dom.value = '';
36722 decimalPrecisionFcn : function(v)
36724 return Math.floor(v);
36727 beforeBlur : function()
36729 var v = this.parseValue(this.getRawValue());
36731 if(v || v === 0 || v === ''){
36736 hiddenEl : function()
36738 return this.el.select('input.hidden-number-input',true).first();
36750 * @class Roo.bootstrap.DocumentSlider
36751 * @extends Roo.bootstrap.Component
36752 * Bootstrap DocumentSlider class
36755 * Create a new DocumentViewer
36756 * @param {Object} config The config object
36759 Roo.bootstrap.DocumentSlider = function(config){
36760 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36767 * Fire after initEvent
36768 * @param {Roo.bootstrap.DocumentSlider} this
36773 * Fire after update
36774 * @param {Roo.bootstrap.DocumentSlider} this
36780 * @param {Roo.bootstrap.DocumentSlider} this
36786 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
36792 getAutoCreate : function()
36796 cls : 'roo-document-slider',
36800 cls : 'roo-document-slider-header',
36804 cls : 'roo-document-slider-header-title'
36810 cls : 'roo-document-slider-body',
36814 cls : 'roo-document-slider-prev',
36818 cls : 'fa fa-chevron-left'
36824 cls : 'roo-document-slider-thumb',
36828 cls : 'roo-document-slider-image'
36834 cls : 'roo-document-slider-next',
36838 cls : 'fa fa-chevron-right'
36850 initEvents : function()
36852 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36853 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36855 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36856 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36858 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36859 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36861 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36862 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36864 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36865 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36867 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36868 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36870 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36871 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36873 this.thumbEl.on('click', this.onClick, this);
36875 this.prevIndicator.on('click', this.prev, this);
36877 this.nextIndicator.on('click', this.next, this);
36881 initial : function()
36883 if(this.files.length){
36884 this.indicator = 1;
36888 this.fireEvent('initial', this);
36891 update : function()
36893 this.imageEl.attr('src', this.files[this.indicator - 1]);
36895 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36897 this.prevIndicator.show();
36899 if(this.indicator == 1){
36900 this.prevIndicator.hide();
36903 this.nextIndicator.show();
36905 if(this.indicator == this.files.length){
36906 this.nextIndicator.hide();
36909 this.thumbEl.scrollTo('top');
36911 this.fireEvent('update', this);
36914 onClick : function(e)
36916 e.preventDefault();
36918 this.fireEvent('click', this);
36923 e.preventDefault();
36925 this.indicator = Math.max(1, this.indicator - 1);
36932 e.preventDefault();
36934 this.indicator = Math.min(this.files.length, this.indicator + 1);
36948 * @class Roo.bootstrap.RadioSet
36949 * @extends Roo.bootstrap.Input
36950 * Bootstrap RadioSet class
36951 * @cfg {String} indicatorpos (left|right) default left
36952 * @cfg {Boolean} inline (true|false) inline the element (default true)
36953 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36955 * Create a new RadioSet
36956 * @param {Object} config The config object
36959 Roo.bootstrap.RadioSet = function(config){
36961 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36965 Roo.bootstrap.RadioSet.register(this);
36970 * Fires when the element is checked or unchecked.
36971 * @param {Roo.bootstrap.RadioSet} this This radio
36972 * @param {Roo.bootstrap.Radio} item The checked item
36977 * Fires when the element is click.
36978 * @param {Roo.bootstrap.RadioSet} this This radio set
36979 * @param {Roo.bootstrap.Radio} item The checked item
36980 * @param {Roo.EventObject} e The event object
36987 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
36995 indicatorpos : 'left',
36997 getAutoCreate : function()
37001 cls : 'roo-radio-set-label',
37005 html : this.fieldLabel
37009 if (Roo.bootstrap.version == 3) {
37012 if(this.indicatorpos == 'left'){
37015 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37016 tooltip : 'This field is required'
37021 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37022 tooltip : 'This field is required'
37028 cls : 'roo-radio-set-items'
37031 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37033 if (align === 'left' && this.fieldLabel.length) {
37036 cls : "roo-radio-set-right",
37042 if(this.labelWidth > 12){
37043 label.style = "width: " + this.labelWidth + 'px';
37046 if(this.labelWidth < 13 && this.labelmd == 0){
37047 this.labelmd = this.labelWidth;
37050 if(this.labellg > 0){
37051 label.cls += ' col-lg-' + this.labellg;
37052 items.cls += ' col-lg-' + (12 - this.labellg);
37055 if(this.labelmd > 0){
37056 label.cls += ' col-md-' + this.labelmd;
37057 items.cls += ' col-md-' + (12 - this.labelmd);
37060 if(this.labelsm > 0){
37061 label.cls += ' col-sm-' + this.labelsm;
37062 items.cls += ' col-sm-' + (12 - this.labelsm);
37065 if(this.labelxs > 0){
37066 label.cls += ' col-xs-' + this.labelxs;
37067 items.cls += ' col-xs-' + (12 - this.labelxs);
37073 cls : 'roo-radio-set',
37077 cls : 'roo-radio-set-input',
37080 value : this.value ? this.value : ''
37087 if(this.weight.length){
37088 cfg.cls += ' roo-radio-' + this.weight;
37092 cfg.cls += ' roo-radio-set-inline';
37096 ['xs','sm','md','lg'].map(function(size){
37097 if (settings[size]) {
37098 cfg.cls += ' col-' + size + '-' + settings[size];
37106 initEvents : function()
37108 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37109 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37111 if(!this.fieldLabel.length){
37112 this.labelEl.hide();
37115 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37116 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37118 this.indicator = this.indicatorEl();
37120 if(this.indicator){
37121 this.indicator.addClass('invisible');
37124 this.originalValue = this.getValue();
37128 inputEl: function ()
37130 return this.el.select('.roo-radio-set-input', true).first();
37133 getChildContainer : function()
37135 return this.itemsEl;
37138 register : function(item)
37140 this.radioes.push(item);
37144 validate : function()
37146 if(this.getVisibilityEl().hasClass('hidden')){
37152 Roo.each(this.radioes, function(i){
37161 if(this.allowBlank) {
37165 if(this.disabled || valid){
37170 this.markInvalid();
37175 markValid : function()
37177 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37178 this.indicatorEl().removeClass('visible');
37179 this.indicatorEl().addClass('invisible');
37183 if (Roo.bootstrap.version == 3) {
37184 this.el.removeClass([this.invalidClass, this.validClass]);
37185 this.el.addClass(this.validClass);
37187 this.el.removeClass(['is-invalid','is-valid']);
37188 this.el.addClass(['is-valid']);
37190 this.fireEvent('valid', this);
37193 markInvalid : function(msg)
37195 if(this.allowBlank || this.disabled){
37199 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37200 this.indicatorEl().removeClass('invisible');
37201 this.indicatorEl().addClass('visible');
37203 if (Roo.bootstrap.version == 3) {
37204 this.el.removeClass([this.invalidClass, this.validClass]);
37205 this.el.addClass(this.invalidClass);
37207 this.el.removeClass(['is-invalid','is-valid']);
37208 this.el.addClass(['is-invalid']);
37211 this.fireEvent('invalid', this, msg);
37215 setValue : function(v, suppressEvent)
37217 if(this.value === v){
37224 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37227 Roo.each(this.radioes, function(i){
37229 i.el.removeClass('checked');
37232 Roo.each(this.radioes, function(i){
37234 if(i.value === v || i.value.toString() === v.toString()){
37236 i.el.addClass('checked');
37238 if(suppressEvent !== true){
37239 this.fireEvent('check', this, i);
37250 clearInvalid : function(){
37252 if(!this.el || this.preventMark){
37256 this.el.removeClass([this.invalidClass]);
37258 this.fireEvent('valid', this);
37263 Roo.apply(Roo.bootstrap.RadioSet, {
37267 register : function(set)
37269 this.groups[set.name] = set;
37272 get: function(name)
37274 if (typeof(this.groups[name]) == 'undefined') {
37278 return this.groups[name] ;
37284 * Ext JS Library 1.1.1
37285 * Copyright(c) 2006-2007, Ext JS, LLC.
37287 * Originally Released Under LGPL - original licence link has changed is not relivant.
37290 * <script type="text/javascript">
37295 * @class Roo.bootstrap.SplitBar
37296 * @extends Roo.util.Observable
37297 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37301 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37302 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37303 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37304 split.minSize = 100;
37305 split.maxSize = 600;
37306 split.animate = true;
37307 split.on('moved', splitterMoved);
37310 * Create a new SplitBar
37311 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
37312 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
37313 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37314 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
37315 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37316 position of the SplitBar).
37318 Roo.bootstrap.SplitBar = function(cfg){
37323 // dragElement : elm
37324 // resizingElement: el,
37326 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37327 // placement : Roo.bootstrap.SplitBar.LEFT ,
37328 // existingProxy ???
37331 this.el = Roo.get(cfg.dragElement, true);
37332 this.el.dom.unselectable = "on";
37334 this.resizingEl = Roo.get(cfg.resizingElement, true);
37338 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37339 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37342 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37345 * The minimum size of the resizing element. (Defaults to 0)
37351 * The maximum size of the resizing element. (Defaults to 2000)
37354 this.maxSize = 2000;
37357 * Whether to animate the transition to the new size
37360 this.animate = false;
37363 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37366 this.useShim = false;
37371 if(!cfg.existingProxy){
37373 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37375 this.proxy = Roo.get(cfg.existingProxy).dom;
37378 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37381 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37384 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37387 this.dragSpecs = {};
37390 * @private The adapter to use to positon and resize elements
37392 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37393 this.adapter.init(this);
37395 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37397 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37398 this.el.addClass("roo-splitbar-h");
37401 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37402 this.el.addClass("roo-splitbar-v");
37408 * Fires when the splitter is moved (alias for {@link #event-moved})
37409 * @param {Roo.bootstrap.SplitBar} this
37410 * @param {Number} newSize the new width or height
37415 * Fires when the splitter is moved
37416 * @param {Roo.bootstrap.SplitBar} this
37417 * @param {Number} newSize the new width or height
37421 * @event beforeresize
37422 * Fires before the splitter is dragged
37423 * @param {Roo.bootstrap.SplitBar} this
37425 "beforeresize" : true,
37427 "beforeapply" : true
37430 Roo.util.Observable.call(this);
37433 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37434 onStartProxyDrag : function(x, y){
37435 this.fireEvent("beforeresize", this);
37437 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37439 o.enableDisplayMode("block");
37440 // all splitbars share the same overlay
37441 Roo.bootstrap.SplitBar.prototype.overlay = o;
37443 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37444 this.overlay.show();
37445 Roo.get(this.proxy).setDisplayed("block");
37446 var size = this.adapter.getElementSize(this);
37447 this.activeMinSize = this.getMinimumSize();;
37448 this.activeMaxSize = this.getMaximumSize();;
37449 var c1 = size - this.activeMinSize;
37450 var c2 = Math.max(this.activeMaxSize - size, 0);
37451 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37452 this.dd.resetConstraints();
37453 this.dd.setXConstraint(
37454 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37455 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37457 this.dd.setYConstraint(0, 0);
37459 this.dd.resetConstraints();
37460 this.dd.setXConstraint(0, 0);
37461 this.dd.setYConstraint(
37462 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37463 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37466 this.dragSpecs.startSize = size;
37467 this.dragSpecs.startPoint = [x, y];
37468 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37472 * @private Called after the drag operation by the DDProxy
37474 onEndProxyDrag : function(e){
37475 Roo.get(this.proxy).setDisplayed(false);
37476 var endPoint = Roo.lib.Event.getXY(e);
37478 this.overlay.hide();
37481 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37482 newSize = this.dragSpecs.startSize +
37483 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37484 endPoint[0] - this.dragSpecs.startPoint[0] :
37485 this.dragSpecs.startPoint[0] - endPoint[0]
37488 newSize = this.dragSpecs.startSize +
37489 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37490 endPoint[1] - this.dragSpecs.startPoint[1] :
37491 this.dragSpecs.startPoint[1] - endPoint[1]
37494 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37495 if(newSize != this.dragSpecs.startSize){
37496 if(this.fireEvent('beforeapply', this, newSize) !== false){
37497 this.adapter.setElementSize(this, newSize);
37498 this.fireEvent("moved", this, newSize);
37499 this.fireEvent("resize", this, newSize);
37505 * Get the adapter this SplitBar uses
37506 * @return The adapter object
37508 getAdapter : function(){
37509 return this.adapter;
37513 * Set the adapter this SplitBar uses
37514 * @param {Object} adapter A SplitBar adapter object
37516 setAdapter : function(adapter){
37517 this.adapter = adapter;
37518 this.adapter.init(this);
37522 * Gets the minimum size for the resizing element
37523 * @return {Number} The minimum size
37525 getMinimumSize : function(){
37526 return this.minSize;
37530 * Sets the minimum size for the resizing element
37531 * @param {Number} minSize The minimum size
37533 setMinimumSize : function(minSize){
37534 this.minSize = minSize;
37538 * Gets the maximum size for the resizing element
37539 * @return {Number} The maximum size
37541 getMaximumSize : function(){
37542 return this.maxSize;
37546 * Sets the maximum size for the resizing element
37547 * @param {Number} maxSize The maximum size
37549 setMaximumSize : function(maxSize){
37550 this.maxSize = maxSize;
37554 * Sets the initialize size for the resizing element
37555 * @param {Number} size The initial size
37557 setCurrentSize : function(size){
37558 var oldAnimate = this.animate;
37559 this.animate = false;
37560 this.adapter.setElementSize(this, size);
37561 this.animate = oldAnimate;
37565 * Destroy this splitbar.
37566 * @param {Boolean} removeEl True to remove the element
37568 destroy : function(removeEl){
37570 this.shim.remove();
37573 this.proxy.parentNode.removeChild(this.proxy);
37581 * @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.
37583 Roo.bootstrap.SplitBar.createProxy = function(dir){
37584 var proxy = new Roo.Element(document.createElement("div"));
37585 proxy.unselectable();
37586 var cls = 'roo-splitbar-proxy';
37587 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37588 document.body.appendChild(proxy.dom);
37593 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37594 * Default Adapter. It assumes the splitter and resizing element are not positioned
37595 * elements and only gets/sets the width of the element. Generally used for table based layouts.
37597 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37600 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37601 // do nothing for now
37602 init : function(s){
37606 * Called before drag operations to get the current size of the resizing element.
37607 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37609 getElementSize : function(s){
37610 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37611 return s.resizingEl.getWidth();
37613 return s.resizingEl.getHeight();
37618 * Called after drag operations to set the size of the resizing element.
37619 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37620 * @param {Number} newSize The new size to set
37621 * @param {Function} onComplete A function to be invoked when resizing is complete
37623 setElementSize : function(s, newSize, onComplete){
37624 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37626 s.resizingEl.setWidth(newSize);
37628 onComplete(s, newSize);
37631 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37636 s.resizingEl.setHeight(newSize);
37638 onComplete(s, newSize);
37641 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37648 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37649 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37650 * Adapter that moves the splitter element to align with the resized sizing element.
37651 * Used with an absolute positioned SplitBar.
37652 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37653 * document.body, make sure you assign an id to the body element.
37655 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37656 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37657 this.container = Roo.get(container);
37660 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37661 init : function(s){
37662 this.basic.init(s);
37665 getElementSize : function(s){
37666 return this.basic.getElementSize(s);
37669 setElementSize : function(s, newSize, onComplete){
37670 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37673 moveSplitter : function(s){
37674 var yes = Roo.bootstrap.SplitBar;
37675 switch(s.placement){
37677 s.el.setX(s.resizingEl.getRight());
37680 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37683 s.el.setY(s.resizingEl.getBottom());
37686 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37693 * Orientation constant - Create a vertical SplitBar
37697 Roo.bootstrap.SplitBar.VERTICAL = 1;
37700 * Orientation constant - Create a horizontal SplitBar
37704 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37707 * Placement constant - The resizing element is to the left of the splitter element
37711 Roo.bootstrap.SplitBar.LEFT = 1;
37714 * Placement constant - The resizing element is to the right of the splitter element
37718 Roo.bootstrap.SplitBar.RIGHT = 2;
37721 * Placement constant - The resizing element is positioned above the splitter element
37725 Roo.bootstrap.SplitBar.TOP = 3;
37728 * Placement constant - The resizing element is positioned under splitter element
37732 Roo.bootstrap.SplitBar.BOTTOM = 4;
37733 Roo.namespace("Roo.bootstrap.layout");/*
37735 * Ext JS Library 1.1.1
37736 * Copyright(c) 2006-2007, Ext JS, LLC.
37738 * Originally Released Under LGPL - original licence link has changed is not relivant.
37741 * <script type="text/javascript">
37745 * @class Roo.bootstrap.layout.Manager
37746 * @extends Roo.bootstrap.Component
37747 * Base class for layout managers.
37749 Roo.bootstrap.layout.Manager = function(config)
37751 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37757 /** false to disable window resize monitoring @type Boolean */
37758 this.monitorWindowResize = true;
37763 * Fires when a layout is performed.
37764 * @param {Roo.LayoutManager} this
37768 * @event regionresized
37769 * Fires when the user resizes a region.
37770 * @param {Roo.LayoutRegion} region The resized region
37771 * @param {Number} newSize The new size (width for east/west, height for north/south)
37773 "regionresized" : true,
37775 * @event regioncollapsed
37776 * Fires when a region is collapsed.
37777 * @param {Roo.LayoutRegion} region The collapsed region
37779 "regioncollapsed" : true,
37781 * @event regionexpanded
37782 * Fires when a region is expanded.
37783 * @param {Roo.LayoutRegion} region The expanded region
37785 "regionexpanded" : true
37787 this.updating = false;
37790 this.el = Roo.get(config.el);
37796 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37801 monitorWindowResize : true,
37807 onRender : function(ct, position)
37810 this.el = Roo.get(ct);
37813 //this.fireEvent('render',this);
37817 initEvents: function()
37821 // ie scrollbar fix
37822 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37823 document.body.scroll = "no";
37824 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37825 this.el.position('relative');
37827 this.id = this.el.id;
37828 this.el.addClass("roo-layout-container");
37829 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37830 if(this.el.dom != document.body ) {
37831 this.el.on('resize', this.layout,this);
37832 this.el.on('show', this.layout,this);
37838 * Returns true if this layout is currently being updated
37839 * @return {Boolean}
37841 isUpdating : function(){
37842 return this.updating;
37846 * Suspend the LayoutManager from doing auto-layouts while
37847 * making multiple add or remove calls
37849 beginUpdate : function(){
37850 this.updating = true;
37854 * Restore auto-layouts and optionally disable the manager from performing a layout
37855 * @param {Boolean} noLayout true to disable a layout update
37857 endUpdate : function(noLayout){
37858 this.updating = false;
37864 layout: function(){
37868 onRegionResized : function(region, newSize){
37869 this.fireEvent("regionresized", region, newSize);
37873 onRegionCollapsed : function(region){
37874 this.fireEvent("regioncollapsed", region);
37877 onRegionExpanded : function(region){
37878 this.fireEvent("regionexpanded", region);
37882 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37883 * performs box-model adjustments.
37884 * @return {Object} The size as an object {width: (the width), height: (the height)}
37886 getViewSize : function()
37889 if(this.el.dom != document.body){
37890 size = this.el.getSize();
37892 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37894 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37895 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37900 * Returns the Element this layout is bound to.
37901 * @return {Roo.Element}
37903 getEl : function(){
37908 * Returns the specified region.
37909 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37910 * @return {Roo.LayoutRegion}
37912 getRegion : function(target){
37913 return this.regions[target.toLowerCase()];
37916 onWindowResize : function(){
37917 if(this.monitorWindowResize){
37924 * Ext JS Library 1.1.1
37925 * Copyright(c) 2006-2007, Ext JS, LLC.
37927 * Originally Released Under LGPL - original licence link has changed is not relivant.
37930 * <script type="text/javascript">
37933 * @class Roo.bootstrap.layout.Border
37934 * @extends Roo.bootstrap.layout.Manager
37935 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37936 * please see: examples/bootstrap/nested.html<br><br>
37938 <b>The container the layout is rendered into can be either the body element or any other element.
37939 If it is not the body element, the container needs to either be an absolute positioned element,
37940 or you will need to add "position:relative" to the css of the container. You will also need to specify
37941 the container size if it is not the body element.</b>
37944 * Create a new Border
37945 * @param {Object} config Configuration options
37947 Roo.bootstrap.layout.Border = function(config){
37948 config = config || {};
37949 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37953 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37954 if(config[region]){
37955 config[region].region = region;
37956 this.addRegion(config[region]);
37962 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
37964 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37966 parent : false, // this might point to a 'nest' or a ???
37969 * Creates and adds a new region if it doesn't already exist.
37970 * @param {String} target The target region key (north, south, east, west or center).
37971 * @param {Object} config The regions config object
37972 * @return {BorderLayoutRegion} The new region
37974 addRegion : function(config)
37976 if(!this.regions[config.region]){
37977 var r = this.factory(config);
37978 this.bindRegion(r);
37980 return this.regions[config.region];
37984 bindRegion : function(r){
37985 this.regions[r.config.region] = r;
37987 r.on("visibilitychange", this.layout, this);
37988 r.on("paneladded", this.layout, this);
37989 r.on("panelremoved", this.layout, this);
37990 r.on("invalidated", this.layout, this);
37991 r.on("resized", this.onRegionResized, this);
37992 r.on("collapsed", this.onRegionCollapsed, this);
37993 r.on("expanded", this.onRegionExpanded, this);
37997 * Performs a layout update.
37999 layout : function()
38001 if(this.updating) {
38005 // render all the rebions if they have not been done alreayd?
38006 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38007 if(this.regions[region] && !this.regions[region].bodyEl){
38008 this.regions[region].onRender(this.el)
38012 var size = this.getViewSize();
38013 var w = size.width;
38014 var h = size.height;
38019 //var x = 0, y = 0;
38021 var rs = this.regions;
38022 var north = rs["north"];
38023 var south = rs["south"];
38024 var west = rs["west"];
38025 var east = rs["east"];
38026 var center = rs["center"];
38027 //if(this.hideOnLayout){ // not supported anymore
38028 //c.el.setStyle("display", "none");
38030 if(north && north.isVisible()){
38031 var b = north.getBox();
38032 var m = north.getMargins();
38033 b.width = w - (m.left+m.right);
38036 centerY = b.height + b.y + m.bottom;
38037 centerH -= centerY;
38038 north.updateBox(this.safeBox(b));
38040 if(south && south.isVisible()){
38041 var b = south.getBox();
38042 var m = south.getMargins();
38043 b.width = w - (m.left+m.right);
38045 var totalHeight = (b.height + m.top + m.bottom);
38046 b.y = h - totalHeight + m.top;
38047 centerH -= totalHeight;
38048 south.updateBox(this.safeBox(b));
38050 if(west && west.isVisible()){
38051 var b = west.getBox();
38052 var m = west.getMargins();
38053 b.height = centerH - (m.top+m.bottom);
38055 b.y = centerY + m.top;
38056 var totalWidth = (b.width + m.left + m.right);
38057 centerX += totalWidth;
38058 centerW -= totalWidth;
38059 west.updateBox(this.safeBox(b));
38061 if(east && east.isVisible()){
38062 var b = east.getBox();
38063 var m = east.getMargins();
38064 b.height = centerH - (m.top+m.bottom);
38065 var totalWidth = (b.width + m.left + m.right);
38066 b.x = w - totalWidth + m.left;
38067 b.y = centerY + m.top;
38068 centerW -= totalWidth;
38069 east.updateBox(this.safeBox(b));
38072 var m = center.getMargins();
38074 x: centerX + m.left,
38075 y: centerY + m.top,
38076 width: centerW - (m.left+m.right),
38077 height: centerH - (m.top+m.bottom)
38079 //if(this.hideOnLayout){
38080 //center.el.setStyle("display", "block");
38082 center.updateBox(this.safeBox(centerBox));
38085 this.fireEvent("layout", this);
38089 safeBox : function(box){
38090 box.width = Math.max(0, box.width);
38091 box.height = Math.max(0, box.height);
38096 * Adds a ContentPanel (or subclass) to this layout.
38097 * @param {String} target The target region key (north, south, east, west or center).
38098 * @param {Roo.ContentPanel} panel The panel to add
38099 * @return {Roo.ContentPanel} The added panel
38101 add : function(target, panel){
38103 target = target.toLowerCase();
38104 return this.regions[target].add(panel);
38108 * Remove a ContentPanel (or subclass) to this layout.
38109 * @param {String} target The target region key (north, south, east, west or center).
38110 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38111 * @return {Roo.ContentPanel} The removed panel
38113 remove : function(target, panel){
38114 target = target.toLowerCase();
38115 return this.regions[target].remove(panel);
38119 * Searches all regions for a panel with the specified id
38120 * @param {String} panelId
38121 * @return {Roo.ContentPanel} The panel or null if it wasn't found
38123 findPanel : function(panelId){
38124 var rs = this.regions;
38125 for(var target in rs){
38126 if(typeof rs[target] != "function"){
38127 var p = rs[target].getPanel(panelId);
38137 * Searches all regions for a panel with the specified id and activates (shows) it.
38138 * @param {String/ContentPanel} panelId The panels id or the panel itself
38139 * @return {Roo.ContentPanel} The shown panel or null
38141 showPanel : function(panelId) {
38142 var rs = this.regions;
38143 for(var target in rs){
38144 var r = rs[target];
38145 if(typeof r != "function"){
38146 if(r.hasPanel(panelId)){
38147 return r.showPanel(panelId);
38155 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38156 * @param {Roo.state.Provider} provider (optional) An alternate state provider
38159 restoreState : function(provider){
38161 provider = Roo.state.Manager;
38163 var sm = new Roo.LayoutStateManager();
38164 sm.init(this, provider);
38170 * Adds a xtype elements to the layout.
38174 xtype : 'ContentPanel',
38181 xtype : 'NestedLayoutPanel',
38187 items : [ ... list of content panels or nested layout panels.. ]
38191 * @param {Object} cfg Xtype definition of item to add.
38193 addxtype : function(cfg)
38195 // basically accepts a pannel...
38196 // can accept a layout region..!?!?
38197 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38200 // theory? children can only be panels??
38202 //if (!cfg.xtype.match(/Panel$/)) {
38207 if (typeof(cfg.region) == 'undefined') {
38208 Roo.log("Failed to add Panel, region was not set");
38212 var region = cfg.region;
38218 xitems = cfg.items;
38223 if ( region == 'center') {
38224 Roo.log("Center: " + cfg.title);
38230 case 'Content': // ContentPanel (el, cfg)
38231 case 'Scroll': // ContentPanel (el, cfg)
38233 cfg.autoCreate = cfg.autoCreate || true;
38234 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38236 // var el = this.el.createChild();
38237 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38240 this.add(region, ret);
38244 case 'TreePanel': // our new panel!
38245 cfg.el = this.el.createChild();
38246 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38247 this.add(region, ret);
38252 // create a new Layout (which is a Border Layout...
38254 var clayout = cfg.layout;
38255 clayout.el = this.el.createChild();
38256 clayout.items = clayout.items || [];
38260 // replace this exitems with the clayout ones..
38261 xitems = clayout.items;
38263 // force background off if it's in center...
38264 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38265 cfg.background = false;
38267 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
38270 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38271 //console.log('adding nested layout panel ' + cfg.toSource());
38272 this.add(region, ret);
38273 nb = {}; /// find first...
38278 // needs grid and region
38280 //var el = this.getRegion(region).el.createChild();
38282 *var el = this.el.createChild();
38283 // create the grid first...
38284 cfg.grid.container = el;
38285 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38288 if (region == 'center' && this.active ) {
38289 cfg.background = false;
38292 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38294 this.add(region, ret);
38296 if (cfg.background) {
38297 // render grid on panel activation (if panel background)
38298 ret.on('activate', function(gp) {
38299 if (!gp.grid.rendered) {
38300 // gp.grid.render(el);
38304 // cfg.grid.render(el);
38310 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38311 // it was the old xcomponent building that caused this before.
38312 // espeically if border is the top element in the tree.
38322 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38324 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38325 this.add(region, ret);
38329 throw "Can not add '" + cfg.xtype + "' to Border";
38335 this.beginUpdate();
38339 Roo.each(xitems, function(i) {
38340 region = nb && i.region ? i.region : false;
38342 var add = ret.addxtype(i);
38345 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38346 if (!i.background) {
38347 abn[region] = nb[region] ;
38354 // make the last non-background panel active..
38355 //if (nb) { Roo.log(abn); }
38358 for(var r in abn) {
38359 region = this.getRegion(r);
38361 // tried using nb[r], but it does not work..
38363 region.showPanel(abn[r]);
38374 factory : function(cfg)
38377 var validRegions = Roo.bootstrap.layout.Border.regions;
38379 var target = cfg.region;
38382 var r = Roo.bootstrap.layout;
38386 return new r.North(cfg);
38388 return new r.South(cfg);
38390 return new r.East(cfg);
38392 return new r.West(cfg);
38394 return new r.Center(cfg);
38396 throw 'Layout region "'+target+'" not supported.';
38403 * Ext JS Library 1.1.1
38404 * Copyright(c) 2006-2007, Ext JS, LLC.
38406 * Originally Released Under LGPL - original licence link has changed is not relivant.
38409 * <script type="text/javascript">
38413 * @class Roo.bootstrap.layout.Basic
38414 * @extends Roo.util.Observable
38415 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38416 * and does not have a titlebar, tabs or any other features. All it does is size and position
38417 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38418 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38419 * @cfg {string} region the region that it inhabits..
38420 * @cfg {bool} skipConfig skip config?
38424 Roo.bootstrap.layout.Basic = function(config){
38426 this.mgr = config.mgr;
38428 this.position = config.region;
38430 var skipConfig = config.skipConfig;
38434 * @scope Roo.BasicLayoutRegion
38438 * @event beforeremove
38439 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38440 * @param {Roo.LayoutRegion} this
38441 * @param {Roo.ContentPanel} panel The panel
38442 * @param {Object} e The cancel event object
38444 "beforeremove" : true,
38446 * @event invalidated
38447 * Fires when the layout for this region is changed.
38448 * @param {Roo.LayoutRegion} this
38450 "invalidated" : true,
38452 * @event visibilitychange
38453 * Fires when this region is shown or hidden
38454 * @param {Roo.LayoutRegion} this
38455 * @param {Boolean} visibility true or false
38457 "visibilitychange" : true,
38459 * @event paneladded
38460 * Fires when a panel is added.
38461 * @param {Roo.LayoutRegion} this
38462 * @param {Roo.ContentPanel} panel The panel
38464 "paneladded" : true,
38466 * @event panelremoved
38467 * Fires when a panel is removed.
38468 * @param {Roo.LayoutRegion} this
38469 * @param {Roo.ContentPanel} panel The panel
38471 "panelremoved" : true,
38473 * @event beforecollapse
38474 * Fires when this region before collapse.
38475 * @param {Roo.LayoutRegion} this
38477 "beforecollapse" : true,
38480 * Fires when this region is collapsed.
38481 * @param {Roo.LayoutRegion} this
38483 "collapsed" : true,
38486 * Fires when this region is expanded.
38487 * @param {Roo.LayoutRegion} this
38492 * Fires when this region is slid into view.
38493 * @param {Roo.LayoutRegion} this
38495 "slideshow" : true,
38498 * Fires when this region slides out of view.
38499 * @param {Roo.LayoutRegion} this
38501 "slidehide" : true,
38503 * @event panelactivated
38504 * Fires when a panel is activated.
38505 * @param {Roo.LayoutRegion} this
38506 * @param {Roo.ContentPanel} panel The activated panel
38508 "panelactivated" : true,
38511 * Fires when the user resizes this region.
38512 * @param {Roo.LayoutRegion} this
38513 * @param {Number} newSize The new size (width for east/west, height for north/south)
38517 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38518 this.panels = new Roo.util.MixedCollection();
38519 this.panels.getKey = this.getPanelId.createDelegate(this);
38521 this.activePanel = null;
38522 // ensure listeners are added...
38524 if (config.listeners || config.events) {
38525 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38526 listeners : config.listeners || {},
38527 events : config.events || {}
38531 if(skipConfig !== true){
38532 this.applyConfig(config);
38536 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38538 getPanelId : function(p){
38542 applyConfig : function(config){
38543 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38544 this.config = config;
38549 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38550 * the width, for horizontal (north, south) the height.
38551 * @param {Number} newSize The new width or height
38553 resizeTo : function(newSize){
38554 var el = this.el ? this.el :
38555 (this.activePanel ? this.activePanel.getEl() : null);
38557 switch(this.position){
38560 el.setWidth(newSize);
38561 this.fireEvent("resized", this, newSize);
38565 el.setHeight(newSize);
38566 this.fireEvent("resized", this, newSize);
38572 getBox : function(){
38573 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38576 getMargins : function(){
38577 return this.margins;
38580 updateBox : function(box){
38582 var el = this.activePanel.getEl();
38583 el.dom.style.left = box.x + "px";
38584 el.dom.style.top = box.y + "px";
38585 this.activePanel.setSize(box.width, box.height);
38589 * Returns the container element for this region.
38590 * @return {Roo.Element}
38592 getEl : function(){
38593 return this.activePanel;
38597 * Returns true if this region is currently visible.
38598 * @return {Boolean}
38600 isVisible : function(){
38601 return this.activePanel ? true : false;
38604 setActivePanel : function(panel){
38605 panel = this.getPanel(panel);
38606 if(this.activePanel && this.activePanel != panel){
38607 this.activePanel.setActiveState(false);
38608 this.activePanel.getEl().setLeftTop(-10000,-10000);
38610 this.activePanel = panel;
38611 panel.setActiveState(true);
38613 panel.setSize(this.box.width, this.box.height);
38615 this.fireEvent("panelactivated", this, panel);
38616 this.fireEvent("invalidated");
38620 * Show the specified panel.
38621 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38622 * @return {Roo.ContentPanel} The shown panel or null
38624 showPanel : function(panel){
38625 panel = this.getPanel(panel);
38627 this.setActivePanel(panel);
38633 * Get the active panel for this region.
38634 * @return {Roo.ContentPanel} The active panel or null
38636 getActivePanel : function(){
38637 return this.activePanel;
38641 * Add the passed ContentPanel(s)
38642 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38643 * @return {Roo.ContentPanel} The panel added (if only one was added)
38645 add : function(panel){
38646 if(arguments.length > 1){
38647 for(var i = 0, len = arguments.length; i < len; i++) {
38648 this.add(arguments[i]);
38652 if(this.hasPanel(panel)){
38653 this.showPanel(panel);
38656 var el = panel.getEl();
38657 if(el.dom.parentNode != this.mgr.el.dom){
38658 this.mgr.el.dom.appendChild(el.dom);
38660 if(panel.setRegion){
38661 panel.setRegion(this);
38663 this.panels.add(panel);
38664 el.setStyle("position", "absolute");
38665 if(!panel.background){
38666 this.setActivePanel(panel);
38667 if(this.config.initialSize && this.panels.getCount()==1){
38668 this.resizeTo(this.config.initialSize);
38671 this.fireEvent("paneladded", this, panel);
38676 * Returns true if the panel is in this region.
38677 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38678 * @return {Boolean}
38680 hasPanel : function(panel){
38681 if(typeof panel == "object"){ // must be panel obj
38682 panel = panel.getId();
38684 return this.getPanel(panel) ? true : false;
38688 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38689 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38690 * @param {Boolean} preservePanel Overrides the config preservePanel option
38691 * @return {Roo.ContentPanel} The panel that was removed
38693 remove : function(panel, preservePanel){
38694 panel = this.getPanel(panel);
38699 this.fireEvent("beforeremove", this, panel, e);
38700 if(e.cancel === true){
38703 var panelId = panel.getId();
38704 this.panels.removeKey(panelId);
38709 * Returns the panel specified or null if it's not in this region.
38710 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38711 * @return {Roo.ContentPanel}
38713 getPanel : function(id){
38714 if(typeof id == "object"){ // must be panel obj
38717 return this.panels.get(id);
38721 * Returns this regions position (north/south/east/west/center).
38724 getPosition: function(){
38725 return this.position;
38729 * Ext JS Library 1.1.1
38730 * Copyright(c) 2006-2007, Ext JS, LLC.
38732 * Originally Released Under LGPL - original licence link has changed is not relivant.
38735 * <script type="text/javascript">
38739 * @class Roo.bootstrap.layout.Region
38740 * @extends Roo.bootstrap.layout.Basic
38741 * This class represents a region in a layout manager.
38743 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38744 * @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})
38745 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
38746 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
38747 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
38748 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
38749 * @cfg {String} title The title for the region (overrides panel titles)
38750 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
38751 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38752 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
38753 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38754 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
38755 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38756 * the space available, similar to FireFox 1.5 tabs (defaults to false)
38757 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
38758 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
38759 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
38761 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
38762 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
38763 * @cfg {Boolean} disableTabTips True to disable tab tooltips
38764 * @cfg {Number} width For East/West panels
38765 * @cfg {Number} height For North/South panels
38766 * @cfg {Boolean} split To show the splitter
38767 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
38769 * @cfg {string} cls Extra CSS classes to add to region
38771 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38772 * @cfg {string} region the region that it inhabits..
38775 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
38776 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
38778 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
38779 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
38780 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
38782 Roo.bootstrap.layout.Region = function(config)
38784 this.applyConfig(config);
38786 var mgr = config.mgr;
38787 var pos = config.region;
38788 config.skipConfig = true;
38789 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38792 this.onRender(mgr.el);
38795 this.visible = true;
38796 this.collapsed = false;
38797 this.unrendered_panels = [];
38800 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38802 position: '', // set by wrapper (eg. north/south etc..)
38803 unrendered_panels : null, // unrendered panels.
38805 tabPosition : false,
38807 mgr: false, // points to 'Border'
38810 createBody : function(){
38811 /** This region's body element
38812 * @type Roo.Element */
38813 this.bodyEl = this.el.createChild({
38815 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38819 onRender: function(ctr, pos)
38821 var dh = Roo.DomHelper;
38822 /** This region's container element
38823 * @type Roo.Element */
38824 this.el = dh.append(ctr.dom, {
38826 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38828 /** This region's title element
38829 * @type Roo.Element */
38831 this.titleEl = dh.append(this.el.dom, {
38833 unselectable: "on",
38834 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38836 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38837 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38841 this.titleEl.enableDisplayMode();
38842 /** This region's title text element
38843 * @type HTMLElement */
38844 this.titleTextEl = this.titleEl.dom.firstChild;
38845 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38847 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38848 this.closeBtn.enableDisplayMode();
38849 this.closeBtn.on("click", this.closeClicked, this);
38850 this.closeBtn.hide();
38852 this.createBody(this.config);
38853 if(this.config.hideWhenEmpty){
38855 this.on("paneladded", this.validateVisibility, this);
38856 this.on("panelremoved", this.validateVisibility, this);
38858 if(this.autoScroll){
38859 this.bodyEl.setStyle("overflow", "auto");
38861 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38863 //if(c.titlebar !== false){
38864 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38865 this.titleEl.hide();
38867 this.titleEl.show();
38868 if(this.config.title){
38869 this.titleTextEl.innerHTML = this.config.title;
38873 if(this.config.collapsed){
38874 this.collapse(true);
38876 if(this.config.hidden){
38880 if (this.unrendered_panels && this.unrendered_panels.length) {
38881 for (var i =0;i< this.unrendered_panels.length; i++) {
38882 this.add(this.unrendered_panels[i]);
38884 this.unrendered_panels = null;
38890 applyConfig : function(c)
38893 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38894 var dh = Roo.DomHelper;
38895 if(c.titlebar !== false){
38896 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38897 this.collapseBtn.on("click", this.collapse, this);
38898 this.collapseBtn.enableDisplayMode();
38900 if(c.showPin === true || this.showPin){
38901 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38902 this.stickBtn.enableDisplayMode();
38903 this.stickBtn.on("click", this.expand, this);
38904 this.stickBtn.hide();
38909 /** This region's collapsed element
38910 * @type Roo.Element */
38913 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38914 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38917 if(c.floatable !== false){
38918 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38919 this.collapsedEl.on("click", this.collapseClick, this);
38922 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38923 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38924 id: "message", unselectable: "on", style:{"float":"left"}});
38925 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38927 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38928 this.expandBtn.on("click", this.expand, this);
38932 if(this.collapseBtn){
38933 this.collapseBtn.setVisible(c.collapsible == true);
38936 this.cmargins = c.cmargins || this.cmargins ||
38937 (this.position == "west" || this.position == "east" ?
38938 {top: 0, left: 2, right:2, bottom: 0} :
38939 {top: 2, left: 0, right:0, bottom: 2});
38941 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38944 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38946 this.autoScroll = c.autoScroll || false;
38951 this.duration = c.duration || .30;
38952 this.slideDuration = c.slideDuration || .45;
38957 * Returns true if this region is currently visible.
38958 * @return {Boolean}
38960 isVisible : function(){
38961 return this.visible;
38965 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38966 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
38968 //setCollapsedTitle : function(title){
38969 // title = title || " ";
38970 // if(this.collapsedTitleTextEl){
38971 // this.collapsedTitleTextEl.innerHTML = title;
38975 getBox : function(){
38977 // if(!this.collapsed){
38978 b = this.el.getBox(false, true);
38980 // b = this.collapsedEl.getBox(false, true);
38985 getMargins : function(){
38986 return this.margins;
38987 //return this.collapsed ? this.cmargins : this.margins;
38990 highlight : function(){
38991 this.el.addClass("x-layout-panel-dragover");
38994 unhighlight : function(){
38995 this.el.removeClass("x-layout-panel-dragover");
38998 updateBox : function(box)
39000 if (!this.bodyEl) {
39001 return; // not rendered yet..
39005 if(!this.collapsed){
39006 this.el.dom.style.left = box.x + "px";
39007 this.el.dom.style.top = box.y + "px";
39008 this.updateBody(box.width, box.height);
39010 this.collapsedEl.dom.style.left = box.x + "px";
39011 this.collapsedEl.dom.style.top = box.y + "px";
39012 this.collapsedEl.setSize(box.width, box.height);
39015 this.tabs.autoSizeTabs();
39019 updateBody : function(w, h)
39022 this.el.setWidth(w);
39023 w -= this.el.getBorderWidth("rl");
39024 if(this.config.adjustments){
39025 w += this.config.adjustments[0];
39028 if(h !== null && h > 0){
39029 this.el.setHeight(h);
39030 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39031 h -= this.el.getBorderWidth("tb");
39032 if(this.config.adjustments){
39033 h += this.config.adjustments[1];
39035 this.bodyEl.setHeight(h);
39037 h = this.tabs.syncHeight(h);
39040 if(this.panelSize){
39041 w = w !== null ? w : this.panelSize.width;
39042 h = h !== null ? h : this.panelSize.height;
39044 if(this.activePanel){
39045 var el = this.activePanel.getEl();
39046 w = w !== null ? w : el.getWidth();
39047 h = h !== null ? h : el.getHeight();
39048 this.panelSize = {width: w, height: h};
39049 this.activePanel.setSize(w, h);
39051 if(Roo.isIE && this.tabs){
39052 this.tabs.el.repaint();
39057 * Returns the container element for this region.
39058 * @return {Roo.Element}
39060 getEl : function(){
39065 * Hides this region.
39068 //if(!this.collapsed){
39069 this.el.dom.style.left = "-2000px";
39072 // this.collapsedEl.dom.style.left = "-2000px";
39073 // this.collapsedEl.hide();
39075 this.visible = false;
39076 this.fireEvent("visibilitychange", this, false);
39080 * Shows this region if it was previously hidden.
39083 //if(!this.collapsed){
39086 // this.collapsedEl.show();
39088 this.visible = true;
39089 this.fireEvent("visibilitychange", this, true);
39092 closeClicked : function(){
39093 if(this.activePanel){
39094 this.remove(this.activePanel);
39098 collapseClick : function(e){
39100 e.stopPropagation();
39103 e.stopPropagation();
39109 * Collapses this region.
39110 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39113 collapse : function(skipAnim, skipCheck = false){
39114 if(this.collapsed) {
39118 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39120 this.collapsed = true;
39122 this.split.el.hide();
39124 if(this.config.animate && skipAnim !== true){
39125 this.fireEvent("invalidated", this);
39126 this.animateCollapse();
39128 this.el.setLocation(-20000,-20000);
39130 this.collapsedEl.show();
39131 this.fireEvent("collapsed", this);
39132 this.fireEvent("invalidated", this);
39138 animateCollapse : function(){
39143 * Expands this region if it was previously collapsed.
39144 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39145 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39148 expand : function(e, skipAnim){
39150 e.stopPropagation();
39152 if(!this.collapsed || this.el.hasActiveFx()) {
39156 this.afterSlideIn();
39159 this.collapsed = false;
39160 if(this.config.animate && skipAnim !== true){
39161 this.animateExpand();
39165 this.split.el.show();
39167 this.collapsedEl.setLocation(-2000,-2000);
39168 this.collapsedEl.hide();
39169 this.fireEvent("invalidated", this);
39170 this.fireEvent("expanded", this);
39174 animateExpand : function(){
39178 initTabs : function()
39180 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39182 var ts = new Roo.bootstrap.panel.Tabs({
39183 el: this.bodyEl.dom,
39185 tabPosition: this.tabPosition ? this.tabPosition : 'top',
39186 disableTooltips: this.config.disableTabTips,
39187 toolbar : this.config.toolbar
39190 if(this.config.hideTabs){
39191 ts.stripWrap.setDisplayed(false);
39194 ts.resizeTabs = this.config.resizeTabs === true;
39195 ts.minTabWidth = this.config.minTabWidth || 40;
39196 ts.maxTabWidth = this.config.maxTabWidth || 250;
39197 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39198 ts.monitorResize = false;
39199 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39200 ts.bodyEl.addClass('roo-layout-tabs-body');
39201 this.panels.each(this.initPanelAsTab, this);
39204 initPanelAsTab : function(panel){
39205 var ti = this.tabs.addTab(
39209 this.config.closeOnTab && panel.isClosable(),
39212 if(panel.tabTip !== undefined){
39213 ti.setTooltip(panel.tabTip);
39215 ti.on("activate", function(){
39216 this.setActivePanel(panel);
39219 if(this.config.closeOnTab){
39220 ti.on("beforeclose", function(t, e){
39222 this.remove(panel);
39226 panel.tabItem = ti;
39231 updatePanelTitle : function(panel, title)
39233 if(this.activePanel == panel){
39234 this.updateTitle(title);
39237 var ti = this.tabs.getTab(panel.getEl().id);
39239 if(panel.tabTip !== undefined){
39240 ti.setTooltip(panel.tabTip);
39245 updateTitle : function(title){
39246 if(this.titleTextEl && !this.config.title){
39247 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
39251 setActivePanel : function(panel)
39253 panel = this.getPanel(panel);
39254 if(this.activePanel && this.activePanel != panel){
39255 if(this.activePanel.setActiveState(false) === false){
39259 this.activePanel = panel;
39260 panel.setActiveState(true);
39261 if(this.panelSize){
39262 panel.setSize(this.panelSize.width, this.panelSize.height);
39265 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39267 this.updateTitle(panel.getTitle());
39269 this.fireEvent("invalidated", this);
39271 this.fireEvent("panelactivated", this, panel);
39275 * Shows the specified panel.
39276 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39277 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39279 showPanel : function(panel)
39281 panel = this.getPanel(panel);
39284 var tab = this.tabs.getTab(panel.getEl().id);
39285 if(tab.isHidden()){
39286 this.tabs.unhideTab(tab.id);
39290 this.setActivePanel(panel);
39297 * Get the active panel for this region.
39298 * @return {Roo.ContentPanel} The active panel or null
39300 getActivePanel : function(){
39301 return this.activePanel;
39304 validateVisibility : function(){
39305 if(this.panels.getCount() < 1){
39306 this.updateTitle(" ");
39307 this.closeBtn.hide();
39310 if(!this.isVisible()){
39317 * Adds the passed ContentPanel(s) to this region.
39318 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39319 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39321 add : function(panel)
39323 if(arguments.length > 1){
39324 for(var i = 0, len = arguments.length; i < len; i++) {
39325 this.add(arguments[i]);
39330 // if we have not been rendered yet, then we can not really do much of this..
39331 if (!this.bodyEl) {
39332 this.unrendered_panels.push(panel);
39339 if(this.hasPanel(panel)){
39340 this.showPanel(panel);
39343 panel.setRegion(this);
39344 this.panels.add(panel);
39345 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39346 // sinle panel - no tab...?? would it not be better to render it with the tabs,
39347 // and hide them... ???
39348 this.bodyEl.dom.appendChild(panel.getEl().dom);
39349 if(panel.background !== true){
39350 this.setActivePanel(panel);
39352 this.fireEvent("paneladded", this, panel);
39359 this.initPanelAsTab(panel);
39363 if(panel.background !== true){
39364 this.tabs.activate(panel.getEl().id);
39366 this.fireEvent("paneladded", this, panel);
39371 * Hides the tab for the specified panel.
39372 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39374 hidePanel : function(panel){
39375 if(this.tabs && (panel = this.getPanel(panel))){
39376 this.tabs.hideTab(panel.getEl().id);
39381 * Unhides the tab for a previously hidden panel.
39382 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39384 unhidePanel : function(panel){
39385 if(this.tabs && (panel = this.getPanel(panel))){
39386 this.tabs.unhideTab(panel.getEl().id);
39390 clearPanels : function(){
39391 while(this.panels.getCount() > 0){
39392 this.remove(this.panels.first());
39397 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39398 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39399 * @param {Boolean} preservePanel Overrides the config preservePanel option
39400 * @return {Roo.ContentPanel} The panel that was removed
39402 remove : function(panel, preservePanel)
39404 panel = this.getPanel(panel);
39409 this.fireEvent("beforeremove", this, panel, e);
39410 if(e.cancel === true){
39413 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39414 var panelId = panel.getId();
39415 this.panels.removeKey(panelId);
39417 document.body.appendChild(panel.getEl().dom);
39420 this.tabs.removeTab(panel.getEl().id);
39421 }else if (!preservePanel){
39422 this.bodyEl.dom.removeChild(panel.getEl().dom);
39424 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39425 var p = this.panels.first();
39426 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39427 tempEl.appendChild(p.getEl().dom);
39428 this.bodyEl.update("");
39429 this.bodyEl.dom.appendChild(p.getEl().dom);
39431 this.updateTitle(p.getTitle());
39433 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39434 this.setActivePanel(p);
39436 panel.setRegion(null);
39437 if(this.activePanel == panel){
39438 this.activePanel = null;
39440 if(this.config.autoDestroy !== false && preservePanel !== true){
39441 try{panel.destroy();}catch(e){}
39443 this.fireEvent("panelremoved", this, panel);
39448 * Returns the TabPanel component used by this region
39449 * @return {Roo.TabPanel}
39451 getTabs : function(){
39455 createTool : function(parentEl, className){
39456 var btn = Roo.DomHelper.append(parentEl, {
39458 cls: "x-layout-tools-button",
39461 cls: "roo-layout-tools-button-inner " + className,
39465 btn.addClassOnOver("roo-layout-tools-button-over");
39470 * Ext JS Library 1.1.1
39471 * Copyright(c) 2006-2007, Ext JS, LLC.
39473 * Originally Released Under LGPL - original licence link has changed is not relivant.
39476 * <script type="text/javascript">
39482 * @class Roo.SplitLayoutRegion
39483 * @extends Roo.LayoutRegion
39484 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39486 Roo.bootstrap.layout.Split = function(config){
39487 this.cursor = config.cursor;
39488 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39491 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39493 splitTip : "Drag to resize.",
39494 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39495 useSplitTips : false,
39497 applyConfig : function(config){
39498 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39501 onRender : function(ctr,pos) {
39503 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39504 if(!this.config.split){
39509 var splitEl = Roo.DomHelper.append(ctr.dom, {
39511 id: this.el.id + "-split",
39512 cls: "roo-layout-split roo-layout-split-"+this.position,
39515 /** The SplitBar for this region
39516 * @type Roo.SplitBar */
39517 // does not exist yet...
39518 Roo.log([this.position, this.orientation]);
39520 this.split = new Roo.bootstrap.SplitBar({
39521 dragElement : splitEl,
39522 resizingElement: this.el,
39523 orientation : this.orientation
39526 this.split.on("moved", this.onSplitMove, this);
39527 this.split.useShim = this.config.useShim === true;
39528 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39529 if(this.useSplitTips){
39530 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39532 //if(config.collapsible){
39533 // this.split.el.on("dblclick", this.collapse, this);
39536 if(typeof this.config.minSize != "undefined"){
39537 this.split.minSize = this.config.minSize;
39539 if(typeof this.config.maxSize != "undefined"){
39540 this.split.maxSize = this.config.maxSize;
39542 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39543 this.hideSplitter();
39548 getHMaxSize : function(){
39549 var cmax = this.config.maxSize || 10000;
39550 var center = this.mgr.getRegion("center");
39551 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39554 getVMaxSize : function(){
39555 var cmax = this.config.maxSize || 10000;
39556 var center = this.mgr.getRegion("center");
39557 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39560 onSplitMove : function(split, newSize){
39561 this.fireEvent("resized", this, newSize);
39565 * Returns the {@link Roo.SplitBar} for this region.
39566 * @return {Roo.SplitBar}
39568 getSplitBar : function(){
39573 this.hideSplitter();
39574 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39577 hideSplitter : function(){
39579 this.split.el.setLocation(-2000,-2000);
39580 this.split.el.hide();
39586 this.split.el.show();
39588 Roo.bootstrap.layout.Split.superclass.show.call(this);
39591 beforeSlide: function(){
39592 if(Roo.isGecko){// firefox overflow auto bug workaround
39593 this.bodyEl.clip();
39595 this.tabs.bodyEl.clip();
39597 if(this.activePanel){
39598 this.activePanel.getEl().clip();
39600 if(this.activePanel.beforeSlide){
39601 this.activePanel.beforeSlide();
39607 afterSlide : function(){
39608 if(Roo.isGecko){// firefox overflow auto bug workaround
39609 this.bodyEl.unclip();
39611 this.tabs.bodyEl.unclip();
39613 if(this.activePanel){
39614 this.activePanel.getEl().unclip();
39615 if(this.activePanel.afterSlide){
39616 this.activePanel.afterSlide();
39622 initAutoHide : function(){
39623 if(this.autoHide !== false){
39624 if(!this.autoHideHd){
39625 var st = new Roo.util.DelayedTask(this.slideIn, this);
39626 this.autoHideHd = {
39627 "mouseout": function(e){
39628 if(!e.within(this.el, true)){
39632 "mouseover" : function(e){
39638 this.el.on(this.autoHideHd);
39642 clearAutoHide : function(){
39643 if(this.autoHide !== false){
39644 this.el.un("mouseout", this.autoHideHd.mouseout);
39645 this.el.un("mouseover", this.autoHideHd.mouseover);
39649 clearMonitor : function(){
39650 Roo.get(document).un("click", this.slideInIf, this);
39653 // these names are backwards but not changed for compat
39654 slideOut : function(){
39655 if(this.isSlid || this.el.hasActiveFx()){
39658 this.isSlid = true;
39659 if(this.collapseBtn){
39660 this.collapseBtn.hide();
39662 this.closeBtnState = this.closeBtn.getStyle('display');
39663 this.closeBtn.hide();
39665 this.stickBtn.show();
39668 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39669 this.beforeSlide();
39670 this.el.setStyle("z-index", 10001);
39671 this.el.slideIn(this.getSlideAnchor(), {
39672 callback: function(){
39674 this.initAutoHide();
39675 Roo.get(document).on("click", this.slideInIf, this);
39676 this.fireEvent("slideshow", this);
39683 afterSlideIn : function(){
39684 this.clearAutoHide();
39685 this.isSlid = false;
39686 this.clearMonitor();
39687 this.el.setStyle("z-index", "");
39688 if(this.collapseBtn){
39689 this.collapseBtn.show();
39691 this.closeBtn.setStyle('display', this.closeBtnState);
39693 this.stickBtn.hide();
39695 this.fireEvent("slidehide", this);
39698 slideIn : function(cb){
39699 if(!this.isSlid || this.el.hasActiveFx()){
39703 this.isSlid = false;
39704 this.beforeSlide();
39705 this.el.slideOut(this.getSlideAnchor(), {
39706 callback: function(){
39707 this.el.setLeftTop(-10000, -10000);
39709 this.afterSlideIn();
39717 slideInIf : function(e){
39718 if(!e.within(this.el)){
39723 animateCollapse : function(){
39724 this.beforeSlide();
39725 this.el.setStyle("z-index", 20000);
39726 var anchor = this.getSlideAnchor();
39727 this.el.slideOut(anchor, {
39728 callback : function(){
39729 this.el.setStyle("z-index", "");
39730 this.collapsedEl.slideIn(anchor, {duration:.3});
39732 this.el.setLocation(-10000,-10000);
39734 this.fireEvent("collapsed", this);
39741 animateExpand : function(){
39742 this.beforeSlide();
39743 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39744 this.el.setStyle("z-index", 20000);
39745 this.collapsedEl.hide({
39748 this.el.slideIn(this.getSlideAnchor(), {
39749 callback : function(){
39750 this.el.setStyle("z-index", "");
39753 this.split.el.show();
39755 this.fireEvent("invalidated", this);
39756 this.fireEvent("expanded", this);
39784 getAnchor : function(){
39785 return this.anchors[this.position];
39788 getCollapseAnchor : function(){
39789 return this.canchors[this.position];
39792 getSlideAnchor : function(){
39793 return this.sanchors[this.position];
39796 getAlignAdj : function(){
39797 var cm = this.cmargins;
39798 switch(this.position){
39814 getExpandAdj : function(){
39815 var c = this.collapsedEl, cm = this.cmargins;
39816 switch(this.position){
39818 return [-(cm.right+c.getWidth()+cm.left), 0];
39821 return [cm.right+c.getWidth()+cm.left, 0];
39824 return [0, -(cm.top+cm.bottom+c.getHeight())];
39827 return [0, cm.top+cm.bottom+c.getHeight()];
39833 * Ext JS Library 1.1.1
39834 * Copyright(c) 2006-2007, Ext JS, LLC.
39836 * Originally Released Under LGPL - original licence link has changed is not relivant.
39839 * <script type="text/javascript">
39842 * These classes are private internal classes
39844 Roo.bootstrap.layout.Center = function(config){
39845 config.region = "center";
39846 Roo.bootstrap.layout.Region.call(this, config);
39847 this.visible = true;
39848 this.minWidth = config.minWidth || 20;
39849 this.minHeight = config.minHeight || 20;
39852 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39854 // center panel can't be hidden
39858 // center panel can't be hidden
39861 getMinWidth: function(){
39862 return this.minWidth;
39865 getMinHeight: function(){
39866 return this.minHeight;
39880 Roo.bootstrap.layout.North = function(config)
39882 config.region = 'north';
39883 config.cursor = 'n-resize';
39885 Roo.bootstrap.layout.Split.call(this, config);
39889 this.split.placement = Roo.bootstrap.SplitBar.TOP;
39890 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39891 this.split.el.addClass("roo-layout-split-v");
39893 //var size = config.initialSize || config.height;
39894 //if(this.el && typeof size != "undefined"){
39895 // this.el.setHeight(size);
39898 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39900 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39903 onRender : function(ctr, pos)
39905 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39906 var size = this.config.initialSize || this.config.height;
39907 if(this.el && typeof size != "undefined"){
39908 this.el.setHeight(size);
39913 getBox : function(){
39914 if(this.collapsed){
39915 return this.collapsedEl.getBox();
39917 var box = this.el.getBox();
39919 box.height += this.split.el.getHeight();
39924 updateBox : function(box){
39925 if(this.split && !this.collapsed){
39926 box.height -= this.split.el.getHeight();
39927 this.split.el.setLeft(box.x);
39928 this.split.el.setTop(box.y+box.height);
39929 this.split.el.setWidth(box.width);
39931 if(this.collapsed){
39932 this.updateBody(box.width, null);
39934 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39942 Roo.bootstrap.layout.South = function(config){
39943 config.region = 'south';
39944 config.cursor = 's-resize';
39945 Roo.bootstrap.layout.Split.call(this, config);
39947 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39948 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39949 this.split.el.addClass("roo-layout-split-v");
39954 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39955 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39957 onRender : function(ctr, pos)
39959 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39960 var size = this.config.initialSize || this.config.height;
39961 if(this.el && typeof size != "undefined"){
39962 this.el.setHeight(size);
39967 getBox : function(){
39968 if(this.collapsed){
39969 return this.collapsedEl.getBox();
39971 var box = this.el.getBox();
39973 var sh = this.split.el.getHeight();
39980 updateBox : function(box){
39981 if(this.split && !this.collapsed){
39982 var sh = this.split.el.getHeight();
39985 this.split.el.setLeft(box.x);
39986 this.split.el.setTop(box.y-sh);
39987 this.split.el.setWidth(box.width);
39989 if(this.collapsed){
39990 this.updateBody(box.width, null);
39992 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39996 Roo.bootstrap.layout.East = function(config){
39997 config.region = "east";
39998 config.cursor = "e-resize";
39999 Roo.bootstrap.layout.Split.call(this, config);
40001 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40002 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40003 this.split.el.addClass("roo-layout-split-h");
40007 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40008 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40010 onRender : function(ctr, pos)
40012 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40013 var size = this.config.initialSize || this.config.width;
40014 if(this.el && typeof size != "undefined"){
40015 this.el.setWidth(size);
40020 getBox : function(){
40021 if(this.collapsed){
40022 return this.collapsedEl.getBox();
40024 var box = this.el.getBox();
40026 var sw = this.split.el.getWidth();
40033 updateBox : function(box){
40034 if(this.split && !this.collapsed){
40035 var sw = this.split.el.getWidth();
40037 this.split.el.setLeft(box.x);
40038 this.split.el.setTop(box.y);
40039 this.split.el.setHeight(box.height);
40042 if(this.collapsed){
40043 this.updateBody(null, box.height);
40045 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40049 Roo.bootstrap.layout.West = function(config){
40050 config.region = "west";
40051 config.cursor = "w-resize";
40053 Roo.bootstrap.layout.Split.call(this, config);
40055 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40056 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40057 this.split.el.addClass("roo-layout-split-h");
40061 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40062 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40064 onRender: function(ctr, pos)
40066 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40067 var size = this.config.initialSize || this.config.width;
40068 if(typeof size != "undefined"){
40069 this.el.setWidth(size);
40073 getBox : function(){
40074 if(this.collapsed){
40075 return this.collapsedEl.getBox();
40077 var box = this.el.getBox();
40078 if (box.width == 0) {
40079 box.width = this.config.width; // kludge?
40082 box.width += this.split.el.getWidth();
40087 updateBox : function(box){
40088 if(this.split && !this.collapsed){
40089 var sw = this.split.el.getWidth();
40091 this.split.el.setLeft(box.x+box.width);
40092 this.split.el.setTop(box.y);
40093 this.split.el.setHeight(box.height);
40095 if(this.collapsed){
40096 this.updateBody(null, box.height);
40098 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40100 });Roo.namespace("Roo.bootstrap.panel");/*
40102 * Ext JS Library 1.1.1
40103 * Copyright(c) 2006-2007, Ext JS, LLC.
40105 * Originally Released Under LGPL - original licence link has changed is not relivant.
40108 * <script type="text/javascript">
40111 * @class Roo.ContentPanel
40112 * @extends Roo.util.Observable
40113 * A basic ContentPanel element.
40114 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
40115 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
40116 * @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
40117 * @cfg {Boolean} closable True if the panel can be closed/removed
40118 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
40119 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40120 * @cfg {Toolbar} toolbar A toolbar for this panel
40121 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
40122 * @cfg {String} title The title for this panel
40123 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40124 * @cfg {String} url Calls {@link #setUrl} with this value
40125 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40126 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
40127 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
40128 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
40129 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
40130 * @cfg {Boolean} badges render the badges
40131 * @cfg {String} cls extra classes to use
40132 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40135 * Create a new ContentPanel.
40136 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40137 * @param {String/Object} config A string to set only the title or a config object
40138 * @param {String} content (optional) Set the HTML content for this panel
40139 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40141 Roo.bootstrap.panel.Content = function( config){
40143 this.tpl = config.tpl || false;
40145 var el = config.el;
40146 var content = config.content;
40148 if(config.autoCreate){ // xtype is available if this is called from factory
40151 this.el = Roo.get(el);
40152 if(!this.el && config && config.autoCreate){
40153 if(typeof config.autoCreate == "object"){
40154 if(!config.autoCreate.id){
40155 config.autoCreate.id = config.id||el;
40157 this.el = Roo.DomHelper.append(document.body,
40158 config.autoCreate, true);
40162 cls: (config.cls || '') +
40163 (config.background ? ' bg-' + config.background : '') +
40164 " roo-layout-inactive-content",
40167 if (config.iframe) {
40171 style : 'border: 0px',
40172 src : 'about:blank'
40178 elcfg.html = config.html;
40182 this.el = Roo.DomHelper.append(document.body, elcfg , true);
40183 if (config.iframe) {
40184 this.iframeEl = this.el.select('iframe',true).first();
40189 this.closable = false;
40190 this.loaded = false;
40191 this.active = false;
40194 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40196 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40198 this.wrapEl = this.el; //this.el.wrap();
40200 if (config.toolbar.items) {
40201 ti = config.toolbar.items ;
40202 delete config.toolbar.items ;
40206 this.toolbar.render(this.wrapEl, 'before');
40207 for(var i =0;i < ti.length;i++) {
40208 // Roo.log(['add child', items[i]]);
40209 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40211 this.toolbar.items = nitems;
40212 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40213 delete config.toolbar;
40217 // xtype created footer. - not sure if will work as we normally have to render first..
40218 if (this.footer && !this.footer.el && this.footer.xtype) {
40219 if (!this.wrapEl) {
40220 this.wrapEl = this.el.wrap();
40223 this.footer.container = this.wrapEl.createChild();
40225 this.footer = Roo.factory(this.footer, Roo);
40230 if(typeof config == "string"){
40231 this.title = config;
40233 Roo.apply(this, config);
40237 this.resizeEl = Roo.get(this.resizeEl, true);
40239 this.resizeEl = this.el;
40241 // handle view.xtype
40249 * Fires when this panel is activated.
40250 * @param {Roo.ContentPanel} this
40254 * @event deactivate
40255 * Fires when this panel is activated.
40256 * @param {Roo.ContentPanel} this
40258 "deactivate" : true,
40262 * Fires when this panel is resized if fitToFrame is true.
40263 * @param {Roo.ContentPanel} this
40264 * @param {Number} width The width after any component adjustments
40265 * @param {Number} height The height after any component adjustments
40271 * Fires when this tab is created
40272 * @param {Roo.ContentPanel} this
40278 * Fires when this content is scrolled
40279 * @param {Roo.ContentPanel} this
40280 * @param {Event} scrollEvent
40291 if(this.autoScroll && !this.iframe){
40292 this.resizeEl.setStyle("overflow", "auto");
40293 this.resizeEl.on('scroll', this.onScroll, this);
40295 // fix randome scrolling
40296 //this.el.on('scroll', function() {
40297 // Roo.log('fix random scolling');
40298 // this.scrollTo('top',0);
40301 content = content || this.content;
40303 this.setContent(content);
40305 if(config && config.url){
40306 this.setUrl(this.url, this.params, this.loadOnce);
40311 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40313 if (this.view && typeof(this.view.xtype) != 'undefined') {
40314 this.view.el = this.el.appendChild(document.createElement("div"));
40315 this.view = Roo.factory(this.view);
40316 this.view.render && this.view.render(false, '');
40320 this.fireEvent('render', this);
40323 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40333 /* Resize Element - use this to work out scroll etc. */
40336 setRegion : function(region){
40337 this.region = region;
40338 this.setActiveClass(region && !this.background);
40342 setActiveClass: function(state)
40345 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40346 this.el.setStyle('position','relative');
40348 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40349 this.el.setStyle('position', 'absolute');
40354 * Returns the toolbar for this Panel if one was configured.
40355 * @return {Roo.Toolbar}
40357 getToolbar : function(){
40358 return this.toolbar;
40361 setActiveState : function(active)
40363 this.active = active;
40364 this.setActiveClass(active);
40366 if(this.fireEvent("deactivate", this) === false){
40371 this.fireEvent("activate", this);
40375 * Updates this panel's element (not for iframe)
40376 * @param {String} content The new content
40377 * @param {Boolean} loadScripts (optional) true to look for and process scripts
40379 setContent : function(content, loadScripts){
40384 this.el.update(content, loadScripts);
40387 ignoreResize : function(w, h){
40388 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40391 this.lastSize = {width: w, height: h};
40396 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40397 * @return {Roo.UpdateManager} The UpdateManager
40399 getUpdateManager : function(){
40403 return this.el.getUpdateManager();
40406 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40407 * Does not work with IFRAME contents
40408 * @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:
40411 url: "your-url.php",
40412 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40413 callback: yourFunction,
40414 scope: yourObject, //(optional scope)
40417 text: "Loading...",
40423 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40424 * 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.
40425 * @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}
40426 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40427 * @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.
40428 * @return {Roo.ContentPanel} this
40436 var um = this.el.getUpdateManager();
40437 um.update.apply(um, arguments);
40443 * 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.
40444 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40445 * @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)
40446 * @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)
40447 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40449 setUrl : function(url, params, loadOnce){
40451 this.iframeEl.dom.src = url;
40455 if(this.refreshDelegate){
40456 this.removeListener("activate", this.refreshDelegate);
40458 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40459 this.on("activate", this.refreshDelegate);
40460 return this.el.getUpdateManager();
40463 _handleRefresh : function(url, params, loadOnce){
40464 if(!loadOnce || !this.loaded){
40465 var updater = this.el.getUpdateManager();
40466 updater.update(url, params, this._setLoaded.createDelegate(this));
40470 _setLoaded : function(){
40471 this.loaded = true;
40475 * Returns this panel's id
40478 getId : function(){
40483 * Returns this panel's element - used by regiosn to add.
40484 * @return {Roo.Element}
40486 getEl : function(){
40487 return this.wrapEl || this.el;
40492 adjustForComponents : function(width, height)
40494 //Roo.log('adjustForComponents ');
40495 if(this.resizeEl != this.el){
40496 width -= this.el.getFrameWidth('lr');
40497 height -= this.el.getFrameWidth('tb');
40500 var te = this.toolbar.getEl();
40501 te.setWidth(width);
40502 height -= te.getHeight();
40505 var te = this.footer.getEl();
40506 te.setWidth(width);
40507 height -= te.getHeight();
40511 if(this.adjustments){
40512 width += this.adjustments[0];
40513 height += this.adjustments[1];
40515 return {"width": width, "height": height};
40518 setSize : function(width, height){
40519 if(this.fitToFrame && !this.ignoreResize(width, height)){
40520 if(this.fitContainer && this.resizeEl != this.el){
40521 this.el.setSize(width, height);
40523 var size = this.adjustForComponents(width, height);
40525 this.iframeEl.setSize(width,height);
40528 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40529 this.fireEvent('resize', this, size.width, size.height);
40536 * Returns this panel's title
40539 getTitle : function(){
40541 if (typeof(this.title) != 'object') {
40546 for (var k in this.title) {
40547 if (!this.title.hasOwnProperty(k)) {
40551 if (k.indexOf('-') >= 0) {
40552 var s = k.split('-');
40553 for (var i = 0; i<s.length; i++) {
40554 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40557 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40564 * Set this panel's title
40565 * @param {String} title
40567 setTitle : function(title){
40568 this.title = title;
40570 this.region.updatePanelTitle(this, title);
40575 * Returns true is this panel was configured to be closable
40576 * @return {Boolean}
40578 isClosable : function(){
40579 return this.closable;
40582 beforeSlide : function(){
40584 this.resizeEl.clip();
40587 afterSlide : function(){
40589 this.resizeEl.unclip();
40593 * Force a content refresh from the URL specified in the {@link #setUrl} method.
40594 * Will fail silently if the {@link #setUrl} method has not been called.
40595 * This does not activate the panel, just updates its content.
40597 refresh : function(){
40598 if(this.refreshDelegate){
40599 this.loaded = false;
40600 this.refreshDelegate();
40605 * Destroys this panel
40607 destroy : function(){
40608 this.el.removeAllListeners();
40609 var tempEl = document.createElement("span");
40610 tempEl.appendChild(this.el.dom);
40611 tempEl.innerHTML = "";
40617 * form - if the content panel contains a form - this is a reference to it.
40618 * @type {Roo.form.Form}
40622 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40623 * This contains a reference to it.
40629 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40639 * @param {Object} cfg Xtype definition of item to add.
40643 getChildContainer: function () {
40644 return this.getEl();
40648 onScroll : function(e)
40650 this.fireEvent('scroll', this, e);
40655 var ret = new Roo.factory(cfg);
40660 if (cfg.xtype.match(/^Form$/)) {
40663 //if (this.footer) {
40664 // el = this.footer.container.insertSibling(false, 'before');
40666 el = this.el.createChild();
40669 this.form = new Roo.form.Form(cfg);
40672 if ( this.form.allItems.length) {
40673 this.form.render(el.dom);
40677 // should only have one of theses..
40678 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40679 // views.. should not be just added - used named prop 'view''
40681 cfg.el = this.el.appendChild(document.createElement("div"));
40684 var ret = new Roo.factory(cfg);
40686 ret.render && ret.render(false, ''); // render blank..
40696 * @class Roo.bootstrap.panel.Grid
40697 * @extends Roo.bootstrap.panel.Content
40699 * Create a new GridPanel.
40700 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40701 * @param {Object} config A the config object
40707 Roo.bootstrap.panel.Grid = function(config)
40711 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40712 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40714 config.el = this.wrapper;
40715 //this.el = this.wrapper;
40717 if (config.container) {
40718 // ctor'ed from a Border/panel.grid
40721 this.wrapper.setStyle("overflow", "hidden");
40722 this.wrapper.addClass('roo-grid-container');
40727 if(config.toolbar){
40728 var tool_el = this.wrapper.createChild();
40729 this.toolbar = Roo.factory(config.toolbar);
40731 if (config.toolbar.items) {
40732 ti = config.toolbar.items ;
40733 delete config.toolbar.items ;
40737 this.toolbar.render(tool_el);
40738 for(var i =0;i < ti.length;i++) {
40739 // Roo.log(['add child', items[i]]);
40740 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40742 this.toolbar.items = nitems;
40744 delete config.toolbar;
40747 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40748 config.grid.scrollBody = true;;
40749 config.grid.monitorWindowResize = false; // turn off autosizing
40750 config.grid.autoHeight = false;
40751 config.grid.autoWidth = false;
40753 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40755 if (config.background) {
40756 // render grid on panel activation (if panel background)
40757 this.on('activate', function(gp) {
40758 if (!gp.grid.rendered) {
40759 gp.grid.render(this.wrapper);
40760 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40765 this.grid.render(this.wrapper);
40766 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40769 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40770 // ??? needed ??? config.el = this.wrapper;
40775 // xtype created footer. - not sure if will work as we normally have to render first..
40776 if (this.footer && !this.footer.el && this.footer.xtype) {
40778 var ctr = this.grid.getView().getFooterPanel(true);
40779 this.footer.dataSource = this.grid.dataSource;
40780 this.footer = Roo.factory(this.footer, Roo);
40781 this.footer.render(ctr);
40791 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40792 getId : function(){
40793 return this.grid.id;
40797 * Returns the grid for this panel
40798 * @return {Roo.bootstrap.Table}
40800 getGrid : function(){
40804 setSize : function(width, height){
40805 if(!this.ignoreResize(width, height)){
40806 var grid = this.grid;
40807 var size = this.adjustForComponents(width, height);
40808 // tfoot is not a footer?
40811 var gridel = grid.getGridEl();
40812 gridel.setSize(size.width, size.height);
40814 var tbd = grid.getGridEl().select('tbody', true).first();
40815 var thd = grid.getGridEl().select('thead',true).first();
40816 var tbf= grid.getGridEl().select('tfoot', true).first();
40819 size.height -= tbf.getHeight();
40822 size.height -= thd.getHeight();
40825 tbd.setSize(size.width, size.height );
40826 // this is for the account management tab -seems to work there.
40827 var thd = grid.getGridEl().select('thead',true).first();
40829 // tbd.setSize(size.width, size.height - thd.getHeight());
40838 beforeSlide : function(){
40839 this.grid.getView().scroller.clip();
40842 afterSlide : function(){
40843 this.grid.getView().scroller.unclip();
40846 destroy : function(){
40847 this.grid.destroy();
40849 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
40854 * @class Roo.bootstrap.panel.Nest
40855 * @extends Roo.bootstrap.panel.Content
40857 * Create a new Panel, that can contain a layout.Border.
40860 * @param {Roo.BorderLayout} layout The layout for this panel
40861 * @param {String/Object} config A string to set only the title or a config object
40863 Roo.bootstrap.panel.Nest = function(config)
40865 // construct with only one argument..
40866 /* FIXME - implement nicer consturctors
40867 if (layout.layout) {
40869 layout = config.layout;
40870 delete config.layout;
40872 if (layout.xtype && !layout.getEl) {
40873 // then layout needs constructing..
40874 layout = Roo.factory(layout, Roo);
40878 config.el = config.layout.getEl();
40880 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40882 config.layout.monitorWindowResize = false; // turn off autosizing
40883 this.layout = config.layout;
40884 this.layout.getEl().addClass("roo-layout-nested-layout");
40885 this.layout.parent = this;
40892 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40894 setSize : function(width, height){
40895 if(!this.ignoreResize(width, height)){
40896 var size = this.adjustForComponents(width, height);
40897 var el = this.layout.getEl();
40898 if (size.height < 1) {
40899 el.setWidth(size.width);
40901 el.setSize(size.width, size.height);
40903 var touch = el.dom.offsetWidth;
40904 this.layout.layout();
40905 // ie requires a double layout on the first pass
40906 if(Roo.isIE && !this.initialized){
40907 this.initialized = true;
40908 this.layout.layout();
40913 // activate all subpanels if not currently active..
40915 setActiveState : function(active){
40916 this.active = active;
40917 this.setActiveClass(active);
40920 this.fireEvent("deactivate", this);
40924 this.fireEvent("activate", this);
40925 // not sure if this should happen before or after..
40926 if (!this.layout) {
40927 return; // should not happen..
40930 for (var r in this.layout.regions) {
40931 reg = this.layout.getRegion(r);
40932 if (reg.getActivePanel()) {
40933 //reg.showPanel(reg.getActivePanel()); // force it to activate..
40934 reg.setActivePanel(reg.getActivePanel());
40937 if (!reg.panels.length) {
40940 reg.showPanel(reg.getPanel(0));
40949 * Returns the nested BorderLayout for this panel
40950 * @return {Roo.BorderLayout}
40952 getLayout : function(){
40953 return this.layout;
40957 * Adds a xtype elements to the layout of the nested panel
40961 xtype : 'ContentPanel',
40968 xtype : 'NestedLayoutPanel',
40974 items : [ ... list of content panels or nested layout panels.. ]
40978 * @param {Object} cfg Xtype definition of item to add.
40980 addxtype : function(cfg) {
40981 return this.layout.addxtype(cfg);
40986 * Ext JS Library 1.1.1
40987 * Copyright(c) 2006-2007, Ext JS, LLC.
40989 * Originally Released Under LGPL - original licence link has changed is not relivant.
40992 * <script type="text/javascript">
40995 * @class Roo.TabPanel
40996 * @extends Roo.util.Observable
40997 * A lightweight tab container.
41001 // basic tabs 1, built from existing content
41002 var tabs = new Roo.TabPanel("tabs1");
41003 tabs.addTab("script", "View Script");
41004 tabs.addTab("markup", "View Markup");
41005 tabs.activate("script");
41007 // more advanced tabs, built from javascript
41008 var jtabs = new Roo.TabPanel("jtabs");
41009 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41011 // set up the UpdateManager
41012 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41013 var updater = tab2.getUpdateManager();
41014 updater.setDefaultUrl("ajax1.htm");
41015 tab2.on('activate', updater.refresh, updater, true);
41017 // Use setUrl for Ajax loading
41018 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41019 tab3.setUrl("ajax2.htm", null, true);
41022 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41025 jtabs.activate("jtabs-1");
41028 * Create a new TabPanel.
41029 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41030 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41032 Roo.bootstrap.panel.Tabs = function(config){
41034 * The container element for this TabPanel.
41035 * @type Roo.Element
41037 this.el = Roo.get(config.el);
41040 if(typeof config == "boolean"){
41041 this.tabPosition = config ? "bottom" : "top";
41043 Roo.apply(this, config);
41047 if(this.tabPosition == "bottom"){
41048 // if tabs are at the bottom = create the body first.
41049 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41050 this.el.addClass("roo-tabs-bottom");
41052 // next create the tabs holders
41054 if (this.tabPosition == "west"){
41056 var reg = this.region; // fake it..
41058 if (!reg.mgr.parent) {
41061 reg = reg.mgr.parent.region;
41063 Roo.log("got nest?");
41065 if (reg.mgr.getRegion('west')) {
41066 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41067 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41068 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41069 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41070 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41078 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41079 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41080 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41081 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41086 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41089 // finally - if tabs are at the top, then create the body last..
41090 if(this.tabPosition != "bottom"){
41091 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41092 * @type Roo.Element
41094 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41095 this.el.addClass("roo-tabs-top");
41099 this.bodyEl.setStyle("position", "relative");
41101 this.active = null;
41102 this.activateDelegate = this.activate.createDelegate(this);
41107 * Fires when the active tab changes
41108 * @param {Roo.TabPanel} this
41109 * @param {Roo.TabPanelItem} activePanel The new active tab
41113 * @event beforetabchange
41114 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41115 * @param {Roo.TabPanel} this
41116 * @param {Object} e Set cancel to true on this object to cancel the tab change
41117 * @param {Roo.TabPanelItem} tab The tab being changed to
41119 "beforetabchange" : true
41122 Roo.EventManager.onWindowResize(this.onResize, this);
41123 this.cpad = this.el.getPadding("lr");
41124 this.hiddenCount = 0;
41127 // toolbar on the tabbar support...
41128 if (this.toolbar) {
41129 alert("no toolbar support yet");
41130 this.toolbar = false;
41132 var tcfg = this.toolbar;
41133 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
41134 this.toolbar = new Roo.Toolbar(tcfg);
41135 if (Roo.isSafari) {
41136 var tbl = tcfg.container.child('table', true);
41137 tbl.setAttribute('width', '100%');
41145 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41148 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41150 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41152 tabPosition : "top",
41154 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41156 currentTabWidth : 0,
41158 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41162 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41166 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41168 preferredTabWidth : 175,
41170 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41172 resizeTabs : false,
41174 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41176 monitorResize : true,
41178 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
41180 toolbar : false, // set by caller..
41182 region : false, /// set by caller
41184 disableTooltips : true, // not used yet...
41187 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41188 * @param {String} id The id of the div to use <b>or create</b>
41189 * @param {String} text The text for the tab
41190 * @param {String} content (optional) Content to put in the TabPanelItem body
41191 * @param {Boolean} closable (optional) True to create a close icon on the tab
41192 * @return {Roo.TabPanelItem} The created TabPanelItem
41194 addTab : function(id, text, content, closable, tpl)
41196 var item = new Roo.bootstrap.panel.TabItem({
41200 closable : closable,
41203 this.addTabItem(item);
41205 item.setContent(content);
41211 * Returns the {@link Roo.TabPanelItem} with the specified id/index
41212 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41213 * @return {Roo.TabPanelItem}
41215 getTab : function(id){
41216 return this.items[id];
41220 * Hides the {@link Roo.TabPanelItem} with the specified id/index
41221 * @param {String/Number} id The id or index of the TabPanelItem to hide.
41223 hideTab : function(id){
41224 var t = this.items[id];
41227 this.hiddenCount++;
41228 this.autoSizeTabs();
41233 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41234 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41236 unhideTab : function(id){
41237 var t = this.items[id];
41239 t.setHidden(false);
41240 this.hiddenCount--;
41241 this.autoSizeTabs();
41246 * Adds an existing {@link Roo.TabPanelItem}.
41247 * @param {Roo.TabPanelItem} item The TabPanelItem to add
41249 addTabItem : function(item)
41251 this.items[item.id] = item;
41252 this.items.push(item);
41253 this.autoSizeTabs();
41254 // if(this.resizeTabs){
41255 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41256 // this.autoSizeTabs();
41258 // item.autoSize();
41263 * Removes a {@link Roo.TabPanelItem}.
41264 * @param {String/Number} id The id or index of the TabPanelItem to remove.
41266 removeTab : function(id){
41267 var items = this.items;
41268 var tab = items[id];
41269 if(!tab) { return; }
41270 var index = items.indexOf(tab);
41271 if(this.active == tab && items.length > 1){
41272 var newTab = this.getNextAvailable(index);
41277 this.stripEl.dom.removeChild(tab.pnode.dom);
41278 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41279 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41281 items.splice(index, 1);
41282 delete this.items[tab.id];
41283 tab.fireEvent("close", tab);
41284 tab.purgeListeners();
41285 this.autoSizeTabs();
41288 getNextAvailable : function(start){
41289 var items = this.items;
41291 // look for a next tab that will slide over to
41292 // replace the one being removed
41293 while(index < items.length){
41294 var item = items[++index];
41295 if(item && !item.isHidden()){
41299 // if one isn't found select the previous tab (on the left)
41302 var item = items[--index];
41303 if(item && !item.isHidden()){
41311 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41312 * @param {String/Number} id The id or index of the TabPanelItem to disable.
41314 disableTab : function(id){
41315 var tab = this.items[id];
41316 if(tab && this.active != tab){
41322 * Enables a {@link Roo.TabPanelItem} that is disabled.
41323 * @param {String/Number} id The id or index of the TabPanelItem to enable.
41325 enableTab : function(id){
41326 var tab = this.items[id];
41331 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41332 * @param {String/Number} id The id or index of the TabPanelItem to activate.
41333 * @return {Roo.TabPanelItem} The TabPanelItem.
41335 activate : function(id)
41337 //Roo.log('activite:' + id);
41339 var tab = this.items[id];
41343 if(tab == this.active || tab.disabled){
41347 this.fireEvent("beforetabchange", this, e, tab);
41348 if(e.cancel !== true && !tab.disabled){
41350 this.active.hide();
41352 this.active = this.items[id];
41353 this.active.show();
41354 this.fireEvent("tabchange", this, this.active);
41360 * Gets the active {@link Roo.TabPanelItem}.
41361 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41363 getActiveTab : function(){
41364 return this.active;
41368 * Updates the tab body element to fit the height of the container element
41369 * for overflow scrolling
41370 * @param {Number} targetHeight (optional) Override the starting height from the elements height
41372 syncHeight : function(targetHeight){
41373 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41374 var bm = this.bodyEl.getMargins();
41375 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41376 this.bodyEl.setHeight(newHeight);
41380 onResize : function(){
41381 if(this.monitorResize){
41382 this.autoSizeTabs();
41387 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41389 beginUpdate : function(){
41390 this.updating = true;
41394 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41396 endUpdate : function(){
41397 this.updating = false;
41398 this.autoSizeTabs();
41402 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41404 autoSizeTabs : function()
41406 var count = this.items.length;
41407 var vcount = count - this.hiddenCount;
41410 this.stripEl.hide();
41412 this.stripEl.show();
41415 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41420 var w = Math.max(this.el.getWidth() - this.cpad, 10);
41421 var availWidth = Math.floor(w / vcount);
41422 var b = this.stripBody;
41423 if(b.getWidth() > w){
41424 var tabs = this.items;
41425 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41426 if(availWidth < this.minTabWidth){
41427 /*if(!this.sleft){ // incomplete scrolling code
41428 this.createScrollButtons();
41431 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41434 if(this.currentTabWidth < this.preferredTabWidth){
41435 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41441 * Returns the number of tabs in this TabPanel.
41444 getCount : function(){
41445 return this.items.length;
41449 * Resizes all the tabs to the passed width
41450 * @param {Number} The new width
41452 setTabWidth : function(width){
41453 this.currentTabWidth = width;
41454 for(var i = 0, len = this.items.length; i < len; i++) {
41455 if(!this.items[i].isHidden()) {
41456 this.items[i].setWidth(width);
41462 * Destroys this TabPanel
41463 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41465 destroy : function(removeEl){
41466 Roo.EventManager.removeResizeListener(this.onResize, this);
41467 for(var i = 0, len = this.items.length; i < len; i++){
41468 this.items[i].purgeListeners();
41470 if(removeEl === true){
41471 this.el.update("");
41476 createStrip : function(container)
41478 var strip = document.createElement("nav");
41479 strip.className = Roo.bootstrap.version == 4 ?
41480 "navbar-light bg-light" :
41481 "navbar navbar-default"; //"x-tabs-wrap";
41482 container.appendChild(strip);
41486 createStripList : function(strip)
41488 // div wrapper for retard IE
41489 // returns the "tr" element.
41490 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41491 //'<div class="x-tabs-strip-wrap">'+
41492 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41493 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41494 return strip.firstChild; //.firstChild.firstChild.firstChild;
41496 createBody : function(container)
41498 var body = document.createElement("div");
41499 Roo.id(body, "tab-body");
41500 //Roo.fly(body).addClass("x-tabs-body");
41501 Roo.fly(body).addClass("tab-content");
41502 container.appendChild(body);
41505 createItemBody :function(bodyEl, id){
41506 var body = Roo.getDom(id);
41508 body = document.createElement("div");
41511 //Roo.fly(body).addClass("x-tabs-item-body");
41512 Roo.fly(body).addClass("tab-pane");
41513 bodyEl.insertBefore(body, bodyEl.firstChild);
41517 createStripElements : function(stripEl, text, closable, tpl)
41519 var td = document.createElement("li"); // was td..
41520 td.className = 'nav-item';
41522 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41525 stripEl.appendChild(td);
41527 td.className = "x-tabs-closable";
41528 if(!this.closeTpl){
41529 this.closeTpl = new Roo.Template(
41530 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41531 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41532 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41535 var el = this.closeTpl.overwrite(td, {"text": text});
41536 var close = el.getElementsByTagName("div")[0];
41537 var inner = el.getElementsByTagName("em")[0];
41538 return {"el": el, "close": close, "inner": inner};
41541 // not sure what this is..
41542 // if(!this.tabTpl){
41543 //this.tabTpl = new Roo.Template(
41544 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41545 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41547 // this.tabTpl = new Roo.Template(
41548 // '<a href="#">' +
41549 // '<span unselectable="on"' +
41550 // (this.disableTooltips ? '' : ' title="{text}"') +
41551 // ' >{text}</span></a>'
41557 var template = tpl || this.tabTpl || false;
41560 template = new Roo.Template(
41561 Roo.bootstrap.version == 4 ?
41563 '<a class="nav-link" href="#" unselectable="on"' +
41564 (this.disableTooltips ? '' : ' title="{text}"') +
41567 '<a class="nav-link" href="#">' +
41568 '<span unselectable="on"' +
41569 (this.disableTooltips ? '' : ' title="{text}"') +
41570 ' >{text}</span></a>'
41575 switch (typeof(template)) {
41579 template = new Roo.Template(template);
41585 var el = template.overwrite(td, {"text": text});
41587 var inner = el.getElementsByTagName("span")[0];
41589 return {"el": el, "inner": inner};
41597 * @class Roo.TabPanelItem
41598 * @extends Roo.util.Observable
41599 * Represents an individual item (tab plus body) in a TabPanel.
41600 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41601 * @param {String} id The id of this TabPanelItem
41602 * @param {String} text The text for the tab of this TabPanelItem
41603 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41605 Roo.bootstrap.panel.TabItem = function(config){
41607 * The {@link Roo.TabPanel} this TabPanelItem belongs to
41608 * @type Roo.TabPanel
41610 this.tabPanel = config.panel;
41612 * The id for this TabPanelItem
41615 this.id = config.id;
41617 this.disabled = false;
41619 this.text = config.text;
41621 this.loaded = false;
41622 this.closable = config.closable;
41625 * The body element for this TabPanelItem.
41626 * @type Roo.Element
41628 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41629 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41630 this.bodyEl.setStyle("display", "block");
41631 this.bodyEl.setStyle("zoom", "1");
41632 //this.hideAction();
41634 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41636 this.el = Roo.get(els.el);
41637 this.inner = Roo.get(els.inner, true);
41638 this.textEl = Roo.bootstrap.version == 4 ?
41639 this.el : Roo.get(this.el.dom.firstChild, true);
41641 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41642 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41645 // this.el.on("mousedown", this.onTabMouseDown, this);
41646 this.el.on("click", this.onTabClick, this);
41648 if(config.closable){
41649 var c = Roo.get(els.close, true);
41650 c.dom.title = this.closeText;
41651 c.addClassOnOver("close-over");
41652 c.on("click", this.closeClick, this);
41658 * Fires when this tab becomes the active tab.
41659 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41660 * @param {Roo.TabPanelItem} this
41664 * @event beforeclose
41665 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41666 * @param {Roo.TabPanelItem} this
41667 * @param {Object} e Set cancel to true on this object to cancel the close.
41669 "beforeclose": true,
41672 * Fires when this tab is closed.
41673 * @param {Roo.TabPanelItem} this
41677 * @event deactivate
41678 * Fires when this tab is no longer the active tab.
41679 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41680 * @param {Roo.TabPanelItem} this
41682 "deactivate" : true
41684 this.hidden = false;
41686 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41689 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41691 purgeListeners : function(){
41692 Roo.util.Observable.prototype.purgeListeners.call(this);
41693 this.el.removeAllListeners();
41696 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41699 this.status_node.addClass("active");
41702 this.tabPanel.stripWrap.repaint();
41704 this.fireEvent("activate", this.tabPanel, this);
41708 * Returns true if this tab is the active tab.
41709 * @return {Boolean}
41711 isActive : function(){
41712 return this.tabPanel.getActiveTab() == this;
41716 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41719 this.status_node.removeClass("active");
41721 this.fireEvent("deactivate", this.tabPanel, this);
41724 hideAction : function(){
41725 this.bodyEl.hide();
41726 this.bodyEl.setStyle("position", "absolute");
41727 this.bodyEl.setLeft("-20000px");
41728 this.bodyEl.setTop("-20000px");
41731 showAction : function(){
41732 this.bodyEl.setStyle("position", "relative");
41733 this.bodyEl.setTop("");
41734 this.bodyEl.setLeft("");
41735 this.bodyEl.show();
41739 * Set the tooltip for the tab.
41740 * @param {String} tooltip The tab's tooltip
41742 setTooltip : function(text){
41743 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41744 this.textEl.dom.qtip = text;
41745 this.textEl.dom.removeAttribute('title');
41747 this.textEl.dom.title = text;
41751 onTabClick : function(e){
41752 e.preventDefault();
41753 this.tabPanel.activate(this.id);
41756 onTabMouseDown : function(e){
41757 e.preventDefault();
41758 this.tabPanel.activate(this.id);
41761 getWidth : function(){
41762 return this.inner.getWidth();
41765 setWidth : function(width){
41766 var iwidth = width - this.linode.getPadding("lr");
41767 this.inner.setWidth(iwidth);
41768 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41769 this.linode.setWidth(width);
41773 * Show or hide the tab
41774 * @param {Boolean} hidden True to hide or false to show.
41776 setHidden : function(hidden){
41777 this.hidden = hidden;
41778 this.linode.setStyle("display", hidden ? "none" : "");
41782 * Returns true if this tab is "hidden"
41783 * @return {Boolean}
41785 isHidden : function(){
41786 return this.hidden;
41790 * Returns the text for this tab
41793 getText : function(){
41797 autoSize : function(){
41798 //this.el.beginMeasure();
41799 this.textEl.setWidth(1);
41801 * #2804 [new] Tabs in Roojs
41802 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41804 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41805 //this.el.endMeasure();
41809 * Sets the text for the tab (Note: this also sets the tooltip text)
41810 * @param {String} text The tab's text and tooltip
41812 setText : function(text){
41814 this.textEl.update(text);
41815 this.setTooltip(text);
41816 //if(!this.tabPanel.resizeTabs){
41817 // this.autoSize();
41821 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41823 activate : function(){
41824 this.tabPanel.activate(this.id);
41828 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41830 disable : function(){
41831 if(this.tabPanel.active != this){
41832 this.disabled = true;
41833 this.status_node.addClass("disabled");
41838 * Enables this TabPanelItem if it was previously disabled.
41840 enable : function(){
41841 this.disabled = false;
41842 this.status_node.removeClass("disabled");
41846 * Sets the content for this TabPanelItem.
41847 * @param {String} content The content
41848 * @param {Boolean} loadScripts true to look for and load scripts
41850 setContent : function(content, loadScripts){
41851 this.bodyEl.update(content, loadScripts);
41855 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41856 * @return {Roo.UpdateManager} The UpdateManager
41858 getUpdateManager : function(){
41859 return this.bodyEl.getUpdateManager();
41863 * Set a URL to be used to load the content for this TabPanelItem.
41864 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41865 * @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)
41866 * @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)
41867 * @return {Roo.UpdateManager} The UpdateManager
41869 setUrl : function(url, params, loadOnce){
41870 if(this.refreshDelegate){
41871 this.un('activate', this.refreshDelegate);
41873 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41874 this.on("activate", this.refreshDelegate);
41875 return this.bodyEl.getUpdateManager();
41879 _handleRefresh : function(url, params, loadOnce){
41880 if(!loadOnce || !this.loaded){
41881 var updater = this.bodyEl.getUpdateManager();
41882 updater.update(url, params, this._setLoaded.createDelegate(this));
41887 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
41888 * Will fail silently if the setUrl method has not been called.
41889 * This does not activate the panel, just updates its content.
41891 refresh : function(){
41892 if(this.refreshDelegate){
41893 this.loaded = false;
41894 this.refreshDelegate();
41899 _setLoaded : function(){
41900 this.loaded = true;
41904 closeClick : function(e){
41907 this.fireEvent("beforeclose", this, o);
41908 if(o.cancel !== true){
41909 this.tabPanel.removeTab(this.id);
41913 * The text displayed in the tooltip for the close icon.
41916 closeText : "Close this tab"
41919 * This script refer to:
41920 * Title: International Telephone Input
41921 * Author: Jack O'Connor
41922 * Code version: v12.1.12
41923 * Availability: https://github.com/jackocnr/intl-tel-input.git
41926 Roo.bootstrap.PhoneInputData = function() {
41929 "Afghanistan (افغانستان)",
41934 "Albania (Shqipëri)",
41939 "Algeria (الجزائر)",
41964 "Antigua and Barbuda",
41974 "Armenia (Հայաստան)",
41990 "Austria (Österreich)",
41995 "Azerbaijan (Azərbaycan)",
42005 "Bahrain (البحرين)",
42010 "Bangladesh (বাংলাদেশ)",
42020 "Belarus (Беларусь)",
42025 "Belgium (België)",
42055 "Bosnia and Herzegovina (Босна и Херцеговина)",
42070 "British Indian Ocean Territory",
42075 "British Virgin Islands",
42085 "Bulgaria (България)",
42095 "Burundi (Uburundi)",
42100 "Cambodia (កម្ពុជា)",
42105 "Cameroon (Cameroun)",
42114 ["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"]
42117 "Cape Verde (Kabu Verdi)",
42122 "Caribbean Netherlands",
42133 "Central African Republic (République centrafricaine)",
42153 "Christmas Island",
42159 "Cocos (Keeling) Islands",
42170 "Comoros (جزر القمر)",
42175 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42180 "Congo (Republic) (Congo-Brazzaville)",
42200 "Croatia (Hrvatska)",
42221 "Czech Republic (Česká republika)",
42226 "Denmark (Danmark)",
42241 "Dominican Republic (República Dominicana)",
42245 ["809", "829", "849"]
42263 "Equatorial Guinea (Guinea Ecuatorial)",
42283 "Falkland Islands (Islas Malvinas)",
42288 "Faroe Islands (Føroyar)",
42309 "French Guiana (Guyane française)",
42314 "French Polynesia (Polynésie française)",
42329 "Georgia (საქართველო)",
42334 "Germany (Deutschland)",
42354 "Greenland (Kalaallit Nunaat)",
42391 "Guinea-Bissau (Guiné Bissau)",
42416 "Hungary (Magyarország)",
42421 "Iceland (Ísland)",
42441 "Iraq (العراق)",
42457 "Israel (ישראל)",
42484 "Jordan (الأردن)",
42489 "Kazakhstan (Казахстан)",
42510 "Kuwait (الكويت)",
42515 "Kyrgyzstan (Кыргызстан)",
42525 "Latvia (Latvija)",
42530 "Lebanon (لبنان)",
42545 "Libya (ليبيا)",
42555 "Lithuania (Lietuva)",
42570 "Macedonia (FYROM) (Македонија)",
42575 "Madagascar (Madagasikara)",
42605 "Marshall Islands",
42615 "Mauritania (موريتانيا)",
42620 "Mauritius (Moris)",
42641 "Moldova (Republica Moldova)",
42651 "Mongolia (Монгол)",
42656 "Montenegro (Crna Gora)",
42666 "Morocco (المغرب)",
42672 "Mozambique (Moçambique)",
42677 "Myanmar (Burma) (မြန်မာ)",
42682 "Namibia (Namibië)",
42697 "Netherlands (Nederland)",
42702 "New Caledonia (Nouvelle-Calédonie)",
42737 "North Korea (조선 민주주의 인민 공화국)",
42742 "Northern Mariana Islands",
42758 "Pakistan (پاکستان)",
42768 "Palestine (فلسطين)",
42778 "Papua New Guinea",
42820 "Réunion (La Réunion)",
42826 "Romania (România)",
42842 "Saint Barthélemy",
42853 "Saint Kitts and Nevis",
42863 "Saint Martin (Saint-Martin (partie française))",
42869 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42874 "Saint Vincent and the Grenadines",
42889 "São Tomé and Príncipe (São Tomé e Príncipe)",
42894 "Saudi Arabia (المملكة العربية السعودية)",
42899 "Senegal (Sénégal)",
42929 "Slovakia (Slovensko)",
42934 "Slovenia (Slovenija)",
42944 "Somalia (Soomaaliya)",
42954 "South Korea (대한민국)",
42959 "South Sudan (جنوب السودان)",
42969 "Sri Lanka (ශ්රී ලංකාව)",
42974 "Sudan (السودان)",
42984 "Svalbard and Jan Mayen",
42995 "Sweden (Sverige)",
43000 "Switzerland (Schweiz)",
43005 "Syria (سوريا)",
43050 "Trinidad and Tobago",
43055 "Tunisia (تونس)",
43060 "Turkey (Türkiye)",
43070 "Turks and Caicos Islands",
43080 "U.S. Virgin Islands",
43090 "Ukraine (Україна)",
43095 "United Arab Emirates (الإمارات العربية المتحدة)",
43117 "Uzbekistan (Oʻzbekiston)",
43127 "Vatican City (Città del Vaticano)",
43138 "Vietnam (Việt Nam)",
43143 "Wallis and Futuna (Wallis-et-Futuna)",
43148 "Western Sahara (الصحراء الغربية)",
43154 "Yemen (اليمن)",
43178 * This script refer to:
43179 * Title: International Telephone Input
43180 * Author: Jack O'Connor
43181 * Code version: v12.1.12
43182 * Availability: https://github.com/jackocnr/intl-tel-input.git
43186 * @class Roo.bootstrap.PhoneInput
43187 * @extends Roo.bootstrap.TriggerField
43188 * An input with International dial-code selection
43190 * @cfg {String} defaultDialCode default '+852'
43191 * @cfg {Array} preferedCountries default []
43194 * Create a new PhoneInput.
43195 * @param {Object} config Configuration options
43198 Roo.bootstrap.PhoneInput = function(config) {
43199 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43202 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43204 listWidth: undefined,
43206 selectedClass: 'active',
43208 invalidClass : "has-warning",
43210 validClass: 'has-success',
43212 allowed: '0123456789',
43217 * @cfg {String} defaultDialCode The default dial code when initializing the input
43219 defaultDialCode: '+852',
43222 * @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
43224 preferedCountries: false,
43226 getAutoCreate : function()
43228 var data = Roo.bootstrap.PhoneInputData();
43229 var align = this.labelAlign || this.parentLabelAlign();
43232 this.allCountries = [];
43233 this.dialCodeMapping = [];
43235 for (var i = 0; i < data.length; i++) {
43237 this.allCountries[i] = {
43241 priority: c[3] || 0,
43242 areaCodes: c[4] || null
43244 this.dialCodeMapping[c[2]] = {
43247 priority: c[3] || 0,
43248 areaCodes: c[4] || null
43260 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43261 maxlength: this.max_length,
43262 cls : 'form-control tel-input',
43263 autocomplete: 'new-password'
43266 var hiddenInput = {
43269 cls: 'hidden-tel-input'
43273 hiddenInput.name = this.name;
43276 if (this.disabled) {
43277 input.disabled = true;
43280 var flag_container = {
43297 cls: this.hasFeedback ? 'has-feedback' : '',
43303 cls: 'dial-code-holder',
43310 cls: 'roo-select2-container input-group',
43317 if (this.fieldLabel.length) {
43320 tooltip: 'This field is required'
43326 cls: 'control-label',
43332 html: this.fieldLabel
43335 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43341 if(this.indicatorpos == 'right') {
43342 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43349 if(align == 'left') {
43357 if(this.labelWidth > 12){
43358 label.style = "width: " + this.labelWidth + 'px';
43360 if(this.labelWidth < 13 && this.labelmd == 0){
43361 this.labelmd = this.labelWidth;
43363 if(this.labellg > 0){
43364 label.cls += ' col-lg-' + this.labellg;
43365 input.cls += ' col-lg-' + (12 - this.labellg);
43367 if(this.labelmd > 0){
43368 label.cls += ' col-md-' + this.labelmd;
43369 container.cls += ' col-md-' + (12 - this.labelmd);
43371 if(this.labelsm > 0){
43372 label.cls += ' col-sm-' + this.labelsm;
43373 container.cls += ' col-sm-' + (12 - this.labelsm);
43375 if(this.labelxs > 0){
43376 label.cls += ' col-xs-' + this.labelxs;
43377 container.cls += ' col-xs-' + (12 - this.labelxs);
43387 var settings = this;
43389 ['xs','sm','md','lg'].map(function(size){
43390 if (settings[size]) {
43391 cfg.cls += ' col-' + size + '-' + settings[size];
43395 this.store = new Roo.data.Store({
43396 proxy : new Roo.data.MemoryProxy({}),
43397 reader : new Roo.data.JsonReader({
43408 'name' : 'dialCode',
43412 'name' : 'priority',
43416 'name' : 'areaCodes',
43423 if(!this.preferedCountries) {
43424 this.preferedCountries = [
43431 var p = this.preferedCountries.reverse();
43434 for (var i = 0; i < p.length; i++) {
43435 for (var j = 0; j < this.allCountries.length; j++) {
43436 if(this.allCountries[j].iso2 == p[i]) {
43437 var t = this.allCountries[j];
43438 this.allCountries.splice(j,1);
43439 this.allCountries.unshift(t);
43445 this.store.proxy.data = {
43447 data: this.allCountries
43453 initEvents : function()
43456 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43458 this.indicator = this.indicatorEl();
43459 this.flag = this.flagEl();
43460 this.dialCodeHolder = this.dialCodeHolderEl();
43462 this.trigger = this.el.select('div.flag-box',true).first();
43463 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43468 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43469 _this.list.setWidth(lw);
43472 this.list.on('mouseover', this.onViewOver, this);
43473 this.list.on('mousemove', this.onViewMove, this);
43474 this.inputEl().on("keyup", this.onKeyUp, this);
43475 this.inputEl().on("keypress", this.onKeyPress, this);
43477 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43479 this.view = new Roo.View(this.list, this.tpl, {
43480 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43483 this.view.on('click', this.onViewClick, this);
43484 this.setValue(this.defaultDialCode);
43487 onTriggerClick : function(e)
43489 Roo.log('trigger click');
43494 if(this.isExpanded()){
43496 this.hasFocus = false;
43498 this.store.load({});
43499 this.hasFocus = true;
43504 isExpanded : function()
43506 return this.list.isVisible();
43509 collapse : function()
43511 if(!this.isExpanded()){
43515 Roo.get(document).un('mousedown', this.collapseIf, this);
43516 Roo.get(document).un('mousewheel', this.collapseIf, this);
43517 this.fireEvent('collapse', this);
43521 expand : function()
43525 if(this.isExpanded() || !this.hasFocus){
43529 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43530 this.list.setWidth(lw);
43533 this.restrictHeight();
43535 Roo.get(document).on('mousedown', this.collapseIf, this);
43536 Roo.get(document).on('mousewheel', this.collapseIf, this);
43538 this.fireEvent('expand', this);
43541 restrictHeight : function()
43543 this.list.alignTo(this.inputEl(), this.listAlign);
43544 this.list.alignTo(this.inputEl(), this.listAlign);
43547 onViewOver : function(e, t)
43549 if(this.inKeyMode){
43552 var item = this.view.findItemFromChild(t);
43555 var index = this.view.indexOf(item);
43556 this.select(index, false);
43561 onViewClick : function(view, doFocus, el, e)
43563 var index = this.view.getSelectedIndexes()[0];
43565 var r = this.store.getAt(index);
43568 this.onSelect(r, index);
43570 if(doFocus !== false && !this.blockFocus){
43571 this.inputEl().focus();
43575 onViewMove : function(e, t)
43577 this.inKeyMode = false;
43580 select : function(index, scrollIntoView)
43582 this.selectedIndex = index;
43583 this.view.select(index);
43584 if(scrollIntoView !== false){
43585 var el = this.view.getNode(index);
43587 this.list.scrollChildIntoView(el, false);
43592 createList : function()
43594 this.list = Roo.get(document.body).createChild({
43596 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43597 style: 'display:none'
43600 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43603 collapseIf : function(e)
43605 var in_combo = e.within(this.el);
43606 var in_list = e.within(this.list);
43607 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43609 if (in_combo || in_list || is_list) {
43615 onSelect : function(record, index)
43617 if(this.fireEvent('beforeselect', this, record, index) !== false){
43619 this.setFlagClass(record.data.iso2);
43620 this.setDialCode(record.data.dialCode);
43621 this.hasFocus = false;
43623 this.fireEvent('select', this, record, index);
43627 flagEl : function()
43629 var flag = this.el.select('div.flag',true).first();
43636 dialCodeHolderEl : function()
43638 var d = this.el.select('input.dial-code-holder',true).first();
43645 setDialCode : function(v)
43647 this.dialCodeHolder.dom.value = '+'+v;
43650 setFlagClass : function(n)
43652 this.flag.dom.className = 'flag '+n;
43655 getValue : function()
43657 var v = this.inputEl().getValue();
43658 if(this.dialCodeHolder) {
43659 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43664 setValue : function(v)
43666 var d = this.getDialCode(v);
43668 //invalid dial code
43669 if(v.length == 0 || !d || d.length == 0) {
43671 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43672 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43678 this.setFlagClass(this.dialCodeMapping[d].iso2);
43679 this.setDialCode(d);
43680 this.inputEl().dom.value = v.replace('+'+d,'');
43681 this.hiddenEl().dom.value = this.getValue();
43686 getDialCode : function(v)
43690 if (v.length == 0) {
43691 return this.dialCodeHolder.dom.value;
43695 if (v.charAt(0) != "+") {
43698 var numericChars = "";
43699 for (var i = 1; i < v.length; i++) {
43700 var c = v.charAt(i);
43703 if (this.dialCodeMapping[numericChars]) {
43704 dialCode = v.substr(1, i);
43706 if (numericChars.length == 4) {
43716 this.setValue(this.defaultDialCode);
43720 hiddenEl : function()
43722 return this.el.select('input.hidden-tel-input',true).first();
43725 // after setting val
43726 onKeyUp : function(e){
43727 this.setValue(this.getValue());
43730 onKeyPress : function(e){
43731 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43738 * @class Roo.bootstrap.MoneyField
43739 * @extends Roo.bootstrap.ComboBox
43740 * Bootstrap MoneyField class
43743 * Create a new MoneyField.
43744 * @param {Object} config Configuration options
43747 Roo.bootstrap.MoneyField = function(config) {
43749 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43753 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43756 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43758 allowDecimals : true,
43760 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43762 decimalSeparator : ".",
43764 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43766 decimalPrecision : 0,
43768 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43770 allowNegative : true,
43772 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43776 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43778 minValue : Number.NEGATIVE_INFINITY,
43780 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43782 maxValue : Number.MAX_VALUE,
43784 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43786 minText : "The minimum value for this field is {0}",
43788 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43790 maxText : "The maximum value for this field is {0}",
43792 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
43793 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43795 nanText : "{0} is not a valid number",
43797 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43801 * @cfg {String} defaults currency of the MoneyField
43802 * value should be in lkey
43804 defaultCurrency : false,
43806 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43808 thousandsDelimiter : false,
43810 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43821 getAutoCreate : function()
43823 var align = this.labelAlign || this.parentLabelAlign();
43835 cls : 'form-control roo-money-amount-input',
43836 autocomplete: 'new-password'
43839 var hiddenInput = {
43843 cls: 'hidden-number-input'
43846 if(this.max_length) {
43847 input.maxlength = this.max_length;
43851 hiddenInput.name = this.name;
43854 if (this.disabled) {
43855 input.disabled = true;
43858 var clg = 12 - this.inputlg;
43859 var cmd = 12 - this.inputmd;
43860 var csm = 12 - this.inputsm;
43861 var cxs = 12 - this.inputxs;
43865 cls : 'row roo-money-field',
43869 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43873 cls: 'roo-select2-container input-group',
43877 cls : 'form-control roo-money-currency-input',
43878 autocomplete: 'new-password',
43880 name : this.currencyName
43884 cls : 'input-group-addon',
43898 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43902 cls: this.hasFeedback ? 'has-feedback' : '',
43913 if (this.fieldLabel.length) {
43916 tooltip: 'This field is required'
43922 cls: 'control-label',
43928 html: this.fieldLabel
43931 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43937 if(this.indicatorpos == 'right') {
43938 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43945 if(align == 'left') {
43953 if(this.labelWidth > 12){
43954 label.style = "width: " + this.labelWidth + 'px';
43956 if(this.labelWidth < 13 && this.labelmd == 0){
43957 this.labelmd = this.labelWidth;
43959 if(this.labellg > 0){
43960 label.cls += ' col-lg-' + this.labellg;
43961 input.cls += ' col-lg-' + (12 - this.labellg);
43963 if(this.labelmd > 0){
43964 label.cls += ' col-md-' + this.labelmd;
43965 container.cls += ' col-md-' + (12 - this.labelmd);
43967 if(this.labelsm > 0){
43968 label.cls += ' col-sm-' + this.labelsm;
43969 container.cls += ' col-sm-' + (12 - this.labelsm);
43971 if(this.labelxs > 0){
43972 label.cls += ' col-xs-' + this.labelxs;
43973 container.cls += ' col-xs-' + (12 - this.labelxs);
43984 var settings = this;
43986 ['xs','sm','md','lg'].map(function(size){
43987 if (settings[size]) {
43988 cfg.cls += ' col-' + size + '-' + settings[size];
43995 initEvents : function()
43997 this.indicator = this.indicatorEl();
43999 this.initCurrencyEvent();
44001 this.initNumberEvent();
44004 initCurrencyEvent : function()
44007 throw "can not find store for combo";
44010 this.store = Roo.factory(this.store, Roo.data);
44011 this.store.parent = this;
44015 this.triggerEl = this.el.select('.input-group-addon', true).first();
44017 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44022 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44023 _this.list.setWidth(lw);
44026 this.list.on('mouseover', this.onViewOver, this);
44027 this.list.on('mousemove', this.onViewMove, this);
44028 this.list.on('scroll', this.onViewScroll, this);
44031 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44034 this.view = new Roo.View(this.list, this.tpl, {
44035 singleSelect:true, store: this.store, selectedClass: this.selectedClass
44038 this.view.on('click', this.onViewClick, this);
44040 this.store.on('beforeload', this.onBeforeLoad, this);
44041 this.store.on('load', this.onLoad, this);
44042 this.store.on('loadexception', this.onLoadException, this);
44044 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44045 "up" : function(e){
44046 this.inKeyMode = true;
44050 "down" : function(e){
44051 if(!this.isExpanded()){
44052 this.onTriggerClick();
44054 this.inKeyMode = true;
44059 "enter" : function(e){
44062 if(this.fireEvent("specialkey", this, e)){
44063 this.onViewClick(false);
44069 "esc" : function(e){
44073 "tab" : function(e){
44076 if(this.fireEvent("specialkey", this, e)){
44077 this.onViewClick(false);
44085 doRelay : function(foo, bar, hname){
44086 if(hname == 'down' || this.scope.isExpanded()){
44087 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44095 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44099 initNumberEvent : function(e)
44101 this.inputEl().on("keydown" , this.fireKey, this);
44102 this.inputEl().on("focus", this.onFocus, this);
44103 this.inputEl().on("blur", this.onBlur, this);
44105 this.inputEl().relayEvent('keyup', this);
44107 if(this.indicator){
44108 this.indicator.addClass('invisible');
44111 this.originalValue = this.getValue();
44113 if(this.validationEvent == 'keyup'){
44114 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44115 this.inputEl().on('keyup', this.filterValidation, this);
44117 else if(this.validationEvent !== false){
44118 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44121 if(this.selectOnFocus){
44122 this.on("focus", this.preFocus, this);
44125 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44126 this.inputEl().on("keypress", this.filterKeys, this);
44128 this.inputEl().relayEvent('keypress', this);
44131 var allowed = "0123456789";
44133 if(this.allowDecimals){
44134 allowed += this.decimalSeparator;
44137 if(this.allowNegative){
44141 if(this.thousandsDelimiter) {
44145 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44147 var keyPress = function(e){
44149 var k = e.getKey();
44151 var c = e.getCharCode();
44154 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44155 allowed.indexOf(String.fromCharCode(c)) === -1
44161 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44165 if(allowed.indexOf(String.fromCharCode(c)) === -1){
44170 this.inputEl().on("keypress", keyPress, this);
44174 onTriggerClick : function(e)
44181 this.loadNext = false;
44183 if(this.isExpanded()){
44188 this.hasFocus = true;
44190 if(this.triggerAction == 'all') {
44191 this.doQuery(this.allQuery, true);
44195 this.doQuery(this.getRawValue());
44198 getCurrency : function()
44200 var v = this.currencyEl().getValue();
44205 restrictHeight : function()
44207 this.list.alignTo(this.currencyEl(), this.listAlign);
44208 this.list.alignTo(this.currencyEl(), this.listAlign);
44211 onViewClick : function(view, doFocus, el, e)
44213 var index = this.view.getSelectedIndexes()[0];
44215 var r = this.store.getAt(index);
44218 this.onSelect(r, index);
44222 onSelect : function(record, index){
44224 if(this.fireEvent('beforeselect', this, record, index) !== false){
44226 this.setFromCurrencyData(index > -1 ? record.data : false);
44230 this.fireEvent('select', this, record, index);
44234 setFromCurrencyData : function(o)
44238 this.lastCurrency = o;
44240 if (this.currencyField) {
44241 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44243 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
44246 this.lastSelectionText = currency;
44248 //setting default currency
44249 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44250 this.setCurrency(this.defaultCurrency);
44254 this.setCurrency(currency);
44257 setFromData : function(o)
44261 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44263 this.setFromCurrencyData(c);
44268 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44270 Roo.log('no value set for '+ (this.name ? this.name : this.id));
44273 this.setValue(value);
44277 setCurrency : function(v)
44279 this.currencyValue = v;
44282 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44287 setValue : function(v)
44289 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44295 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44297 this.inputEl().dom.value = (v == '') ? '' :
44298 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44300 if(!this.allowZero && v === '0') {
44301 this.hiddenEl().dom.value = '';
44302 this.inputEl().dom.value = '';
44309 getRawValue : function()
44311 var v = this.inputEl().getValue();
44316 getValue : function()
44318 return this.fixPrecision(this.parseValue(this.getRawValue()));
44321 parseValue : function(value)
44323 if(this.thousandsDelimiter) {
44325 r = new RegExp(",", "g");
44326 value = value.replace(r, "");
44329 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44330 return isNaN(value) ? '' : value;
44334 fixPrecision : function(value)
44336 if(this.thousandsDelimiter) {
44338 r = new RegExp(",", "g");
44339 value = value.replace(r, "");
44342 var nan = isNaN(value);
44344 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44345 return nan ? '' : value;
44347 return parseFloat(value).toFixed(this.decimalPrecision);
44350 decimalPrecisionFcn : function(v)
44352 return Math.floor(v);
44355 validateValue : function(value)
44357 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44361 var num = this.parseValue(value);
44364 this.markInvalid(String.format(this.nanText, value));
44368 if(num < this.minValue){
44369 this.markInvalid(String.format(this.minText, this.minValue));
44373 if(num > this.maxValue){
44374 this.markInvalid(String.format(this.maxText, this.maxValue));
44381 validate : function()
44383 if(this.disabled || this.allowBlank){
44388 var currency = this.getCurrency();
44390 if(this.validateValue(this.getRawValue()) && currency.length){
44395 this.markInvalid();
44399 getName: function()
44404 beforeBlur : function()
44410 var v = this.parseValue(this.getRawValue());
44417 onBlur : function()
44421 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44422 //this.el.removeClass(this.focusClass);
44425 this.hasFocus = false;
44427 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44431 var v = this.getValue();
44433 if(String(v) !== String(this.startValue)){
44434 this.fireEvent('change', this, v, this.startValue);
44437 this.fireEvent("blur", this);
44440 inputEl : function()
44442 return this.el.select('.roo-money-amount-input', true).first();
44445 currencyEl : function()
44447 return this.el.select('.roo-money-currency-input', true).first();
44450 hiddenEl : function()
44452 return this.el.select('input.hidden-number-input',true).first();
44456 * @class Roo.bootstrap.BezierSignature
44457 * @extends Roo.bootstrap.Component
44458 * Bootstrap BezierSignature class
44459 * This script refer to:
44460 * Title: Signature Pad
44462 * Availability: https://github.com/szimek/signature_pad
44465 * Create a new BezierSignature
44466 * @param {Object} config The config object
44469 Roo.bootstrap.BezierSignature = function(config){
44470 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44476 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44483 mouse_btn_down: true,
44486 * @cfg {int} canvas height
44488 canvas_height: '200px',
44491 * @cfg {float|function} Radius of a single dot.
44496 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44501 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44506 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44511 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44516 * @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.
44518 bg_color: 'rgba(0, 0, 0, 0)',
44521 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44523 dot_color: 'black',
44526 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44528 velocity_filter_weight: 0.7,
44531 * @cfg {function} Callback when stroke begin.
44536 * @cfg {function} Callback when stroke end.
44540 getAutoCreate : function()
44542 var cls = 'roo-signature column';
44545 cls += ' ' + this.cls;
44555 for(var i = 0; i < col_sizes.length; i++) {
44556 if(this[col_sizes[i]]) {
44557 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44567 cls: 'roo-signature-body',
44571 cls: 'roo-signature-body-canvas',
44572 height: this.canvas_height,
44573 width: this.canvas_width
44580 style: 'display: none'
44588 initEvents: function()
44590 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44592 var canvas = this.canvasEl();
44594 // mouse && touch event swapping...
44595 canvas.dom.style.touchAction = 'none';
44596 canvas.dom.style.msTouchAction = 'none';
44598 this.mouse_btn_down = false;
44599 canvas.on('mousedown', this._handleMouseDown, this);
44600 canvas.on('mousemove', this._handleMouseMove, this);
44601 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44603 if (window.PointerEvent) {
44604 canvas.on('pointerdown', this._handleMouseDown, this);
44605 canvas.on('pointermove', this._handleMouseMove, this);
44606 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44609 if ('ontouchstart' in window) {
44610 canvas.on('touchstart', this._handleTouchStart, this);
44611 canvas.on('touchmove', this._handleTouchMove, this);
44612 canvas.on('touchend', this._handleTouchEnd, this);
44615 Roo.EventManager.onWindowResize(this.resize, this, true);
44617 // file input event
44618 this.fileEl().on('change', this.uploadImage, this);
44625 resize: function(){
44627 var canvas = this.canvasEl().dom;
44628 var ctx = this.canvasElCtx();
44629 var img_data = false;
44631 if(canvas.width > 0) {
44632 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44634 // setting canvas width will clean img data
44637 var style = window.getComputedStyle ?
44638 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44640 var padding_left = parseInt(style.paddingLeft) || 0;
44641 var padding_right = parseInt(style.paddingRight) || 0;
44643 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44646 ctx.putImageData(img_data, 0, 0);
44650 _handleMouseDown: function(e)
44652 if (e.browserEvent.which === 1) {
44653 this.mouse_btn_down = true;
44654 this.strokeBegin(e);
44658 _handleMouseMove: function (e)
44660 if (this.mouse_btn_down) {
44661 this.strokeMoveUpdate(e);
44665 _handleMouseUp: function (e)
44667 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44668 this.mouse_btn_down = false;
44673 _handleTouchStart: function (e) {
44675 e.preventDefault();
44676 if (e.browserEvent.targetTouches.length === 1) {
44677 // var touch = e.browserEvent.changedTouches[0];
44678 // this.strokeBegin(touch);
44680 this.strokeBegin(e); // assume e catching the correct xy...
44684 _handleTouchMove: function (e) {
44685 e.preventDefault();
44686 // var touch = event.targetTouches[0];
44687 // _this._strokeMoveUpdate(touch);
44688 this.strokeMoveUpdate(e);
44691 _handleTouchEnd: function (e) {
44692 var wasCanvasTouched = e.target === this.canvasEl().dom;
44693 if (wasCanvasTouched) {
44694 e.preventDefault();
44695 // var touch = event.changedTouches[0];
44696 // _this._strokeEnd(touch);
44701 reset: function () {
44702 this._lastPoints = [];
44703 this._lastVelocity = 0;
44704 this._lastWidth = (this.min_width + this.max_width) / 2;
44705 this.canvasElCtx().fillStyle = this.dot_color;
44708 strokeMoveUpdate: function(e)
44710 this.strokeUpdate(e);
44712 if (this.throttle) {
44713 this.throttleStroke(this.strokeUpdate, this.throttle);
44716 this.strokeUpdate(e);
44720 strokeBegin: function(e)
44722 var newPointGroup = {
44723 color: this.dot_color,
44727 if (typeof this.onBegin === 'function') {
44731 this.curve_data.push(newPointGroup);
44733 this.strokeUpdate(e);
44736 strokeUpdate: function(e)
44738 var rect = this.canvasEl().dom.getBoundingClientRect();
44739 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44740 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44741 var lastPoints = lastPointGroup.points;
44742 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44743 var isLastPointTooClose = lastPoint
44744 ? point.distanceTo(lastPoint) <= this.min_distance
44746 var color = lastPointGroup.color;
44747 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44748 var curve = this.addPoint(point);
44750 this.drawDot({color: color, point: point});
44753 this.drawCurve({color: color, curve: curve});
44763 strokeEnd: function(e)
44765 this.strokeUpdate(e);
44766 if (typeof this.onEnd === 'function') {
44771 addPoint: function (point) {
44772 var _lastPoints = this._lastPoints;
44773 _lastPoints.push(point);
44774 if (_lastPoints.length > 2) {
44775 if (_lastPoints.length === 3) {
44776 _lastPoints.unshift(_lastPoints[0]);
44778 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44779 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44780 _lastPoints.shift();
44786 calculateCurveWidths: function (startPoint, endPoint) {
44787 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44788 (1 - this.velocity_filter_weight) * this._lastVelocity;
44790 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44793 start: this._lastWidth
44796 this._lastVelocity = velocity;
44797 this._lastWidth = newWidth;
44801 drawDot: function (_a) {
44802 var color = _a.color, point = _a.point;
44803 var ctx = this.canvasElCtx();
44804 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44806 this.drawCurveSegment(point.x, point.y, width);
44808 ctx.fillStyle = color;
44812 drawCurve: function (_a) {
44813 var color = _a.color, curve = _a.curve;
44814 var ctx = this.canvasElCtx();
44815 var widthDelta = curve.endWidth - curve.startWidth;
44816 var drawSteps = Math.floor(curve.length()) * 2;
44818 ctx.fillStyle = color;
44819 for (var i = 0; i < drawSteps; i += 1) {
44820 var t = i / drawSteps;
44826 var x = uuu * curve.startPoint.x;
44827 x += 3 * uu * t * curve.control1.x;
44828 x += 3 * u * tt * curve.control2.x;
44829 x += ttt * curve.endPoint.x;
44830 var y = uuu * curve.startPoint.y;
44831 y += 3 * uu * t * curve.control1.y;
44832 y += 3 * u * tt * curve.control2.y;
44833 y += ttt * curve.endPoint.y;
44834 var width = curve.startWidth + ttt * widthDelta;
44835 this.drawCurveSegment(x, y, width);
44841 drawCurveSegment: function (x, y, width) {
44842 var ctx = this.canvasElCtx();
44844 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44845 this.is_empty = false;
44850 var ctx = this.canvasElCtx();
44851 var canvas = this.canvasEl().dom;
44852 ctx.fillStyle = this.bg_color;
44853 ctx.clearRect(0, 0, canvas.width, canvas.height);
44854 ctx.fillRect(0, 0, canvas.width, canvas.height);
44855 this.curve_data = [];
44857 this.is_empty = true;
44862 return this.el.select('input',true).first();
44865 canvasEl: function()
44867 return this.el.select('canvas',true).first();
44870 canvasElCtx: function()
44872 return this.el.select('canvas',true).first().dom.getContext('2d');
44875 getImage: function(type)
44877 if(this.is_empty) {
44882 return this.canvasEl().dom.toDataURL('image/'+type, 1);
44885 drawFromImage: function(img_src)
44887 var img = new Image();
44889 img.onload = function(){
44890 this.canvasElCtx().drawImage(img, 0, 0);
44895 this.is_empty = false;
44898 selectImage: function()
44900 this.fileEl().dom.click();
44903 uploadImage: function(e)
44905 var reader = new FileReader();
44907 reader.onload = function(e){
44908 var img = new Image();
44909 img.onload = function(){
44911 this.canvasElCtx().drawImage(img, 0, 0);
44913 img.src = e.target.result;
44916 reader.readAsDataURL(e.target.files[0]);
44919 // Bezier Point Constructor
44920 Point: (function () {
44921 function Point(x, y, time) {
44924 this.time = time || Date.now();
44926 Point.prototype.distanceTo = function (start) {
44927 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44929 Point.prototype.equals = function (other) {
44930 return this.x === other.x && this.y === other.y && this.time === other.time;
44932 Point.prototype.velocityFrom = function (start) {
44933 return this.time !== start.time
44934 ? this.distanceTo(start) / (this.time - start.time)
44941 // Bezier Constructor
44942 Bezier: (function () {
44943 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44944 this.startPoint = startPoint;
44945 this.control2 = control2;
44946 this.control1 = control1;
44947 this.endPoint = endPoint;
44948 this.startWidth = startWidth;
44949 this.endWidth = endWidth;
44951 Bezier.fromPoints = function (points, widths, scope) {
44952 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44953 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44954 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44956 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44957 var dx1 = s1.x - s2.x;
44958 var dy1 = s1.y - s2.y;
44959 var dx2 = s2.x - s3.x;
44960 var dy2 = s2.y - s3.y;
44961 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44962 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44963 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44964 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44965 var dxm = m1.x - m2.x;
44966 var dym = m1.y - m2.y;
44967 var k = l2 / (l1 + l2);
44968 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44969 var tx = s2.x - cm.x;
44970 var ty = s2.y - cm.y;
44972 c1: new scope.Point(m1.x + tx, m1.y + ty),
44973 c2: new scope.Point(m2.x + tx, m2.y + ty)
44976 Bezier.prototype.length = function () {
44981 for (var i = 0; i <= steps; i += 1) {
44983 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44984 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44986 var xdiff = cx - px;
44987 var ydiff = cy - py;
44988 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44995 Bezier.prototype.point = function (t, start, c1, c2, end) {
44996 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44997 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44998 + (3.0 * c2 * (1.0 - t) * t * t)
44999 + (end * t * t * t);
45004 throttleStroke: function(fn, wait) {
45005 if (wait === void 0) { wait = 250; }
45007 var timeout = null;
45011 var later = function () {
45012 previous = Date.now();
45014 result = fn.apply(storedContext, storedArgs);
45016 storedContext = null;
45020 return function wrapper() {
45022 for (var _i = 0; _i < arguments.length; _i++) {
45023 args[_i] = arguments[_i];
45025 var now = Date.now();
45026 var remaining = wait - (now - previous);
45027 storedContext = this;
45029 if (remaining <= 0 || remaining > wait) {
45031 clearTimeout(timeout);
45035 result = fn.apply(storedContext, storedArgs);
45037 storedContext = null;
45041 else if (!timeout) {
45042 timeout = window.setTimeout(later, remaining);