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 this.el.dom.src = url;
3237 this.el.select('img', true).first().dom.src = url;
3253 * @class Roo.bootstrap.Link
3254 * @extends Roo.bootstrap.Component
3255 * Bootstrap Link Class
3256 * @cfg {String} alt image alternative text
3257 * @cfg {String} href a tag href
3258 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3259 * @cfg {String} html the content of the link.
3260 * @cfg {String} anchor name for the anchor link
3261 * @cfg {String} fa - favicon
3263 * @cfg {Boolean} preventDefault (true | false) default false
3267 * Create a new Input
3268 * @param {Object} config The config object
3271 Roo.bootstrap.Link = function(config){
3272 Roo.bootstrap.Link.superclass.constructor.call(this, config);
3278 * The img click event for the img.
3279 * @param {Roo.EventObject} e
3285 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
3289 preventDefault: false,
3295 getAutoCreate : function()
3297 var html = this.html || '';
3299 if (this.fa !== false) {
3300 html = '<i class="fa fa-' + this.fa + '"></i>';
3305 // anchor's do not require html/href...
3306 if (this.anchor === false) {
3308 cfg.href = this.href || '#';
3310 cfg.name = this.anchor;
3311 if (this.html !== false || this.fa !== false) {
3314 if (this.href !== false) {
3315 cfg.href = this.href;
3319 if(this.alt !== false){
3324 if(this.target !== false) {
3325 cfg.target = this.target;
3331 initEvents: function() {
3333 if(!this.href || this.preventDefault){
3334 this.el.on('click', this.onClick, this);
3338 onClick : function(e)
3340 if(this.preventDefault){
3343 //Roo.log('img onclick');
3344 this.fireEvent('click', this, e);
3357 * @class Roo.bootstrap.Header
3358 * @extends Roo.bootstrap.Component
3359 * Bootstrap Header class
3360 * @cfg {String} html content of header
3361 * @cfg {Number} level (1|2|3|4|5|6) default 1
3364 * Create a new Header
3365 * @param {Object} config The config object
3369 Roo.bootstrap.Header = function(config){
3370 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3373 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3381 getAutoCreate : function(){
3386 tag: 'h' + (1 *this.level),
3387 html: this.html || ''
3399 * Ext JS Library 1.1.1
3400 * Copyright(c) 2006-2007, Ext JS, LLC.
3402 * Originally Released Under LGPL - original licence link has changed is not relivant.
3405 * <script type="text/javascript">
3409 * @class Roo.bootstrap.MenuMgr
3410 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3413 Roo.bootstrap.MenuMgr = function(){
3414 var menus, active, groups = {}, attached = false, lastShow = new Date();
3416 // private - called when first menu is created
3419 active = new Roo.util.MixedCollection();
3420 Roo.get(document).addKeyListener(27, function(){
3421 if(active.length > 0){
3429 if(active && active.length > 0){
3430 var c = active.clone();
3440 if(active.length < 1){
3441 Roo.get(document).un("mouseup", onMouseDown);
3449 var last = active.last();
3450 lastShow = new Date();
3453 Roo.get(document).on("mouseup", onMouseDown);
3458 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3459 m.parentMenu.activeChild = m;
3460 }else if(last && last.isVisible()){
3461 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3466 function onBeforeHide(m){
3468 m.activeChild.hide();
3470 if(m.autoHideTimer){
3471 clearTimeout(m.autoHideTimer);
3472 delete m.autoHideTimer;
3477 function onBeforeShow(m){
3478 var pm = m.parentMenu;
3479 if(!pm && !m.allowOtherMenus){
3481 }else if(pm && pm.activeChild && active != m){
3482 pm.activeChild.hide();
3486 // private this should really trigger on mouseup..
3487 function onMouseDown(e){
3488 Roo.log("on Mouse Up");
3490 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3491 Roo.log("MenuManager hideAll");
3500 function onBeforeCheck(mi, state){
3502 var g = groups[mi.group];
3503 for(var i = 0, l = g.length; i < l; i++){
3505 g[i].setChecked(false);
3514 * Hides all menus that are currently visible
3516 hideAll : function(){
3521 register : function(menu){
3525 menus[menu.id] = menu;
3526 menu.on("beforehide", onBeforeHide);
3527 menu.on("hide", onHide);
3528 menu.on("beforeshow", onBeforeShow);
3529 menu.on("show", onShow);
3531 if(g && menu.events["checkchange"]){
3535 groups[g].push(menu);
3536 menu.on("checkchange", onCheck);
3541 * Returns a {@link Roo.menu.Menu} object
3542 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3543 * be used to generate and return a new Menu instance.
3545 get : function(menu){
3546 if(typeof menu == "string"){ // menu id
3548 }else if(menu.events){ // menu instance
3551 /*else if(typeof menu.length == 'number'){ // array of menu items?
3552 return new Roo.bootstrap.Menu({items:menu});
3553 }else{ // otherwise, must be a config
3554 return new Roo.bootstrap.Menu(menu);
3561 unregister : function(menu){
3562 delete menus[menu.id];
3563 menu.un("beforehide", onBeforeHide);
3564 menu.un("hide", onHide);
3565 menu.un("beforeshow", onBeforeShow);
3566 menu.un("show", onShow);
3568 if(g && menu.events["checkchange"]){
3569 groups[g].remove(menu);
3570 menu.un("checkchange", onCheck);
3575 registerCheckable : function(menuItem){
3576 var g = menuItem.group;
3581 groups[g].push(menuItem);
3582 menuItem.on("beforecheckchange", onBeforeCheck);
3587 unregisterCheckable : function(menuItem){
3588 var g = menuItem.group;
3590 groups[g].remove(menuItem);
3591 menuItem.un("beforecheckchange", onBeforeCheck);
3603 * @class Roo.bootstrap.Menu
3604 * @extends Roo.bootstrap.Component
3605 * Bootstrap Menu class - container for MenuItems
3606 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3607 * @cfg {bool} hidden if the menu should be hidden when rendered.
3608 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3609 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3610 * @cfg {bool} hideTrigger (true|false) default false - hide the carret for trigger.
3611 * @cfg {String} align default tl-bl? == below - how the menu should be aligned.
3615 * @param {Object} config The config object
3619 Roo.bootstrap.Menu = function(config){
3621 if (config.type == 'treeview') {
3622 // normally menu's are drawn attached to the document to handle layering etc..
3623 // however treeview (used by the docs menu is drawn into the parent element)
3624 this.container_method = 'getChildContainer';
3627 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3628 if (this.registerMenu && this.type != 'treeview') {
3629 Roo.bootstrap.MenuMgr.register(this);
3636 * Fires before this menu is displayed (return false to block)
3637 * @param {Roo.menu.Menu} this
3642 * Fires before this menu is hidden (return false to block)
3643 * @param {Roo.menu.Menu} this
3648 * Fires after this menu is displayed
3649 * @param {Roo.menu.Menu} this
3654 * Fires after this menu is hidden
3655 * @param {Roo.menu.Menu} this
3660 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3661 * @param {Roo.menu.Menu} this
3662 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3663 * @param {Roo.EventObject} e
3668 * Fires when the mouse is hovering over this menu
3669 * @param {Roo.menu.Menu} this
3670 * @param {Roo.EventObject} e
3671 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3676 * Fires when the mouse exits this menu
3677 * @param {Roo.menu.Menu} this
3678 * @param {Roo.EventObject} e
3679 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3684 * Fires when a menu item contained in this menu is clicked
3685 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3686 * @param {Roo.EventObject} e
3690 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3693 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
3697 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3700 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3702 registerMenu : true,
3704 menuItems :false, // stores the menu items..
3714 container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3716 hideTrigger : false,
3721 getChildContainer : function() {
3725 getAutoCreate : function(){
3727 //if (['right'].indexOf(this.align)!==-1) {
3728 // cfg.cn[1].cls += ' pull-right'
3733 cls : 'dropdown-menu shadow' ,
3734 style : 'z-index:1000'
3738 if (this.type === 'submenu') {
3739 cfg.cls = 'submenu active';
3741 if (this.type === 'treeview') {
3742 cfg.cls = 'treeview-menu';
3747 initEvents : function() {
3749 // Roo.log("ADD event");
3750 // Roo.log(this.triggerEl.dom);
3751 if (this.triggerEl) {
3753 this.triggerEl.on('click', this.onTriggerClick, this);
3755 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3757 if (!this.hideTrigger) {
3758 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3759 // dropdown toggle on the 'a' in BS4?
3760 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3762 this.triggerEl.addClass('dropdown-toggle');
3768 this.el.on('touchstart' , this.onTouch, this);
3770 this.el.on('click' , this.onClick, this);
3772 this.el.on("mouseover", this.onMouseOver, this);
3773 this.el.on("mouseout", this.onMouseOut, this);
3777 findTargetItem : function(e)
3779 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3783 //Roo.log(t); Roo.log(t.id);
3785 //Roo.log(this.menuitems);
3786 return this.menuitems.get(t.id);
3788 //return this.items.get(t.menuItemId);
3794 onTouch : function(e)
3796 Roo.log("menu.onTouch");
3797 //e.stopEvent(); this make the user popdown broken
3801 onClick : function(e)
3803 Roo.log("menu.onClick");
3805 var t = this.findTargetItem(e);
3806 if(!t || t.isContainer){
3811 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3812 if(t == this.activeItem && t.shouldDeactivate(e)){
3813 this.activeItem.deactivate();
3814 delete this.activeItem;
3818 this.setActiveItem(t, true);
3826 Roo.log('pass click event');
3830 this.fireEvent("click", this, t, e);
3834 if(!t.href.length || t.href == '#'){
3835 (function() { _this.hide(); }).defer(100);
3840 onMouseOver : function(e){
3841 var t = this.findTargetItem(e);
3844 // if(t.canActivate && !t.disabled){
3845 // this.setActiveItem(t, true);
3849 this.fireEvent("mouseover", this, e, t);
3851 isVisible : function(){
3852 return !this.hidden;
3854 onMouseOut : function(e){
3855 var t = this.findTargetItem(e);
3858 // if(t == this.activeItem && t.shouldDeactivate(e)){
3859 // this.activeItem.deactivate();
3860 // delete this.activeItem;
3863 this.fireEvent("mouseout", this, e, t);
3868 * Displays this menu relative to another element
3869 * @param {String/HTMLElement/Roo.Element} element The element to align to
3870 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3871 * the element (defaults to this.defaultAlign)
3872 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3874 show : function(el, pos, parentMenu)
3876 if (false === this.fireEvent("beforeshow", this)) {
3877 Roo.log("show canceled");
3880 this.parentMenu = parentMenu;
3884 this.el.addClass('show'); // show otherwise we do not know how big we are..
3886 var xy = this.el.getAlignToXY(el, pos);
3888 // bl-tl << left align below
3889 // tl-bl << left align
3891 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3892 // if it goes to far to the right.. -> align left.
3893 xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3896 // was left align - go right?
3897 xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3900 // goes down the bottom
3901 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3903 var a = this.align.replace('?', '').split('-');
3904 xy = this.el.getAlignToXY(el, a[1] + '-' + a[0] + '?')
3908 this.showAt( xy , parentMenu, false);
3911 * Displays this menu at a specific xy position
3912 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3913 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3915 showAt : function(xy, parentMenu, /* private: */_e){
3916 this.parentMenu = parentMenu;
3921 this.fireEvent("beforeshow", this);
3922 //xy = this.el.adjustForConstraints(xy);
3926 this.hideMenuItems();
3927 this.hidden = false;
3928 if (this.triggerEl) {
3929 this.triggerEl.addClass('open');
3932 this.el.addClass('show');
3936 // reassign x when hitting right
3938 // reassign y when hitting bottom
3940 // but the list may align on trigger left or trigger top... should it be a properity?
3942 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3947 this.fireEvent("show", this);
3953 this.doFocus.defer(50, this);
3957 doFocus : function(){
3959 this.focusEl.focus();
3964 * Hides this menu and optionally all parent menus
3965 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3967 hide : function(deep)
3969 if (false === this.fireEvent("beforehide", this)) {
3970 Roo.log("hide canceled");
3973 this.hideMenuItems();
3974 if(this.el && this.isVisible()){
3976 if(this.activeItem){
3977 this.activeItem.deactivate();
3978 this.activeItem = null;
3980 if (this.triggerEl) {
3981 this.triggerEl.removeClass('open');
3984 this.el.removeClass('show');
3986 this.fireEvent("hide", this);
3988 if(deep === true && this.parentMenu){
3989 this.parentMenu.hide(true);
3993 onTriggerClick : function(e)
3995 Roo.log('trigger click');
3997 var target = e.getTarget();
3999 Roo.log(target.nodeName.toLowerCase());
4001 if(target.nodeName.toLowerCase() === 'i'){
4007 onTriggerPress : function(e)
4009 Roo.log('trigger press');
4010 //Roo.log(e.getTarget());
4011 // Roo.log(this.triggerEl.dom);
4013 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4014 var pel = Roo.get(e.getTarget());
4015 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4016 Roo.log('is treeview or dropdown?');
4020 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4024 if (this.isVisible()) {
4030 this.show(this.triggerEl, this.align, false);
4033 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4040 hideMenuItems : function()
4042 Roo.log("hide Menu Items");
4047 this.el.select('.open',true).each(function(aa) {
4049 aa.removeClass('open');
4053 addxtypeChild : function (tree, cntr) {
4054 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4056 this.menuitems.add(comp);
4068 this.getEl().dom.innerHTML = '';
4069 this.menuitems.clear();
4083 * @class Roo.bootstrap.MenuItem
4084 * @extends Roo.bootstrap.Component
4085 * Bootstrap MenuItem class
4086 * @cfg {String} html the menu label
4087 * @cfg {String} href the link
4088 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4089 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4090 * @cfg {Boolean} active used on sidebars to highlight active itesm
4091 * @cfg {String} fa favicon to show on left of menu item.
4092 * @cfg {Roo.bootsrap.Menu} menu the child menu.
4096 * Create a new MenuItem
4097 * @param {Object} config The config object
4101 Roo.bootstrap.MenuItem = function(config){
4102 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4107 * The raw click event for the entire grid.
4108 * @param {Roo.bootstrap.MenuItem} this
4109 * @param {Roo.EventObject} e
4115 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
4119 preventDefault: false,
4120 isContainer : false,
4124 getAutoCreate : function(){
4126 if(this.isContainer){
4129 cls: 'dropdown-menu-item '
4139 cls : 'dropdown-item',
4144 if (this.fa !== false) {
4147 cls : 'fa fa-' + this.fa
4156 cls: 'dropdown-menu-item',
4159 if (this.parent().type == 'treeview') {
4160 cfg.cls = 'treeview-menu';
4163 cfg.cls += ' active';
4168 anc.href = this.href || cfg.cn[0].href ;
4169 ctag.html = this.html || cfg.cn[0].html ;
4173 initEvents: function()
4175 if (this.parent().type == 'treeview') {
4176 this.el.select('a').on('click', this.onClick, this);
4180 this.menu.parentType = this.xtype;
4181 this.menu.triggerEl = this.el;
4182 this.menu = this.addxtype(Roo.apply({}, this.menu));
4186 onClick : function(e)
4188 Roo.log('item on click ');
4190 if(this.preventDefault){
4193 //this.parent().hideMenuItems();
4195 this.fireEvent('click', this, e);
4214 * @class Roo.bootstrap.MenuSeparator
4215 * @extends Roo.bootstrap.Component
4216 * Bootstrap MenuSeparator class
4219 * Create a new MenuItem
4220 * @param {Object} config The config object
4224 Roo.bootstrap.MenuSeparator = function(config){
4225 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4228 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
4230 getAutoCreate : function(){
4249 * @class Roo.bootstrap.Modal
4250 * @extends Roo.bootstrap.Component
4251 * Bootstrap Modal class
4252 * @cfg {String} title Title of dialog
4253 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4254 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
4255 * @cfg {Boolean} specificTitle default false
4256 * @cfg {Array} buttons Array of buttons or standard button set..
4257 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4258 * @cfg {Boolean} animate default true
4259 * @cfg {Boolean} allow_close default true
4260 * @cfg {Boolean} fitwindow default false
4261 * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4262 * @cfg {Number} width fixed width - usefull for chrome extension only really.
4263 * @cfg {Number} height fixed height - usefull for chrome extension only really.
4264 * @cfg {String} size (sm|lg|xl) default empty
4265 * @cfg {Number} max_width set the max width of modal
4266 * @cfg {Boolean} editableTitle can the title be edited
4271 * Create a new Modal Dialog
4272 * @param {Object} config The config object
4275 Roo.bootstrap.Modal = function(config){
4276 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4281 * The raw btnclick event for the button
4282 * @param {Roo.EventObject} e
4287 * Fire when dialog resize
4288 * @param {Roo.bootstrap.Modal} this
4289 * @param {Roo.EventObject} e
4293 * @event titlechanged
4294 * Fire when the editable title has been changed
4295 * @param {Roo.bootstrap.Modal} this
4296 * @param {Roo.EventObject} value
4298 "titlechanged" : true
4301 this.buttons = this.buttons || [];
4304 this.tmpl = Roo.factory(this.tmpl);
4309 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
4311 title : 'test dialog',
4321 specificTitle: false,
4323 buttonPosition: 'right',
4345 editableTitle : false,
4347 onRender : function(ct, position)
4349 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4352 var cfg = Roo.apply({}, this.getAutoCreate());
4355 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4357 //if (!cfg.name.length) {
4361 cfg.cls += ' ' + this.cls;
4364 cfg.style = this.style;
4366 this.el = Roo.get(document.body).createChild(cfg, position);
4368 //var type = this.el.dom.type;
4371 if(this.tabIndex !== undefined){
4372 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4375 this.dialogEl = this.el.select('.modal-dialog',true).first();
4376 this.bodyEl = this.el.select('.modal-body',true).first();
4377 this.closeEl = this.el.select('.modal-header .close', true).first();
4378 this.headerEl = this.el.select('.modal-header',true).first();
4379 this.titleEl = this.el.select('.modal-title',true).first();
4380 this.footerEl = this.el.select('.modal-footer',true).first();
4382 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4384 //this.el.addClass("x-dlg-modal");
4386 if (this.buttons.length) {
4387 Roo.each(this.buttons, function(bb) {
4388 var b = Roo.apply({}, bb);
4389 b.xns = b.xns || Roo.bootstrap;
4390 b.xtype = b.xtype || 'Button';
4391 if (typeof(b.listeners) == 'undefined') {
4392 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4395 var btn = Roo.factory(b);
4397 btn.render(this.getButtonContainer());
4401 // render the children.
4404 if(typeof(this.items) != 'undefined'){
4405 var items = this.items;
4408 for(var i =0;i < items.length;i++) {
4409 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4413 this.items = nitems;
4415 // where are these used - they used to be body/close/footer
4419 //this.el.addClass([this.fieldClass, this.cls]);
4423 getAutoCreate : function()
4425 // we will default to modal-body-overflow - might need to remove or make optional later.
4427 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''),
4428 html : this.html || ''
4433 cls : 'modal-title',
4437 if(this.specificTitle){ // WTF is this?
4442 if (this.allow_close && Roo.bootstrap.version == 3) {
4452 if (this.editableTitle) {
4454 cls: 'form-control roo-editable-title d-none',
4460 if (this.allow_close && Roo.bootstrap.version == 4) {
4470 if(this.size.length){
4471 size = 'modal-' + this.size;
4474 var footer = Roo.bootstrap.version == 3 ?
4476 cls : 'modal-footer',
4480 cls: 'btn-' + this.buttonPosition
4485 { // BS4 uses mr-auto on left buttons....
4486 cls : 'modal-footer'
4497 cls: "modal-dialog " + size,
4500 cls : "modal-content",
4503 cls : 'modal-header',
4518 modal.cls += ' fade';
4524 getChildContainer : function() {
4529 getButtonContainer : function() {
4531 return Roo.bootstrap.version == 4 ?
4532 this.el.select('.modal-footer',true).first()
4533 : this.el.select('.modal-footer div',true).first();
4536 initEvents : function()
4538 if (this.allow_close) {
4539 this.closeEl.on('click', this.hide, this);
4541 Roo.EventManager.onWindowResize(this.resize, this, true);
4542 if (this.editableTitle) {
4543 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4544 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4545 this.headerEditEl.on('keyup', function(e) {
4546 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4547 this.toggleHeaderInput(false)
4550 this.headerEditEl.on('blur', function(e) {
4551 this.toggleHeaderInput(false)
4560 this.maskEl.setSize(
4561 Roo.lib.Dom.getViewWidth(true),
4562 Roo.lib.Dom.getViewHeight(true)
4565 if (this.fitwindow) {
4567 this.dialogEl.setStyle( { 'max-width' : '100%' });
4569 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4570 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4575 if(this.max_width !== 0) {
4577 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4580 this.setSize(w, this.height);
4584 if(this.max_height) {
4585 this.setSize(w,Math.min(
4587 Roo.lib.Dom.getViewportHeight(true) - 60
4593 if(!this.fit_content) {
4594 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4598 this.setSize(w, Math.min(
4600 this.headerEl.getHeight() +
4601 this.footerEl.getHeight() +
4602 this.getChildHeight(this.bodyEl.dom.childNodes),
4603 Roo.lib.Dom.getViewportHeight(true) - 60)
4609 setSize : function(w,h)
4620 if (!this.rendered) {
4623 this.toggleHeaderInput(false);
4624 //this.el.setStyle('display', 'block');
4625 this.el.removeClass('hideing');
4626 this.el.dom.style.display='block';
4628 Roo.get(document.body).addClass('modal-open');
4630 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4633 this.el.addClass('show');
4634 this.el.addClass('in');
4637 this.el.addClass('show');
4638 this.el.addClass('in');
4641 // not sure how we can show data in here..
4643 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4646 Roo.get(document.body).addClass("x-body-masked");
4648 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4649 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4650 this.maskEl.dom.style.display = 'block';
4651 this.maskEl.addClass('show');
4656 this.fireEvent('show', this);
4658 // set zindex here - otherwise it appears to be ignored...
4659 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4662 this.items.forEach( function(e) {
4663 e.layout ? e.layout() : false;
4671 if(this.fireEvent("beforehide", this) !== false){
4673 this.maskEl.removeClass('show');
4675 this.maskEl.dom.style.display = '';
4676 Roo.get(document.body).removeClass("x-body-masked");
4677 this.el.removeClass('in');
4678 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4680 if(this.animate){ // why
4681 this.el.addClass('hideing');
4682 this.el.removeClass('show');
4684 if (!this.el.hasClass('hideing')) {
4685 return; // it's been shown again...
4688 this.el.dom.style.display='';
4690 Roo.get(document.body).removeClass('modal-open');
4691 this.el.removeClass('hideing');
4695 this.el.removeClass('show');
4696 this.el.dom.style.display='';
4697 Roo.get(document.body).removeClass('modal-open');
4700 this.fireEvent('hide', this);
4703 isVisible : function()
4706 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4710 addButton : function(str, cb)
4714 var b = Roo.apply({}, { html : str } );
4715 b.xns = b.xns || Roo.bootstrap;
4716 b.xtype = b.xtype || 'Button';
4717 if (typeof(b.listeners) == 'undefined') {
4718 b.listeners = { click : cb.createDelegate(this) };
4721 var btn = Roo.factory(b);
4723 btn.render(this.getButtonContainer());
4729 setDefaultButton : function(btn)
4731 //this.el.select('.modal-footer').()
4734 resizeTo: function(w,h)
4736 this.dialogEl.setWidth(w);
4738 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4740 this.bodyEl.setHeight(h - diff);
4742 this.fireEvent('resize', this);
4745 setContentSize : function(w, h)
4749 onButtonClick: function(btn,e)
4752 this.fireEvent('btnclick', btn.name, e);
4755 * Set the title of the Dialog
4756 * @param {String} str new Title
4758 setTitle: function(str) {
4759 this.titleEl.dom.innerHTML = str;
4763 * Set the body of the Dialog
4764 * @param {String} str new Title
4766 setBody: function(str) {
4767 this.bodyEl.dom.innerHTML = str;
4770 * Set the body of the Dialog using the template
4771 * @param {Obj} data - apply this data to the template and replace the body contents.
4773 applyBody: function(obj)
4776 Roo.log("Error - using apply Body without a template");
4779 this.tmpl.overwrite(this.bodyEl, obj);
4782 getChildHeight : function(child_nodes)
4786 child_nodes.length == 0
4791 var child_height = 0;
4793 for(var i = 0; i < child_nodes.length; i++) {
4796 * for modal with tabs...
4797 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4799 var layout_childs = child_nodes[i].childNodes;
4801 for(var j = 0; j < layout_childs.length; j++) {
4803 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4805 var layout_body_childs = layout_childs[j].childNodes;
4807 for(var k = 0; k < layout_body_childs.length; k++) {
4809 if(layout_body_childs[k].classList.contains('navbar')) {
4810 child_height += layout_body_childs[k].offsetHeight;
4814 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4816 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4818 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4820 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4821 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4836 child_height += child_nodes[i].offsetHeight;
4837 // Roo.log(child_nodes[i].offsetHeight);
4840 return child_height;
4842 toggleHeaderInput : function(is_edit)
4844 if (!this.editableTitle) {
4845 return; // not editable.
4847 if (is_edit && this.is_header_editing) {
4848 return; // already editing..
4852 this.headerEditEl.dom.value = this.title;
4853 this.headerEditEl.removeClass('d-none');
4854 this.headerEditEl.dom.focus();
4855 this.titleEl.addClass('d-none');
4857 this.is_header_editing = true;
4860 // flip back to not editing.
4861 this.title = this.headerEditEl.dom.value;
4862 this.headerEditEl.addClass('d-none');
4863 this.titleEl.removeClass('d-none');
4864 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4865 this.is_header_editing = false;
4866 this.fireEvent('titlechanged', this, this.title);
4875 Roo.apply(Roo.bootstrap.Modal, {
4877 * Button config that displays a single OK button
4886 * Button config that displays Yes and No buttons
4902 * Button config that displays OK and Cancel buttons
4917 * Button config that displays Yes, No and Cancel buttons
4942 * messagebox - can be used as a replace
4946 * @class Roo.MessageBox
4947 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4951 Roo.Msg.alert('Status', 'Changes saved successfully.');
4953 // Prompt for user data:
4954 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4956 // process text value...
4960 // Show a dialog using config options:
4962 title:'Save Changes?',
4963 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4964 buttons: Roo.Msg.YESNOCANCEL,
4971 Roo.bootstrap.MessageBox = function(){
4972 var dlg, opt, mask, waitTimer;
4973 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4974 var buttons, activeTextEl, bwidth;
4978 var handleButton = function(button){
4980 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4984 var handleHide = function(){
4986 dlg.el.removeClass(opt.cls);
4989 // Roo.TaskMgr.stop(waitTimer);
4990 // waitTimer = null;
4995 var updateButtons = function(b){
4998 buttons["ok"].hide();
4999 buttons["cancel"].hide();
5000 buttons["yes"].hide();
5001 buttons["no"].hide();
5002 dlg.footerEl.hide();
5006 dlg.footerEl.show();
5007 for(var k in buttons){
5008 if(typeof buttons[k] != "function"){
5011 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5012 width += buttons[k].el.getWidth()+15;
5022 var handleEsc = function(d, k, e){
5023 if(opt && opt.closable !== false){
5033 * Returns a reference to the underlying {@link Roo.BasicDialog} element
5034 * @return {Roo.BasicDialog} The BasicDialog element
5036 getDialog : function(){
5038 dlg = new Roo.bootstrap.Modal( {
5041 //constraintoviewport:false,
5043 //collapsible : false,
5048 //buttonAlign:"center",
5049 closeClick : function(){
5050 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5053 handleButton("cancel");
5058 dlg.on("hide", handleHide);
5060 //dlg.addKeyListener(27, handleEsc);
5062 this.buttons = buttons;
5063 var bt = this.buttonText;
5064 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5065 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5066 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5067 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5069 bodyEl = dlg.bodyEl.createChild({
5071 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5072 '<textarea class="roo-mb-textarea"></textarea>' +
5073 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
5075 msgEl = bodyEl.dom.firstChild;
5076 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5077 textboxEl.enableDisplayMode();
5078 textboxEl.addKeyListener([10,13], function(){
5079 if(dlg.isVisible() && opt && opt.buttons){
5082 }else if(opt.buttons.yes){
5083 handleButton("yes");
5087 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5088 textareaEl.enableDisplayMode();
5089 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5090 progressEl.enableDisplayMode();
5092 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5093 var pf = progressEl.dom.firstChild;
5095 pp = Roo.get(pf.firstChild);
5096 pp.setHeight(pf.offsetHeight);
5104 * Updates the message box body text
5105 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5106 * the XHTML-compliant non-breaking space character '&#160;')
5107 * @return {Roo.MessageBox} This message box
5109 updateText : function(text)
5111 if(!dlg.isVisible() && !opt.width){
5112 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5113 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5115 msgEl.innerHTML = text || ' ';
5117 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5118 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5120 Math.min(opt.width || cw , this.maxWidth),
5121 Math.max(opt.minWidth || this.minWidth, bwidth)
5124 activeTextEl.setWidth(w);
5126 if(dlg.isVisible()){
5127 dlg.fixedcenter = false;
5129 // to big, make it scroll. = But as usual stupid IE does not support
5132 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5133 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5134 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5136 bodyEl.dom.style.height = '';
5137 bodyEl.dom.style.overflowY = '';
5140 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5142 bodyEl.dom.style.overflowX = '';
5145 dlg.setContentSize(w, bodyEl.getHeight());
5146 if(dlg.isVisible()){
5147 dlg.fixedcenter = true;
5153 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
5154 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5155 * @param {Number} value Any number between 0 and 1 (e.g., .5)
5156 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5157 * @return {Roo.MessageBox} This message box
5159 updateProgress : function(value, text){
5161 this.updateText(text);
5164 if (pp) { // weird bug on my firefox - for some reason this is not defined
5165 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5166 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5172 * Returns true if the message box is currently displayed
5173 * @return {Boolean} True if the message box is visible, else false
5175 isVisible : function(){
5176 return dlg && dlg.isVisible();
5180 * Hides the message box if it is displayed
5183 if(this.isVisible()){
5189 * Displays a new message box, or reinitializes an existing message box, based on the config options
5190 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5191 * The following config object properties are supported:
5193 Property Type Description
5194 ---------- --------------- ------------------------------------------------------------------------------------
5195 animEl String/Element An id or Element from which the message box should animate as it opens and
5196 closes (defaults to undefined)
5197 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5198 cancel:'Bar'}), or false to not show any buttons (defaults to false)
5199 closable Boolean False to hide the top-right close button (defaults to true). Note that
5200 progress and wait dialogs will ignore this property and always hide the
5201 close button as they can only be closed programmatically.
5202 cls String A custom CSS class to apply to the message box element
5203 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
5204 displayed (defaults to 75)
5205 fn Function A callback function to execute after closing the dialog. The arguments to the
5206 function will be btn (the name of the button that was clicked, if applicable,
5207 e.g. "ok"), and text (the value of the active text field, if applicable).
5208 Progress and wait dialogs will ignore this option since they do not respond to
5209 user actions and can only be closed programmatically, so any required function
5210 should be called by the same code after it closes the dialog.
5211 icon String A CSS class that provides a background image to be used as an icon for
5212 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5213 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
5214 minWidth Number The minimum width in pixels of the message box (defaults to 100)
5215 modal Boolean False to allow user interaction with the page while the message box is
5216 displayed (defaults to true)
5217 msg String A string that will replace the existing message box body text (defaults
5218 to the XHTML-compliant non-breaking space character ' ')
5219 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
5220 progress Boolean True to display a progress bar (defaults to false)
5221 progressText String The text to display inside the progress bar if progress = true (defaults to '')
5222 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
5223 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
5224 title String The title text
5225 value String The string value to set into the active textbox element if displayed
5226 wait Boolean True to display a progress bar (defaults to false)
5227 width Number The width of the dialog in pixels
5234 msg: 'Please enter your address:',
5236 buttons: Roo.MessageBox.OKCANCEL,
5239 animEl: 'addAddressBtn'
5242 * @param {Object} config Configuration options
5243 * @return {Roo.MessageBox} This message box
5245 show : function(options)
5248 // this causes nightmares if you show one dialog after another
5249 // especially on callbacks..
5251 if(this.isVisible()){
5254 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5255 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
5256 Roo.log("New Dialog Message:" + options.msg )
5257 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5258 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5261 var d = this.getDialog();
5263 d.setTitle(opt.title || " ");
5264 d.closeEl.setDisplayed(opt.closable !== false);
5265 activeTextEl = textboxEl;
5266 opt.prompt = opt.prompt || (opt.multiline ? true : false);
5271 textareaEl.setHeight(typeof opt.multiline == "number" ?
5272 opt.multiline : this.defaultTextHeight);
5273 activeTextEl = textareaEl;
5282 progressEl.setDisplayed(opt.progress === true);
5284 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5286 this.updateProgress(0);
5287 activeTextEl.dom.value = opt.value || "";
5289 dlg.setDefaultButton(activeTextEl);
5291 var bs = opt.buttons;
5295 }else if(bs && bs.yes){
5296 db = buttons["yes"];
5298 dlg.setDefaultButton(db);
5300 bwidth = updateButtons(opt.buttons);
5301 this.updateText(opt.msg);
5303 d.el.addClass(opt.cls);
5305 d.proxyDrag = opt.proxyDrag === true;
5306 d.modal = opt.modal !== false;
5307 d.mask = opt.modal !== false ? mask : false;
5309 // force it to the end of the z-index stack so it gets a cursor in FF
5310 document.body.appendChild(dlg.el.dom);
5311 d.animateTarget = null;
5312 d.show(options.animEl);
5318 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5319 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5320 * and closing the message box when the process is complete.
5321 * @param {String} title The title bar text
5322 * @param {String} msg The message box body text
5323 * @return {Roo.MessageBox} This message box
5325 progress : function(title, msg){
5332 minWidth: this.minProgressWidth,
5339 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5340 * If a callback function is passed it will be called after the user clicks the button, and the
5341 * id of the button that was clicked will be passed as the only parameter to the callback
5342 * (could also be the top-right close button).
5343 * @param {String} title The title bar text
5344 * @param {String} msg The message box body text
5345 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5346 * @param {Object} scope (optional) The scope of the callback function
5347 * @return {Roo.MessageBox} This message box
5349 alert : function(title, msg, fn, scope)
5364 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5365 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5366 * You are responsible for closing the message box when the process is complete.
5367 * @param {String} msg The message box body text
5368 * @param {String} title (optional) The title bar text
5369 * @return {Roo.MessageBox} This message box
5371 wait : function(msg, title){
5382 waitTimer = Roo.TaskMgr.start({
5384 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5392 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5393 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5394 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5395 * @param {String} title The title bar text
5396 * @param {String} msg The message box body text
5397 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5398 * @param {Object} scope (optional) The scope of the callback function
5399 * @return {Roo.MessageBox} This message box
5401 confirm : function(title, msg, fn, scope){
5405 buttons: this.YESNO,
5414 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5415 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5416 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5417 * (could also be the top-right close button) and the text that was entered will be passed as the two
5418 * parameters to the callback.
5419 * @param {String} title The title bar text
5420 * @param {String} msg The message box body text
5421 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5422 * @param {Object} scope (optional) The scope of the callback function
5423 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5424 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5425 * @return {Roo.MessageBox} This message box
5427 prompt : function(title, msg, fn, scope, multiline){
5431 buttons: this.OKCANCEL,
5436 multiline: multiline,
5443 * Button config that displays a single OK button
5448 * Button config that displays Yes and No buttons
5451 YESNO : {yes:true, no:true},
5453 * Button config that displays OK and Cancel buttons
5456 OKCANCEL : {ok:true, cancel:true},
5458 * Button config that displays Yes, No and Cancel buttons
5461 YESNOCANCEL : {yes:true, no:true, cancel:true},
5464 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5467 defaultTextHeight : 75,
5469 * The maximum width in pixels of the message box (defaults to 600)
5474 * The minimum width in pixels of the message box (defaults to 100)
5479 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5480 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5483 minProgressWidth : 250,
5485 * An object containing the default button text strings that can be overriden for localized language support.
5486 * Supported properties are: ok, cancel, yes and no.
5487 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5500 * Shorthand for {@link Roo.MessageBox}
5502 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5503 Roo.Msg = Roo.Msg || Roo.MessageBox;
5512 * @class Roo.bootstrap.Navbar
5513 * @extends Roo.bootstrap.Component
5514 * Bootstrap Navbar class
5517 * Create a new Navbar
5518 * @param {Object} config The config object
5522 Roo.bootstrap.Navbar = function(config){
5523 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5527 * @event beforetoggle
5528 * Fire before toggle the menu
5529 * @param {Roo.EventObject} e
5531 "beforetoggle" : true
5535 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
5544 getAutoCreate : function(){
5547 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5551 initEvents :function ()
5553 //Roo.log(this.el.select('.navbar-toggle',true));
5554 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5561 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5563 var size = this.el.getSize();
5564 this.maskEl.setSize(size.width, size.height);
5565 this.maskEl.enableDisplayMode("block");
5574 getChildContainer : function()
5576 if (this.el && this.el.select('.collapse').getCount()) {
5577 return this.el.select('.collapse',true).first();
5592 onToggle : function()
5595 if(this.fireEvent('beforetoggle', this) === false){
5598 var ce = this.el.select('.navbar-collapse',true).first();
5600 if (!ce.hasClass('show')) {
5610 * Expand the navbar pulldown
5612 expand : function ()
5615 var ce = this.el.select('.navbar-collapse',true).first();
5616 if (ce.hasClass('collapsing')) {
5619 ce.dom.style.height = '';
5621 ce.addClass('in'); // old...
5622 ce.removeClass('collapse');
5623 ce.addClass('show');
5624 var h = ce.getHeight();
5626 ce.removeClass('show');
5627 // at this point we should be able to see it..
5628 ce.addClass('collapsing');
5630 ce.setHeight(0); // resize it ...
5631 ce.on('transitionend', function() {
5632 //Roo.log('done transition');
5633 ce.removeClass('collapsing');
5634 ce.addClass('show');
5635 ce.removeClass('collapse');
5637 ce.dom.style.height = '';
5638 }, this, { single: true} );
5640 ce.dom.scrollTop = 0;
5643 * Collapse the navbar pulldown
5645 collapse : function()
5647 var ce = this.el.select('.navbar-collapse',true).first();
5649 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5650 // it's collapsed or collapsing..
5653 ce.removeClass('in'); // old...
5654 ce.setHeight(ce.getHeight());
5655 ce.removeClass('show');
5656 ce.addClass('collapsing');
5658 ce.on('transitionend', function() {
5659 ce.dom.style.height = '';
5660 ce.removeClass('collapsing');
5661 ce.addClass('collapse');
5662 }, this, { single: true} );
5682 * @class Roo.bootstrap.NavSimplebar
5683 * @extends Roo.bootstrap.Navbar
5684 * Bootstrap Sidebar class
5686 * @cfg {Boolean} inverse is inverted color
5688 * @cfg {String} type (nav | pills | tabs)
5689 * @cfg {Boolean} arrangement stacked | justified
5690 * @cfg {String} align (left | right) alignment
5692 * @cfg {Boolean} main (true|false) main nav bar? default false
5693 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5695 * @cfg {String} tag (header|footer|nav|div) default is nav
5697 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5701 * Create a new Sidebar
5702 * @param {Object} config The config object
5706 Roo.bootstrap.NavSimplebar = function(config){
5707 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5710 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5726 getAutoCreate : function(){
5730 tag : this.tag || 'div',
5731 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5733 if (['light','white'].indexOf(this.weight) > -1) {
5734 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5736 cfg.cls += ' bg-' + this.weight;
5739 cfg.cls += ' navbar-inverse';
5743 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5745 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5754 cls: 'nav nav-' + this.xtype,
5760 this.type = this.type || 'nav';
5761 if (['tabs','pills'].indexOf(this.type) != -1) {
5762 cfg.cn[0].cls += ' nav-' + this.type
5766 if (this.type!=='nav') {
5767 Roo.log('nav type must be nav/tabs/pills')
5769 cfg.cn[0].cls += ' navbar-nav'
5775 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5776 cfg.cn[0].cls += ' nav-' + this.arrangement;
5780 if (this.align === 'right') {
5781 cfg.cn[0].cls += ' navbar-right';
5806 * navbar-expand-md fixed-top
5810 * @class Roo.bootstrap.NavHeaderbar
5811 * @extends Roo.bootstrap.NavSimplebar
5812 * Bootstrap Sidebar class
5814 * @cfg {String} brand what is brand
5815 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5816 * @cfg {String} brand_href href of the brand
5817 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5818 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5819 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5820 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5823 * Create a new Sidebar
5824 * @param {Object} config The config object
5828 Roo.bootstrap.NavHeaderbar = function(config){
5829 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5833 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5840 desktopCenter : false,
5843 getAutoCreate : function(){
5846 tag: this.nav || 'nav',
5847 cls: 'navbar navbar-expand-md',
5853 if (this.desktopCenter) {
5854 cn.push({cls : 'container', cn : []});
5862 cls: 'navbar-toggle navbar-toggler',
5863 'data-toggle': 'collapse',
5868 html: 'Toggle navigation'
5872 cls: 'icon-bar navbar-toggler-icon'
5885 cn.push( Roo.bootstrap.version == 4 ? btn : {
5887 cls: 'navbar-header',
5896 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5900 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5902 if (['light','white'].indexOf(this.weight) > -1) {
5903 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5905 cfg.cls += ' bg-' + this.weight;
5908 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5909 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5911 // tag can override this..
5913 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5916 if (this.brand !== '') {
5917 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5918 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5920 href: this.brand_href ? this.brand_href : '#',
5921 cls: 'navbar-brand',
5929 cfg.cls += ' main-nav';
5937 getHeaderChildContainer : function()
5939 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5940 return this.el.select('.navbar-header',true).first();
5943 return this.getChildContainer();
5946 getChildContainer : function()
5949 return this.el.select('.roo-navbar-collapse',true).first();
5954 initEvents : function()
5956 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5958 if (this.autohide) {
5963 Roo.get(document).on('scroll',function(e) {
5964 var ns = Roo.get(document).getScroll().top;
5965 var os = prevScroll;
5969 ft.removeClass('slideDown');
5970 ft.addClass('slideUp');
5973 ft.removeClass('slideUp');
5974 ft.addClass('slideDown');
5995 * @class Roo.bootstrap.NavSidebar
5996 * @extends Roo.bootstrap.Navbar
5997 * Bootstrap Sidebar class
6000 * Create a new Sidebar
6001 * @param {Object} config The config object
6005 Roo.bootstrap.NavSidebar = function(config){
6006 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
6009 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
6011 sidebar : true, // used by Navbar Item and NavbarGroup at present...
6013 getAutoCreate : function(){
6018 cls: 'sidebar sidebar-nav'
6040 * @class Roo.bootstrap.NavGroup
6041 * @extends Roo.bootstrap.Component
6042 * Bootstrap NavGroup class
6043 * @cfg {String} align (left|right)
6044 * @cfg {Boolean} inverse
6045 * @cfg {String} type (nav|pills|tab) default nav
6046 * @cfg {String} navId - reference Id for navbar.
6047 * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6050 * Create a new nav group
6051 * @param {Object} config The config object
6054 Roo.bootstrap.NavGroup = function(config){
6055 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
6058 Roo.bootstrap.NavGroup.register(this);
6062 * Fires when the active item changes
6063 * @param {Roo.bootstrap.NavGroup} this
6064 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6065 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
6072 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
6084 getAutoCreate : function()
6086 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6092 if (Roo.bootstrap.version == 4) {
6093 if (['tabs','pills'].indexOf(this.type) != -1) {
6094 cfg.cls += ' nav-' + this.type;
6096 // trying to remove so header bar can right align top?
6097 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6098 // do not use on header bar...
6099 cfg.cls += ' navbar-nav';
6104 if (['tabs','pills'].indexOf(this.type) != -1) {
6105 cfg.cls += ' nav-' + this.type
6107 if (this.type !== 'nav') {
6108 Roo.log('nav type must be nav/tabs/pills')
6110 cfg.cls += ' navbar-nav'
6114 if (this.parent() && this.parent().sidebar) {
6117 cls: 'dashboard-menu sidebar-menu'
6123 if (this.form === true) {
6126 cls: 'navbar-form form-inline'
6128 //nav navbar-right ml-md-auto
6129 if (this.align === 'right') {
6130 cfg.cls += ' navbar-right ml-md-auto';
6132 cfg.cls += ' navbar-left';
6136 if (this.align === 'right') {
6137 cfg.cls += ' navbar-right ml-md-auto';
6139 cfg.cls += ' mr-auto';
6143 cfg.cls += ' navbar-inverse';
6151 * sets the active Navigation item
6152 * @param {Roo.bootstrap.NavItem} the new current navitem
6154 setActiveItem : function(item)
6157 Roo.each(this.navItems, function(v){
6162 v.setActive(false, true);
6169 item.setActive(true, true);
6170 this.fireEvent('changed', this, item, prev);
6175 * gets the active Navigation item
6176 * @return {Roo.bootstrap.NavItem} the current navitem
6178 getActive : function()
6182 Roo.each(this.navItems, function(v){
6193 indexOfNav : function()
6197 Roo.each(this.navItems, function(v,i){
6208 * adds a Navigation item
6209 * @param {Roo.bootstrap.NavItem} the navitem to add
6211 addItem : function(cfg)
6213 if (this.form && Roo.bootstrap.version == 4) {
6216 var cn = new Roo.bootstrap.NavItem(cfg);
6218 cn.parentId = this.id;
6219 cn.onRender(this.el, null);
6223 * register a Navigation item
6224 * @param {Roo.bootstrap.NavItem} the navitem to add
6226 register : function(item)
6228 this.navItems.push( item);
6229 item.navId = this.navId;
6234 * clear all the Navigation item
6237 clearAll : function()
6240 this.el.dom.innerHTML = '';
6243 getNavItem: function(tabId)
6246 Roo.each(this.navItems, function(e) {
6247 if (e.tabId == tabId) {
6257 setActiveNext : function()
6259 var i = this.indexOfNav(this.getActive());
6260 if (i > this.navItems.length) {
6263 this.setActiveItem(this.navItems[i+1]);
6265 setActivePrev : function()
6267 var i = this.indexOfNav(this.getActive());
6271 this.setActiveItem(this.navItems[i-1]);
6273 clearWasActive : function(except) {
6274 Roo.each(this.navItems, function(e) {
6275 if (e.tabId != except.tabId && e.was_active) {
6276 e.was_active = false;
6283 getWasActive : function ()
6286 Roo.each(this.navItems, function(e) {
6301 Roo.apply(Roo.bootstrap.NavGroup, {
6305 * register a Navigation Group
6306 * @param {Roo.bootstrap.NavGroup} the navgroup to add
6308 register : function(navgrp)
6310 this.groups[navgrp.navId] = navgrp;
6314 * fetch a Navigation Group based on the navigation ID
6315 * @param {string} the navgroup to add
6316 * @returns {Roo.bootstrap.NavGroup} the navgroup
6318 get: function(navId) {
6319 if (typeof(this.groups[navId]) == 'undefined') {
6321 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6323 return this.groups[navId] ;
6338 * @class Roo.bootstrap.NavItem
6339 * @extends Roo.bootstrap.Component
6340 * Bootstrap Navbar.NavItem class
6341 * @cfg {String} href link to
6342 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6343 * @cfg {Boolean} button_outline show and outlined button
6344 * @cfg {String} html content of button
6345 * @cfg {String} badge text inside badge
6346 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6347 * @cfg {String} glyphicon DEPRICATED - use fa
6348 * @cfg {String} icon DEPRICATED - use fa
6349 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6350 * @cfg {Boolean} active Is item active
6351 * @cfg {Boolean} disabled Is item disabled
6352 * @cfg {String} linkcls Link Class
6353 * @cfg {Boolean} preventDefault (true | false) default false
6354 * @cfg {String} tabId the tab that this item activates.
6355 * @cfg {String} tagtype (a|span) render as a href or span?
6356 * @cfg {Boolean} animateRef (true|false) link to element default false
6359 * Create a new Navbar Item
6360 * @param {Object} config The config object
6362 Roo.bootstrap.NavItem = function(config){
6363 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6368 * The raw click event for the entire grid.
6369 * @param {Roo.EventObject} e
6374 * Fires when the active item active state changes
6375 * @param {Roo.bootstrap.NavItem} this
6376 * @param {boolean} state the new state
6382 * Fires when scroll to element
6383 * @param {Roo.bootstrap.NavItem} this
6384 * @param {Object} options
6385 * @param {Roo.EventObject} e
6393 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
6402 preventDefault : false,
6410 button_outline : false,
6414 getAutoCreate : function(){
6421 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6424 cfg.cls += ' active' ;
6426 if (this.disabled) {
6427 cfg.cls += ' disabled';
6431 if (this.button_weight.length) {
6432 cfg.tag = this.href ? 'a' : 'button';
6433 cfg.html = this.html || '';
6434 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6436 cfg.href = this.href;
6439 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6441 cfg.cls += " nav-html";
6444 // menu .. should add dropdown-menu class - so no need for carat..
6446 if (this.badge !== '') {
6448 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6453 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6457 href : this.href || "#",
6458 html: this.html || '',
6462 if (this.tagtype == 'a') {
6463 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
6467 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6468 } else if (this.fa) {
6469 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6470 } else if(this.glyphicon) {
6471 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6473 cfg.cn[0].cls += " nav-html";
6477 cfg.cn[0].html += " <span class='caret'></span>";
6481 if (this.badge !== '') {
6482 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6490 onRender : function(ct, position)
6492 // Roo.log("Call onRender: " + this.xtype);
6493 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6497 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6498 this.navLink = this.el.select('.nav-link',true).first();
6499 this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6504 initEvents: function()
6506 if (typeof (this.menu) != 'undefined') {
6507 this.menu.parentType = this.xtype;
6508 this.menu.triggerEl = this.el;
6509 this.menu = this.addxtype(Roo.apply({}, this.menu));
6512 this.el.on('click', this.onClick, this);
6514 //if(this.tagtype == 'span'){
6515 // this.el.select('span',true).on('click', this.onClick, this);
6518 // at this point parent should be available..
6519 this.parent().register(this);
6522 onClick : function(e)
6524 if (e.getTarget('.dropdown-menu-item')) {
6525 // did you click on a menu itemm.... - then don't trigger onclick..
6530 this.preventDefault ||
6533 Roo.log("NavItem - prevent Default?");
6537 if (this.disabled) {
6541 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6542 if (tg && tg.transition) {
6543 Roo.log("waiting for the transitionend");
6549 //Roo.log("fire event clicked");
6550 if(this.fireEvent('click', this, e) === false){
6554 if(this.tagtype == 'span'){
6558 //Roo.log(this.href);
6559 var ael = this.el.select('a',true).first();
6562 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6563 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6564 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6565 return; // ignore... - it's a 'hash' to another page.
6567 Roo.log("NavItem - prevent Default?");
6569 this.scrollToElement(e);
6573 var p = this.parent();
6575 if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6576 if (typeof(p.setActiveItem) !== 'undefined') {
6577 p.setActiveItem(this);
6581 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6582 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6583 // remove the collapsed menu expand...
6584 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6588 isActive: function () {
6591 setActive : function(state, fire, is_was_active)
6593 if (this.active && !state && this.navId) {
6594 this.was_active = true;
6595 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6597 nv.clearWasActive(this);
6601 this.active = state;
6604 this.el.removeClass('active');
6605 this.navLink ? this.navLink.removeClass('active') : false;
6606 } else if (!this.el.hasClass('active')) {
6608 this.el.addClass('active');
6609 if (Roo.bootstrap.version == 4 && this.navLink ) {
6610 this.navLink.addClass('active');
6615 this.fireEvent('changed', this, state);
6618 // show a panel if it's registered and related..
6620 if (!this.navId || !this.tabId || !state || is_was_active) {
6624 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6628 var pan = tg.getPanelByName(this.tabId);
6632 // if we can not flip to new panel - go back to old nav highlight..
6633 if (false == tg.showPanel(pan)) {
6634 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6636 var onav = nv.getWasActive();
6638 onav.setActive(true, false, true);
6647 // this should not be here...
6648 setDisabled : function(state)
6650 this.disabled = state;
6652 this.el.removeClass('disabled');
6653 } else if (!this.el.hasClass('disabled')) {
6654 this.el.addClass('disabled');
6660 * Fetch the element to display the tooltip on.
6661 * @return {Roo.Element} defaults to this.el
6663 tooltipEl : function()
6665 return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
6668 scrollToElement : function(e)
6670 var c = document.body;
6673 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6675 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6676 c = document.documentElement;
6679 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6685 var o = target.calcOffsetsTo(c);
6692 this.fireEvent('scrollto', this, options, e);
6694 Roo.get(c).scrollTo('top', options.value, true);
6699 * Set the HTML (text content) of the item
6700 * @param {string} html content for the nav item
6702 setHtml : function(html)
6705 this.htmlEl.dom.innerHTML = html;
6717 * <span> icon </span>
6718 * <span> text </span>
6719 * <span>badge </span>
6723 * @class Roo.bootstrap.NavSidebarItem
6724 * @extends Roo.bootstrap.NavItem
6725 * Bootstrap Navbar.NavSidebarItem class
6726 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6727 * {Boolean} open is the menu open
6728 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6729 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6730 * {String} buttonSize (sm|md|lg)the extra classes for the button
6731 * {Boolean} showArrow show arrow next to the text (default true)
6733 * Create a new Navbar Button
6734 * @param {Object} config The config object
6736 Roo.bootstrap.NavSidebarItem = function(config){
6737 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6742 * The raw click event for the entire grid.
6743 * @param {Roo.EventObject} e
6748 * Fires when the active item active state changes
6749 * @param {Roo.bootstrap.NavSidebarItem} this
6750 * @param {boolean} state the new state
6758 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6760 badgeWeight : 'default',
6766 buttonWeight : 'default',
6772 getAutoCreate : function(){
6777 href : this.href || '#',
6783 if(this.buttonView){
6786 href : this.href || '#',
6787 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6800 cfg.cls += ' active';
6803 if (this.disabled) {
6804 cfg.cls += ' disabled';
6807 cfg.cls += ' open x-open';
6810 if (this.glyphicon || this.icon) {
6811 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6812 a.cn.push({ tag : 'i', cls : c }) ;
6815 if(!this.buttonView){
6818 html : this.html || ''
6825 if (this.badge !== '') {
6826 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6832 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6835 a.cls += ' dropdown-toggle treeview' ;
6841 initEvents : function()
6843 if (typeof (this.menu) != 'undefined') {
6844 this.menu.parentType = this.xtype;
6845 this.menu.triggerEl = this.el;
6846 this.menu = this.addxtype(Roo.apply({}, this.menu));
6849 this.el.on('click', this.onClick, this);
6851 if(this.badge !== ''){
6852 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6857 onClick : function(e)
6864 if(this.preventDefault){
6868 this.fireEvent('click', this, e);
6871 disable : function()
6873 this.setDisabled(true);
6878 this.setDisabled(false);
6881 setDisabled : function(state)
6883 if(this.disabled == state){
6887 this.disabled = state;
6890 this.el.addClass('disabled');
6894 this.el.removeClass('disabled');
6899 setActive : function(state)
6901 if(this.active == state){
6905 this.active = state;
6908 this.el.addClass('active');
6912 this.el.removeClass('active');
6917 isActive: function ()
6922 setBadge : function(str)
6928 this.badgeEl.dom.innerHTML = str;
6943 Roo.namespace('Roo.bootstrap.breadcrumb');
6947 * @class Roo.bootstrap.breadcrumb.Nav
6948 * @extends Roo.bootstrap.Component
6949 * Bootstrap Breadcrumb Nav Class
6951 * @children Roo.bootstrap.breadcrumb.Item
6954 * Create a new breadcrumb.Nav
6955 * @param {Object} config The config object
6959 Roo.bootstrap.breadcrumb.Nav = function(config){
6960 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6965 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
6967 getAutoCreate : function()
6984 initEvents: function()
6986 this.olEl = this.el.select('ol',true).first();
6988 getChildContainer : function()
7004 * @class Roo.bootstrap.breadcrumb.Nav
7005 * @extends Roo.bootstrap.Component
7006 * Bootstrap Breadcrumb Nav Class
7008 * @children Roo.bootstrap.breadcrumb.Component
7009 * @cfg {String} html the content of the link.
7010 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7011 * @cfg {Boolean} active is it active
7015 * Create a new breadcrumb.Nav
7016 * @param {Object} config The config object
7019 Roo.bootstrap.breadcrumb.Item = function(config){
7020 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7025 * The img click event for the img.
7026 * @param {Roo.EventObject} e
7033 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
7038 getAutoCreate : function()
7043 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7045 if (this.href !== false) {
7052 cfg.html = this.html;
7058 initEvents: function()
7061 this.el.select('a', true).first().on('click',this.onClick, this)
7065 onClick : function(e)
7068 this.fireEvent('click',this, e);
7081 * @class Roo.bootstrap.Row
7082 * @extends Roo.bootstrap.Component
7083 * Bootstrap Row class (contains columns...)
7087 * @param {Object} config The config object
7090 Roo.bootstrap.Row = function(config){
7091 Roo.bootstrap.Row.superclass.constructor.call(this, config);
7094 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
7096 getAutoCreate : function(){
7115 * @class Roo.bootstrap.Pagination
7116 * @extends Roo.bootstrap.Component
7117 * Bootstrap Pagination class
7118 * @cfg {String} size xs | sm | md | lg
7119 * @cfg {Boolean} inverse false | true
7122 * Create a new Pagination
7123 * @param {Object} config The config object
7126 Roo.bootstrap.Pagination = function(config){
7127 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7130 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
7136 getAutoCreate : function(){
7142 cfg.cls += ' inverse';
7148 cfg.cls += " " + this.cls;
7166 * @class Roo.bootstrap.PaginationItem
7167 * @extends Roo.bootstrap.Component
7168 * Bootstrap PaginationItem class
7169 * @cfg {String} html text
7170 * @cfg {String} href the link
7171 * @cfg {Boolean} preventDefault (true | false) default true
7172 * @cfg {Boolean} active (true | false) default false
7173 * @cfg {Boolean} disabled default false
7177 * Create a new PaginationItem
7178 * @param {Object} config The config object
7182 Roo.bootstrap.PaginationItem = function(config){
7183 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7188 * The raw click event for the entire grid.
7189 * @param {Roo.EventObject} e
7195 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
7199 preventDefault: true,
7204 getAutoCreate : function(){
7210 href : this.href ? this.href : '#',
7211 html : this.html ? this.html : ''
7221 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7225 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7231 initEvents: function() {
7233 this.el.on('click', this.onClick, this);
7236 onClick : function(e)
7238 Roo.log('PaginationItem on click ');
7239 if(this.preventDefault){
7247 this.fireEvent('click', this, e);
7263 * @class Roo.bootstrap.Slider
7264 * @extends Roo.bootstrap.Component
7265 * Bootstrap Slider class
7268 * Create a new Slider
7269 * @param {Object} config The config object
7272 Roo.bootstrap.Slider = function(config){
7273 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7276 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
7278 getAutoCreate : function(){
7282 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7286 cls: 'ui-slider-handle ui-state-default ui-corner-all'
7298 * Ext JS Library 1.1.1
7299 * Copyright(c) 2006-2007, Ext JS, LLC.
7301 * Originally Released Under LGPL - original licence link has changed is not relivant.
7304 * <script type="text/javascript">
7309 * @class Roo.grid.ColumnModel
7310 * @extends Roo.util.Observable
7311 * This is the default implementation of a ColumnModel used by the Grid. It defines
7312 * the columns in the grid.
7315 var colModel = new Roo.grid.ColumnModel([
7316 {header: "Ticker", width: 60, sortable: true, locked: true},
7317 {header: "Company Name", width: 150, sortable: true},
7318 {header: "Market Cap.", width: 100, sortable: true},
7319 {header: "$ Sales", width: 100, sortable: true, renderer: money},
7320 {header: "Employees", width: 100, sortable: true, resizable: false}
7325 * The config options listed for this class are options which may appear in each
7326 * individual column definition.
7327 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7329 * @param {Object} config An Array of column config objects. See this class's
7330 * config objects for details.
7332 Roo.grid.ColumnModel = function(config){
7334 * The config passed into the constructor
7336 this.config = []; //config;
7339 // if no id, create one
7340 // if the column does not have a dataIndex mapping,
7341 // map it to the order it is in the config
7342 for(var i = 0, len = config.length; i < len; i++){
7343 this.addColumn(config[i]);
7348 * The width of columns which have no width specified (defaults to 100)
7351 this.defaultWidth = 100;
7354 * Default sortable of columns which have no sortable specified (defaults to false)
7357 this.defaultSortable = false;
7361 * @event widthchange
7362 * Fires when the width of a column changes.
7363 * @param {ColumnModel} this
7364 * @param {Number} columnIndex The column index
7365 * @param {Number} newWidth The new width
7367 "widthchange": true,
7369 * @event headerchange
7370 * Fires when the text of a header changes.
7371 * @param {ColumnModel} this
7372 * @param {Number} columnIndex The column index
7373 * @param {Number} newText The new header text
7375 "headerchange": true,
7377 * @event hiddenchange
7378 * Fires when a column is hidden or "unhidden".
7379 * @param {ColumnModel} this
7380 * @param {Number} columnIndex The column index
7381 * @param {Boolean} hidden true if hidden, false otherwise
7383 "hiddenchange": true,
7385 * @event columnmoved
7386 * Fires when a column is moved.
7387 * @param {ColumnModel} this
7388 * @param {Number} oldIndex
7389 * @param {Number} newIndex
7391 "columnmoved" : true,
7393 * @event columlockchange
7394 * Fires when a column's locked state is changed
7395 * @param {ColumnModel} this
7396 * @param {Number} colIndex
7397 * @param {Boolean} locked true if locked
7399 "columnlockchange" : true
7401 Roo.grid.ColumnModel.superclass.constructor.call(this);
7403 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7405 * @cfg {String} header The header text to display in the Grid view.
7408 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7409 * {@link Roo.data.Record} definition from which to draw the column's value. If not
7410 * specified, the column's index is used as an index into the Record's data Array.
7413 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7414 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7417 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7418 * Defaults to the value of the {@link #defaultSortable} property.
7419 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7422 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
7425 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
7428 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7431 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7434 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7435 * given the cell's data value. See {@link #setRenderer}. If not specified, the
7436 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7437 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7440 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
7443 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
7446 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
7449 * @cfg {String} cursor (Optional)
7452 * @cfg {String} tooltip (Optional)
7455 * @cfg {Number} xs (Optional)
7458 * @cfg {Number} sm (Optional)
7461 * @cfg {Number} md (Optional)
7464 * @cfg {Number} lg (Optional)
7467 * Returns the id of the column at the specified index.
7468 * @param {Number} index The column index
7469 * @return {String} the id
7471 getColumnId : function(index){
7472 return this.config[index].id;
7476 * Returns the column for a specified id.
7477 * @param {String} id The column id
7478 * @return {Object} the column
7480 getColumnById : function(id){
7481 return this.lookup[id];
7486 * Returns the column Object for a specified dataIndex.
7487 * @param {String} dataIndex The column dataIndex
7488 * @return {Object|Boolean} the column or false if not found
7490 getColumnByDataIndex: function(dataIndex){
7491 var index = this.findColumnIndex(dataIndex);
7492 return index > -1 ? this.config[index] : false;
7496 * Returns the index for a specified column id.
7497 * @param {String} id The column id
7498 * @return {Number} the index, or -1 if not found
7500 getIndexById : function(id){
7501 for(var i = 0, len = this.config.length; i < len; i++){
7502 if(this.config[i].id == id){
7510 * Returns the index for a specified column dataIndex.
7511 * @param {String} dataIndex The column dataIndex
7512 * @return {Number} the index, or -1 if not found
7515 findColumnIndex : function(dataIndex){
7516 for(var i = 0, len = this.config.length; i < len; i++){
7517 if(this.config[i].dataIndex == dataIndex){
7525 moveColumn : function(oldIndex, newIndex){
7526 var c = this.config[oldIndex];
7527 this.config.splice(oldIndex, 1);
7528 this.config.splice(newIndex, 0, c);
7529 this.dataMap = null;
7530 this.fireEvent("columnmoved", this, oldIndex, newIndex);
7533 isLocked : function(colIndex){
7534 return this.config[colIndex].locked === true;
7537 setLocked : function(colIndex, value, suppressEvent){
7538 if(this.isLocked(colIndex) == value){
7541 this.config[colIndex].locked = value;
7543 this.fireEvent("columnlockchange", this, colIndex, value);
7547 getTotalLockedWidth : function(){
7549 for(var i = 0; i < this.config.length; i++){
7550 if(this.isLocked(i) && !this.isHidden(i)){
7551 this.totalWidth += this.getColumnWidth(i);
7557 getLockedCount : function(){
7558 for(var i = 0, len = this.config.length; i < len; i++){
7559 if(!this.isLocked(i)){
7564 return this.config.length;
7568 * Returns the number of columns.
7571 getColumnCount : function(visibleOnly){
7572 if(visibleOnly === true){
7574 for(var i = 0, len = this.config.length; i < len; i++){
7575 if(!this.isHidden(i)){
7581 return this.config.length;
7585 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7586 * @param {Function} fn
7587 * @param {Object} scope (optional)
7588 * @return {Array} result
7590 getColumnsBy : function(fn, scope){
7592 for(var i = 0, len = this.config.length; i < len; i++){
7593 var c = this.config[i];
7594 if(fn.call(scope||this, c, i) === true){
7602 * Returns true if the specified column is sortable.
7603 * @param {Number} col The column index
7606 isSortable : function(col){
7607 if(typeof this.config[col].sortable == "undefined"){
7608 return this.defaultSortable;
7610 return this.config[col].sortable;
7614 * Returns the rendering (formatting) function defined for the column.
7615 * @param {Number} col The column index.
7616 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7618 getRenderer : function(col){
7619 if(!this.config[col].renderer){
7620 return Roo.grid.ColumnModel.defaultRenderer;
7622 return this.config[col].renderer;
7626 * Sets the rendering (formatting) function for a column.
7627 * @param {Number} col The column index
7628 * @param {Function} fn The function to use to process the cell's raw data
7629 * to return HTML markup for the grid view. The render function is called with
7630 * the following parameters:<ul>
7631 * <li>Data value.</li>
7632 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7633 * <li>css A CSS style string to apply to the table cell.</li>
7634 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7635 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7636 * <li>Row index</li>
7637 * <li>Column index</li>
7638 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7640 setRenderer : function(col, fn){
7641 this.config[col].renderer = fn;
7645 * Returns the width for the specified column.
7646 * @param {Number} col The column index
7649 getColumnWidth : function(col){
7650 return this.config[col].width * 1 || this.defaultWidth;
7654 * Sets the width for a column.
7655 * @param {Number} col The column index
7656 * @param {Number} width The new width
7658 setColumnWidth : function(col, width, suppressEvent){
7659 this.config[col].width = width;
7660 this.totalWidth = null;
7662 this.fireEvent("widthchange", this, col, width);
7667 * Returns the total width of all columns.
7668 * @param {Boolean} includeHidden True to include hidden column widths
7671 getTotalWidth : function(includeHidden){
7672 if(!this.totalWidth){
7673 this.totalWidth = 0;
7674 for(var i = 0, len = this.config.length; i < len; i++){
7675 if(includeHidden || !this.isHidden(i)){
7676 this.totalWidth += this.getColumnWidth(i);
7680 return this.totalWidth;
7684 * Returns the header for the specified column.
7685 * @param {Number} col The column index
7688 getColumnHeader : function(col){
7689 return this.config[col].header;
7693 * Sets the header for a column.
7694 * @param {Number} col The column index
7695 * @param {String} header The new header
7697 setColumnHeader : function(col, header){
7698 this.config[col].header = header;
7699 this.fireEvent("headerchange", this, col, header);
7703 * Returns the tooltip for the specified column.
7704 * @param {Number} col The column index
7707 getColumnTooltip : function(col){
7708 return this.config[col].tooltip;
7711 * Sets the tooltip for a column.
7712 * @param {Number} col The column index
7713 * @param {String} tooltip The new tooltip
7715 setColumnTooltip : function(col, tooltip){
7716 this.config[col].tooltip = tooltip;
7720 * Returns the dataIndex for the specified column.
7721 * @param {Number} col The column index
7724 getDataIndex : function(col){
7725 return this.config[col].dataIndex;
7729 * Sets the dataIndex for a column.
7730 * @param {Number} col The column index
7731 * @param {Number} dataIndex The new dataIndex
7733 setDataIndex : function(col, dataIndex){
7734 this.config[col].dataIndex = dataIndex;
7740 * Returns true if the cell is editable.
7741 * @param {Number} colIndex The column index
7742 * @param {Number} rowIndex The row index - this is nto actually used..?
7745 isCellEditable : function(colIndex, rowIndex){
7746 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7750 * Returns the editor defined for the cell/column.
7751 * return false or null to disable editing.
7752 * @param {Number} colIndex The column index
7753 * @param {Number} rowIndex The row index
7756 getCellEditor : function(colIndex, rowIndex){
7757 return this.config[colIndex].editor;
7761 * Sets if a column is editable.
7762 * @param {Number} col The column index
7763 * @param {Boolean} editable True if the column is editable
7765 setEditable : function(col, editable){
7766 this.config[col].editable = editable;
7771 * Returns true if the column is hidden.
7772 * @param {Number} colIndex The column index
7775 isHidden : function(colIndex){
7776 return this.config[colIndex].hidden;
7781 * Returns true if the column width cannot be changed
7783 isFixed : function(colIndex){
7784 return this.config[colIndex].fixed;
7788 * Returns true if the column can be resized
7791 isResizable : function(colIndex){
7792 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7795 * Sets if a column is hidden.
7796 * @param {Number} colIndex The column index
7797 * @param {Boolean} hidden True if the column is hidden
7799 setHidden : function(colIndex, hidden){
7800 this.config[colIndex].hidden = hidden;
7801 this.totalWidth = null;
7802 this.fireEvent("hiddenchange", this, colIndex, hidden);
7806 * Sets the editor for a column.
7807 * @param {Number} col The column index
7808 * @param {Object} editor The editor object
7810 setEditor : function(col, editor){
7811 this.config[col].editor = editor;
7814 * Add a column (experimental...) - defaults to adding to the end..
7815 * @param {Object} config
7817 addColumn : function(c)
7820 var i = this.config.length;
7823 if(typeof c.dataIndex == "undefined"){
7826 if(typeof c.renderer == "string"){
7827 c.renderer = Roo.util.Format[c.renderer];
7829 if(typeof c.id == "undefined"){
7832 if(c.editor && c.editor.xtype){
7833 c.editor = Roo.factory(c.editor, Roo.grid);
7835 if(c.editor && c.editor.isFormField){
7836 c.editor = new Roo.grid.GridEditor(c.editor);
7838 this.lookup[c.id] = c;
7843 Roo.grid.ColumnModel.defaultRenderer = function(value)
7845 if(typeof value == "object") {
7848 if(typeof value == "string" && value.length < 1){
7852 return String.format("{0}", value);
7855 // Alias for backwards compatibility
7856 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7859 * Ext JS Library 1.1.1
7860 * Copyright(c) 2006-2007, Ext JS, LLC.
7862 * Originally Released Under LGPL - original licence link has changed is not relivant.
7865 * <script type="text/javascript">
7869 * @class Roo.LoadMask
7870 * A simple utility class for generically masking elements while loading data. If the element being masked has
7871 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7872 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
7873 * element's UpdateManager load indicator and will be destroyed after the initial load.
7875 * Create a new LoadMask
7876 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7877 * @param {Object} config The config object
7879 Roo.LoadMask = function(el, config){
7880 this.el = Roo.get(el);
7881 Roo.apply(this, config);
7883 this.store.on('beforeload', this.onBeforeLoad, this);
7884 this.store.on('load', this.onLoad, this);
7885 this.store.on('loadexception', this.onLoadException, this);
7886 this.removeMask = false;
7888 var um = this.el.getUpdateManager();
7889 um.showLoadIndicator = false; // disable the default indicator
7890 um.on('beforeupdate', this.onBeforeLoad, this);
7891 um.on('update', this.onLoad, this);
7892 um.on('failure', this.onLoad, this);
7893 this.removeMask = true;
7897 Roo.LoadMask.prototype = {
7899 * @cfg {Boolean} removeMask
7900 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7901 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
7905 * The text to display in a centered loading message box (defaults to 'Loading...')
7909 * @cfg {String} msgCls
7910 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7912 msgCls : 'x-mask-loading',
7915 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7921 * Disables the mask to prevent it from being displayed
7923 disable : function(){
7924 this.disabled = true;
7928 * Enables the mask so that it can be displayed
7930 enable : function(){
7931 this.disabled = false;
7934 onLoadException : function()
7938 if (typeof(arguments[3]) != 'undefined') {
7939 Roo.MessageBox.alert("Error loading",arguments[3]);
7943 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7944 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7951 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7956 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7960 onBeforeLoad : function(){
7962 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7967 destroy : function(){
7969 this.store.un('beforeload', this.onBeforeLoad, this);
7970 this.store.un('load', this.onLoad, this);
7971 this.store.un('loadexception', this.onLoadException, this);
7973 var um = this.el.getUpdateManager();
7974 um.un('beforeupdate', this.onBeforeLoad, this);
7975 um.un('update', this.onLoad, this);
7976 um.un('failure', this.onLoad, this);
7987 * @class Roo.bootstrap.Table
7988 * @extends Roo.bootstrap.Component
7989 * Bootstrap Table class
7990 * @cfg {String} cls table class
7991 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7992 * @cfg {String} bgcolor Specifies the background color for a table
7993 * @cfg {Number} border Specifies whether the table cells should have borders or not
7994 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7995 * @cfg {Number} cellspacing Specifies the space between cells
7996 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7997 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7998 * @cfg {String} sortable Specifies that the table should be sortable
7999 * @cfg {String} summary Specifies a summary of the content of a table
8000 * @cfg {Number} width Specifies the width of a table
8001 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
8003 * @cfg {boolean} striped Should the rows be alternative striped
8004 * @cfg {boolean} bordered Add borders to the table
8005 * @cfg {boolean} hover Add hover highlighting
8006 * @cfg {boolean} condensed Format condensed
8007 * @cfg {boolean} responsive Format condensed
8008 * @cfg {Boolean} loadMask (true|false) default false
8009 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
8010 * @cfg {Boolean} headerShow (true|false) generate thead, default true
8011 * @cfg {Boolean} rowSelection (true|false) default false
8012 * @cfg {Boolean} cellSelection (true|false) default false
8013 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
8014 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
8015 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
8016 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
8020 * Create a new Table
8021 * @param {Object} config The config object
8024 Roo.bootstrap.Table = function(config){
8025 Roo.bootstrap.Table.superclass.constructor.call(this, config);
8030 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
8031 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
8032 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
8033 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
8035 this.sm = this.sm || {xtype: 'RowSelectionModel'};
8037 this.sm.grid = this;
8038 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
8039 this.sm = this.selModel;
8040 this.sm.xmodule = this.xmodule || false;
8043 if (this.cm && typeof(this.cm.config) == 'undefined') {
8044 this.colModel = new Roo.grid.ColumnModel(this.cm);
8045 this.cm = this.colModel;
8046 this.cm.xmodule = this.xmodule || false;
8049 this.store= Roo.factory(this.store, Roo.data);
8050 this.ds = this.store;
8051 this.ds.xmodule = this.xmodule || false;
8054 if (this.footer && this.store) {
8055 this.footer.dataSource = this.ds;
8056 this.footer = Roo.factory(this.footer);
8063 * Fires when a cell is clicked
8064 * @param {Roo.bootstrap.Table} this
8065 * @param {Roo.Element} el
8066 * @param {Number} rowIndex
8067 * @param {Number} columnIndex
8068 * @param {Roo.EventObject} e
8072 * @event celldblclick
8073 * Fires when a cell is double clicked
8074 * @param {Roo.bootstrap.Table} this
8075 * @param {Roo.Element} el
8076 * @param {Number} rowIndex
8077 * @param {Number} columnIndex
8078 * @param {Roo.EventObject} e
8080 "celldblclick" : true,
8083 * Fires when a row is clicked
8084 * @param {Roo.bootstrap.Table} this
8085 * @param {Roo.Element} el
8086 * @param {Number} rowIndex
8087 * @param {Roo.EventObject} e
8091 * @event rowdblclick
8092 * Fires when a row is double clicked
8093 * @param {Roo.bootstrap.Table} this
8094 * @param {Roo.Element} el
8095 * @param {Number} rowIndex
8096 * @param {Roo.EventObject} e
8098 "rowdblclick" : true,
8101 * Fires when a mouseover occur
8102 * @param {Roo.bootstrap.Table} this
8103 * @param {Roo.Element} el
8104 * @param {Number} rowIndex
8105 * @param {Number} columnIndex
8106 * @param {Roo.EventObject} e
8111 * Fires when a mouseout occur
8112 * @param {Roo.bootstrap.Table} this
8113 * @param {Roo.Element} el
8114 * @param {Number} rowIndex
8115 * @param {Number} columnIndex
8116 * @param {Roo.EventObject} e
8121 * Fires when a row is rendered, so you can change add a style to it.
8122 * @param {Roo.bootstrap.Table} this
8123 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
8127 * @event rowsrendered
8128 * Fires when all the rows have been rendered
8129 * @param {Roo.bootstrap.Table} this
8131 'rowsrendered' : true,
8133 * @event contextmenu
8134 * The raw contextmenu event for the entire grid.
8135 * @param {Roo.EventObject} e
8137 "contextmenu" : true,
8139 * @event rowcontextmenu
8140 * Fires when a row is right clicked
8141 * @param {Roo.bootstrap.Table} this
8142 * @param {Number} rowIndex
8143 * @param {Roo.EventObject} e
8145 "rowcontextmenu" : true,
8147 * @event cellcontextmenu
8148 * Fires when a cell is right clicked
8149 * @param {Roo.bootstrap.Table} this
8150 * @param {Number} rowIndex
8151 * @param {Number} cellIndex
8152 * @param {Roo.EventObject} e
8154 "cellcontextmenu" : true,
8156 * @event headercontextmenu
8157 * Fires when a header is right clicked
8158 * @param {Roo.bootstrap.Table} this
8159 * @param {Number} columnIndex
8160 * @param {Roo.EventObject} e
8162 "headercontextmenu" : true
8166 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
8192 rowSelection : false,
8193 cellSelection : false,
8196 // Roo.Element - the tbody
8198 // Roo.Element - thead element
8201 container: false, // used by gridpanel...
8207 auto_hide_footer : false,
8209 getAutoCreate : function()
8211 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8218 if (this.scrollBody) {
8219 cfg.cls += ' table-body-fixed';
8222 cfg.cls += ' table-striped';
8226 cfg.cls += ' table-hover';
8228 if (this.bordered) {
8229 cfg.cls += ' table-bordered';
8231 if (this.condensed) {
8232 cfg.cls += ' table-condensed';
8234 if (this.responsive) {
8235 cfg.cls += ' table-responsive';
8239 cfg.cls+= ' ' +this.cls;
8242 // this lot should be simplifed...
8255 ].forEach(function(k) {
8263 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8266 if(this.store || this.cm){
8267 if(this.headerShow){
8268 cfg.cn.push(this.renderHeader());
8271 cfg.cn.push(this.renderBody());
8273 if(this.footerShow){
8274 cfg.cn.push(this.renderFooter());
8276 // where does this come from?
8277 //cfg.cls+= ' TableGrid';
8280 return { cn : [ cfg ] };
8283 initEvents : function()
8285 if(!this.store || !this.cm){
8288 if (this.selModel) {
8289 this.selModel.initEvents();
8293 //Roo.log('initEvents with ds!!!!');
8295 this.mainBody = this.el.select('tbody', true).first();
8296 this.mainHead = this.el.select('thead', true).first();
8297 this.mainFoot = this.el.select('tfoot', true).first();
8302 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8303 e.on('click', this.sort, this);
8306 this.mainBody.on("click", this.onClick, this);
8307 this.mainBody.on("dblclick", this.onDblClick, this);
8309 // why is this done????? = it breaks dialogs??
8310 //this.parent().el.setStyle('position', 'relative');
8314 this.footer.parentId = this.id;
8315 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8318 this.el.select('tfoot tr td').first().addClass('hide');
8323 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8326 this.store.on('load', this.onLoad, this);
8327 this.store.on('beforeload', this.onBeforeLoad, this);
8328 this.store.on('update', this.onUpdate, this);
8329 this.store.on('add', this.onAdd, this);
8330 this.store.on("clear", this.clear, this);
8332 this.el.on("contextmenu", this.onContextMenu, this);
8334 this.mainBody.on('scroll', this.onBodyScroll, this);
8336 this.cm.on("headerchange", this.onHeaderChange, this);
8338 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8342 onContextMenu : function(e, t)
8344 this.processEvent("contextmenu", e);
8347 processEvent : function(name, e)
8349 if (name != 'touchstart' ) {
8350 this.fireEvent(name, e);
8353 var t = e.getTarget();
8355 var cell = Roo.get(t);
8361 if(cell.findParent('tfoot', false, true)){
8365 if(cell.findParent('thead', false, true)){
8367 if(e.getTarget().nodeName.toLowerCase() != 'th'){
8368 cell = Roo.get(t).findParent('th', false, true);
8370 Roo.log("failed to find th in thead?");
8371 Roo.log(e.getTarget());
8376 var cellIndex = cell.dom.cellIndex;
8378 var ename = name == 'touchstart' ? 'click' : name;
8379 this.fireEvent("header" + ename, this, cellIndex, e);
8384 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8385 cell = Roo.get(t).findParent('td', false, true);
8387 Roo.log("failed to find th in tbody?");
8388 Roo.log(e.getTarget());
8393 var row = cell.findParent('tr', false, true);
8394 var cellIndex = cell.dom.cellIndex;
8395 var rowIndex = row.dom.rowIndex - 1;
8399 this.fireEvent("row" + name, this, rowIndex, e);
8403 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8409 onMouseover : function(e, el)
8411 var cell = Roo.get(el);
8417 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8418 cell = cell.findParent('td', false, true);
8421 var row = cell.findParent('tr', false, true);
8422 var cellIndex = cell.dom.cellIndex;
8423 var rowIndex = row.dom.rowIndex - 1; // start from 0
8425 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8429 onMouseout : function(e, el)
8431 var cell = Roo.get(el);
8437 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8438 cell = cell.findParent('td', false, true);
8441 var row = cell.findParent('tr', false, true);
8442 var cellIndex = cell.dom.cellIndex;
8443 var rowIndex = row.dom.rowIndex - 1; // start from 0
8445 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8449 onClick : function(e, el)
8451 var cell = Roo.get(el);
8453 if(!cell || (!this.cellSelection && !this.rowSelection)){
8457 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8458 cell = cell.findParent('td', false, true);
8461 if(!cell || typeof(cell) == 'undefined'){
8465 var row = cell.findParent('tr', false, true);
8467 if(!row || typeof(row) == 'undefined'){
8471 var cellIndex = cell.dom.cellIndex;
8472 var rowIndex = this.getRowIndex(row);
8474 // why??? - should these not be based on SelectionModel?
8475 //if(this.cellSelection){
8476 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8479 //if(this.rowSelection){
8480 this.fireEvent('rowclick', this, row, rowIndex, e);
8485 onDblClick : function(e,el)
8487 var cell = Roo.get(el);
8489 if(!cell || (!this.cellSelection && !this.rowSelection)){
8493 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8494 cell = cell.findParent('td', false, true);
8497 if(!cell || typeof(cell) == 'undefined'){
8501 var row = cell.findParent('tr', false, true);
8503 if(!row || typeof(row) == 'undefined'){
8507 var cellIndex = cell.dom.cellIndex;
8508 var rowIndex = this.getRowIndex(row);
8510 if(this.cellSelection){
8511 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8514 if(this.rowSelection){
8515 this.fireEvent('rowdblclick', this, row, rowIndex, e);
8519 sort : function(e,el)
8521 var col = Roo.get(el);
8523 if(!col.hasClass('sortable')){
8527 var sort = col.attr('sort');
8530 if(col.select('i', true).first().hasClass('fa-arrow-up')){
8534 this.store.sortInfo = {field : sort, direction : dir};
8537 Roo.log("calling footer first");
8538 this.footer.onClick('first');
8541 this.store.load({ params : { start : 0 } });
8545 renderHeader : function()
8553 this.totalWidth = 0;
8555 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8557 var config = cm.config[i];
8561 cls : 'x-hcol-' + i,
8564 html: cm.getColumnHeader(i)
8567 var tooltip = cm.getColumnTooltip(i);
8569 c.tooltip = tooltip;
8575 if(typeof(config.sortable) != 'undefined' && config.sortable){
8577 c.html = '<i class="fa"></i>' + c.html;
8580 // could use BS4 hidden-..-down
8582 if(typeof(config.lgHeader) != 'undefined'){
8583 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8586 if(typeof(config.mdHeader) != 'undefined'){
8587 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8590 if(typeof(config.smHeader) != 'undefined'){
8591 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8594 if(typeof(config.xsHeader) != 'undefined'){
8595 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8602 if(typeof(config.tooltip) != 'undefined'){
8603 c.tooltip = config.tooltip;
8606 if(typeof(config.colspan) != 'undefined'){
8607 c.colspan = config.colspan;
8610 if(typeof(config.hidden) != 'undefined' && config.hidden){
8611 c.style += ' display:none;';
8614 if(typeof(config.dataIndex) != 'undefined'){
8615 c.sort = config.dataIndex;
8620 if(typeof(config.align) != 'undefined' && config.align.length){
8621 c.style += ' text-align:' + config.align + ';';
8624 if(typeof(config.width) != 'undefined'){
8625 c.style += ' width:' + config.width + 'px;';
8626 this.totalWidth += config.width;
8628 this.totalWidth += 100; // assume minimum of 100 per column?
8631 if(typeof(config.cls) != 'undefined'){
8632 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8635 ['xs','sm','md','lg'].map(function(size){
8637 if(typeof(config[size]) == 'undefined'){
8641 if (!config[size]) { // 0 = hidden
8642 // BS 4 '0' is treated as hide that column and below.
8643 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8647 c.cls += ' col-' + size + '-' + config[size] + (
8648 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8660 renderBody : function()
8670 colspan : this.cm.getColumnCount()
8680 renderFooter : function()
8690 colspan : this.cm.getColumnCount()
8704 // Roo.log('ds onload');
8709 var ds = this.store;
8711 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8712 e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
8713 if (_this.store.sortInfo) {
8715 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8716 e.select('i', true).addClass(['fa-arrow-up']);
8719 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8720 e.select('i', true).addClass(['fa-arrow-down']);
8725 var tbody = this.mainBody;
8727 if(ds.getCount() > 0){
8728 ds.data.each(function(d,rowIndex){
8729 var row = this.renderRow(cm, ds, rowIndex);
8731 tbody.createChild(row);
8735 if(row.cellObjects.length){
8736 Roo.each(row.cellObjects, function(r){
8737 _this.renderCellObject(r);
8744 var tfoot = this.el.select('tfoot', true).first();
8746 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8748 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8750 var total = this.ds.getTotalCount();
8752 if(this.footer.pageSize < total){
8753 this.mainFoot.show();
8757 Roo.each(this.el.select('tbody td', true).elements, function(e){
8758 e.on('mouseover', _this.onMouseover, _this);
8761 Roo.each(this.el.select('tbody td', true).elements, function(e){
8762 e.on('mouseout', _this.onMouseout, _this);
8764 this.fireEvent('rowsrendered', this);
8770 onUpdate : function(ds,record)
8772 this.refreshRow(record);
8776 onRemove : function(ds, record, index, isUpdate){
8777 if(isUpdate !== true){
8778 this.fireEvent("beforerowremoved", this, index, record);
8780 var bt = this.mainBody.dom;
8782 var rows = this.el.select('tbody > tr', true).elements;
8784 if(typeof(rows[index]) != 'undefined'){
8785 bt.removeChild(rows[index].dom);
8788 // if(bt.rows[index]){
8789 // bt.removeChild(bt.rows[index]);
8792 if(isUpdate !== true){
8793 //this.stripeRows(index);
8794 //this.syncRowHeights(index, index);
8796 this.fireEvent("rowremoved", this, index, record);
8800 onAdd : function(ds, records, rowIndex)
8802 //Roo.log('on Add called');
8803 // - note this does not handle multiple adding very well..
8804 var bt = this.mainBody.dom;
8805 for (var i =0 ; i < records.length;i++) {
8806 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8807 //Roo.log(records[i]);
8808 //Roo.log(this.store.getAt(rowIndex+i));
8809 this.insertRow(this.store, rowIndex + i, false);
8816 refreshRow : function(record){
8817 var ds = this.store, index;
8818 if(typeof record == 'number'){
8820 record = ds.getAt(index);
8822 index = ds.indexOf(record);
8824 return; // should not happen - but seems to
8827 this.insertRow(ds, index, true);
8829 this.onRemove(ds, record, index+1, true);
8831 //this.syncRowHeights(index, index);
8833 this.fireEvent("rowupdated", this, index, record);
8836 insertRow : function(dm, rowIndex, isUpdate){
8839 this.fireEvent("beforerowsinserted", this, rowIndex);
8841 //var s = this.getScrollState();
8842 var row = this.renderRow(this.cm, this.store, rowIndex);
8843 // insert before rowIndex..
8844 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8848 if(row.cellObjects.length){
8849 Roo.each(row.cellObjects, function(r){
8850 _this.renderCellObject(r);
8855 this.fireEvent("rowsinserted", this, rowIndex);
8856 //this.syncRowHeights(firstRow, lastRow);
8857 //this.stripeRows(firstRow);
8864 getRowDom : function(rowIndex)
8866 var rows = this.el.select('tbody > tr', true).elements;
8868 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8871 // returns the object tree for a tr..
8874 renderRow : function(cm, ds, rowIndex)
8876 var d = ds.getAt(rowIndex);
8880 cls : 'x-row-' + rowIndex,
8884 var cellObjects = [];
8886 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8887 var config = cm.config[i];
8889 var renderer = cm.getRenderer(i);
8893 if(typeof(renderer) !== 'undefined'){
8894 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8896 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8897 // and are rendered into the cells after the row is rendered - using the id for the element.
8899 if(typeof(value) === 'object'){
8909 rowIndex : rowIndex,
8914 this.fireEvent('rowclass', this, rowcfg);
8918 // this might end up displaying HTML?
8919 // this is too messy... - better to only do it on columsn you know are going to be too long
8920 //tooltip : (typeof(value) === 'object') ? '' : value,
8921 cls : rowcfg.rowClass + ' x-col-' + i,
8923 html: (typeof(value) === 'object') ? '' : value
8930 if(typeof(config.colspan) != 'undefined'){
8931 td.colspan = config.colspan;
8934 if(typeof(config.hidden) != 'undefined' && config.hidden){
8935 td.style += ' display:none;';
8938 if(typeof(config.align) != 'undefined' && config.align.length){
8939 td.style += ' text-align:' + config.align + ';';
8941 if(typeof(config.valign) != 'undefined' && config.valign.length){
8942 td.style += ' vertical-align:' + config.valign + ';';
8945 if(typeof(config.width) != 'undefined'){
8946 td.style += ' width:' + config.width + 'px;';
8949 if(typeof(config.cursor) != 'undefined'){
8950 td.style += ' cursor:' + config.cursor + ';';
8953 if(typeof(config.cls) != 'undefined'){
8954 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8957 ['xs','sm','md','lg'].map(function(size){
8959 if(typeof(config[size]) == 'undefined'){
8965 if (!config[size]) { // 0 = hidden
8966 // BS 4 '0' is treated as hide that column and below.
8967 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8971 td.cls += ' col-' + size + '-' + config[size] + (
8972 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8982 row.cellObjects = cellObjects;
8990 onBeforeLoad : function()
8999 this.el.select('tbody', true).first().dom.innerHTML = '';
9002 * Show or hide a row.
9003 * @param {Number} rowIndex to show or hide
9004 * @param {Boolean} state hide
9006 setRowVisibility : function(rowIndex, state)
9008 var bt = this.mainBody.dom;
9010 var rows = this.el.select('tbody > tr', true).elements;
9012 if(typeof(rows[rowIndex]) == 'undefined'){
9015 rows[rowIndex].dom.style.display = state ? '' : 'none';
9019 getSelectionModel : function(){
9021 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
9023 return this.selModel;
9026 * Render the Roo.bootstrap object from renderder
9028 renderCellObject : function(r)
9032 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
9034 var t = r.cfg.render(r.container);
9037 Roo.each(r.cfg.cn, function(c){
9039 container: t.getChildContainer(),
9042 _this.renderCellObject(child);
9047 getRowIndex : function(row)
9051 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
9062 * Returns the grid's underlying element = used by panel.Grid
9063 * @return {Element} The element
9065 getGridEl : function(){
9069 * Forces a resize - used by panel.Grid
9070 * @return {Element} The element
9072 autoSize : function()
9074 //var ctr = Roo.get(this.container.dom.parentElement);
9075 var ctr = Roo.get(this.el.dom);
9077 var thd = this.getGridEl().select('thead',true).first();
9078 var tbd = this.getGridEl().select('tbody', true).first();
9079 var tfd = this.getGridEl().select('tfoot', true).first();
9081 var cw = ctr.getWidth();
9082 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
9086 tbd.setWidth(ctr.getWidth());
9087 // if the body has a max height - and then scrolls - we should perhaps set up the height here
9088 // this needs fixing for various usage - currently only hydra job advers I think..
9090 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
9092 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
9095 cw = Math.max(cw, this.totalWidth);
9096 this.getGridEl().select('tbody tr',true).setWidth(cw);
9098 // resize 'expandable coloumn?
9100 return; // we doe not have a view in this design..
9103 onBodyScroll: function()
9105 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
9107 this.mainHead.setStyle({
9108 'position' : 'relative',
9109 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
9115 var scrollHeight = this.mainBody.dom.scrollHeight;
9117 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
9119 var height = this.mainBody.getHeight();
9121 if(scrollHeight - height == scrollTop) {
9123 var total = this.ds.getTotalCount();
9125 if(this.footer.cursor + this.footer.pageSize < total){
9127 this.footer.ds.load({
9129 start : this.footer.cursor + this.footer.pageSize,
9130 limit : this.footer.pageSize
9140 onHeaderChange : function()
9142 var header = this.renderHeader();
9143 var table = this.el.select('table', true).first();
9145 this.mainHead.remove();
9146 this.mainHead = table.createChild(header, this.mainBody, false);
9148 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9149 e.on('click', this.sort, this);
9155 onHiddenChange : function(colModel, colIndex, hidden)
9157 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
9158 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
9160 this.CSS.updateRule(thSelector, "display", "");
9161 this.CSS.updateRule(tdSelector, "display", "");
9164 this.CSS.updateRule(thSelector, "display", "none");
9165 this.CSS.updateRule(tdSelector, "display", "none");
9168 this.onHeaderChange();
9172 setColumnWidth: function(col_index, width)
9174 // width = "md-2 xs-2..."
9175 if(!this.colModel.config[col_index]) {
9179 var w = width.split(" ");
9181 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
9183 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
9186 for(var j = 0; j < w.length; j++) {
9192 var size_cls = w[j].split("-");
9194 if(!Number.isInteger(size_cls[1] * 1)) {
9198 if(!this.colModel.config[col_index][size_cls[0]]) {
9202 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9206 h_row[0].classList.replace(
9207 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9208 "col-"+size_cls[0]+"-"+size_cls[1]
9211 for(var i = 0; i < rows.length; i++) {
9213 var size_cls = w[j].split("-");
9215 if(!Number.isInteger(size_cls[1] * 1)) {
9219 if(!this.colModel.config[col_index][size_cls[0]]) {
9223 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9227 rows[i].classList.replace(
9228 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9229 "col-"+size_cls[0]+"-"+size_cls[1]
9233 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
9248 * @class Roo.bootstrap.TableCell
9249 * @extends Roo.bootstrap.Component
9250 * Bootstrap TableCell class
9251 * @cfg {String} html cell contain text
9252 * @cfg {String} cls cell class
9253 * @cfg {String} tag cell tag (td|th) default td
9254 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
9255 * @cfg {String} align Aligns the content in a cell
9256 * @cfg {String} axis Categorizes cells
9257 * @cfg {String} bgcolor Specifies the background color of a cell
9258 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9259 * @cfg {Number} colspan Specifies the number of columns a cell should span
9260 * @cfg {String} headers Specifies one or more header cells a cell is related to
9261 * @cfg {Number} height Sets the height of a cell
9262 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
9263 * @cfg {Number} rowspan Sets the number of rows a cell should span
9264 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
9265 * @cfg {String} valign Vertical aligns the content in a cell
9266 * @cfg {Number} width Specifies the width of a cell
9269 * Create a new TableCell
9270 * @param {Object} config The config object
9273 Roo.bootstrap.TableCell = function(config){
9274 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
9277 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
9297 getAutoCreate : function(){
9298 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
9318 cfg.align=this.align
9324 cfg.bgcolor=this.bgcolor
9327 cfg.charoff=this.charoff
9330 cfg.colspan=this.colspan
9333 cfg.headers=this.headers
9336 cfg.height=this.height
9339 cfg.nowrap=this.nowrap
9342 cfg.rowspan=this.rowspan
9345 cfg.scope=this.scope
9348 cfg.valign=this.valign
9351 cfg.width=this.width
9370 * @class Roo.bootstrap.TableRow
9371 * @extends Roo.bootstrap.Component
9372 * Bootstrap TableRow class
9373 * @cfg {String} cls row class
9374 * @cfg {String} align Aligns the content in a table row
9375 * @cfg {String} bgcolor Specifies a background color for a table row
9376 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9377 * @cfg {String} valign Vertical aligns the content in a table row
9380 * Create a new TableRow
9381 * @param {Object} config The config object
9384 Roo.bootstrap.TableRow = function(config){
9385 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9388 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
9396 getAutoCreate : function(){
9397 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9407 cfg.align = this.align;
9410 cfg.bgcolor = this.bgcolor;
9413 cfg.charoff = this.charoff;
9416 cfg.valign = this.valign;
9434 * @class Roo.bootstrap.TableBody
9435 * @extends Roo.bootstrap.Component
9436 * Bootstrap TableBody class
9437 * @cfg {String} cls element class
9438 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9439 * @cfg {String} align Aligns the content inside the element
9440 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9441 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9444 * Create a new TableBody
9445 * @param {Object} config The config object
9448 Roo.bootstrap.TableBody = function(config){
9449 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9452 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
9460 getAutoCreate : function(){
9461 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9475 cfg.align = this.align;
9478 cfg.charoff = this.charoff;
9481 cfg.valign = this.valign;
9488 // initEvents : function()
9495 // this.store = Roo.factory(this.store, Roo.data);
9496 // this.store.on('load', this.onLoad, this);
9498 // this.store.load();
9502 // onLoad: function ()
9504 // this.fireEvent('load', this);
9514 * Ext JS Library 1.1.1
9515 * Copyright(c) 2006-2007, Ext JS, LLC.
9517 * Originally Released Under LGPL - original licence link has changed is not relivant.
9520 * <script type="text/javascript">
9523 // as we use this in bootstrap.
9524 Roo.namespace('Roo.form');
9526 * @class Roo.form.Action
9527 * Internal Class used to handle form actions
9529 * @param {Roo.form.BasicForm} el The form element or its id
9530 * @param {Object} config Configuration options
9535 // define the action interface
9536 Roo.form.Action = function(form, options){
9538 this.options = options || {};
9541 * Client Validation Failed
9544 Roo.form.Action.CLIENT_INVALID = 'client';
9546 * Server Validation Failed
9549 Roo.form.Action.SERVER_INVALID = 'server';
9551 * Connect to Server Failed
9554 Roo.form.Action.CONNECT_FAILURE = 'connect';
9556 * Reading Data from Server Failed
9559 Roo.form.Action.LOAD_FAILURE = 'load';
9561 Roo.form.Action.prototype = {
9563 failureType : undefined,
9564 response : undefined,
9568 run : function(options){
9573 success : function(response){
9578 handleResponse : function(response){
9582 // default connection failure
9583 failure : function(response){
9585 this.response = response;
9586 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9587 this.form.afterAction(this, false);
9590 processResponse : function(response){
9591 this.response = response;
9592 if(!response.responseText){
9595 this.result = this.handleResponse(response);
9599 // utility functions used internally
9600 getUrl : function(appendParams){
9601 var url = this.options.url || this.form.url || this.form.el.dom.action;
9603 var p = this.getParams();
9605 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9611 getMethod : function(){
9612 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9615 getParams : function(){
9616 var bp = this.form.baseParams;
9617 var p = this.options.params;
9619 if(typeof p == "object"){
9620 p = Roo.urlEncode(Roo.applyIf(p, bp));
9621 }else if(typeof p == 'string' && bp){
9622 p += '&' + Roo.urlEncode(bp);
9625 p = Roo.urlEncode(bp);
9630 createCallback : function(){
9632 success: this.success,
9633 failure: this.failure,
9635 timeout: (this.form.timeout*1000),
9636 upload: this.form.fileUpload ? this.success : undefined
9641 Roo.form.Action.Submit = function(form, options){
9642 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9645 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9648 haveProgress : false,
9649 uploadComplete : false,
9651 // uploadProgress indicator.
9652 uploadProgress : function()
9654 if (!this.form.progressUrl) {
9658 if (!this.haveProgress) {
9659 Roo.MessageBox.progress("Uploading", "Uploading");
9661 if (this.uploadComplete) {
9662 Roo.MessageBox.hide();
9666 this.haveProgress = true;
9668 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9670 var c = new Roo.data.Connection();
9672 url : this.form.progressUrl,
9677 success : function(req){
9678 //console.log(data);
9682 rdata = Roo.decode(req.responseText)
9684 Roo.log("Invalid data from server..");
9688 if (!rdata || !rdata.success) {
9690 Roo.MessageBox.alert(Roo.encode(rdata));
9693 var data = rdata.data;
9695 if (this.uploadComplete) {
9696 Roo.MessageBox.hide();
9701 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9702 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9705 this.uploadProgress.defer(2000,this);
9708 failure: function(data) {
9709 Roo.log('progress url failed ');
9720 // run get Values on the form, so it syncs any secondary forms.
9721 this.form.getValues();
9723 var o = this.options;
9724 var method = this.getMethod();
9725 var isPost = method == 'POST';
9726 if(o.clientValidation === false || this.form.isValid()){
9728 if (this.form.progressUrl) {
9729 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9730 (new Date() * 1) + '' + Math.random());
9735 Roo.Ajax.request(Roo.apply(this.createCallback(), {
9736 form:this.form.el.dom,
9737 url:this.getUrl(!isPost),
9739 params:isPost ? this.getParams() : null,
9740 isUpload: this.form.fileUpload,
9741 formData : this.form.formData
9744 this.uploadProgress();
9746 }else if (o.clientValidation !== false){ // client validation failed
9747 this.failureType = Roo.form.Action.CLIENT_INVALID;
9748 this.form.afterAction(this, false);
9752 success : function(response)
9754 this.uploadComplete= true;
9755 if (this.haveProgress) {
9756 Roo.MessageBox.hide();
9760 var result = this.processResponse(response);
9761 if(result === true || result.success){
9762 this.form.afterAction(this, true);
9766 this.form.markInvalid(result.errors);
9767 this.failureType = Roo.form.Action.SERVER_INVALID;
9769 this.form.afterAction(this, false);
9771 failure : function(response)
9773 this.uploadComplete= true;
9774 if (this.haveProgress) {
9775 Roo.MessageBox.hide();
9778 this.response = response;
9779 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9780 this.form.afterAction(this, false);
9783 handleResponse : function(response){
9784 if(this.form.errorReader){
9785 var rs = this.form.errorReader.read(response);
9788 for(var i = 0, len = rs.records.length; i < len; i++) {
9789 var r = rs.records[i];
9793 if(errors.length < 1){
9797 success : rs.success,
9803 ret = Roo.decode(response.responseText);
9807 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9817 Roo.form.Action.Load = function(form, options){
9818 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9819 this.reader = this.form.reader;
9822 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9827 Roo.Ajax.request(Roo.apply(
9828 this.createCallback(), {
9829 method:this.getMethod(),
9830 url:this.getUrl(false),
9831 params:this.getParams()
9835 success : function(response){
9837 var result = this.processResponse(response);
9838 if(result === true || !result.success || !result.data){
9839 this.failureType = Roo.form.Action.LOAD_FAILURE;
9840 this.form.afterAction(this, false);
9843 this.form.clearInvalid();
9844 this.form.setValues(result.data);
9845 this.form.afterAction(this, true);
9848 handleResponse : function(response){
9849 if(this.form.reader){
9850 var rs = this.form.reader.read(response);
9851 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9853 success : rs.success,
9857 return Roo.decode(response.responseText);
9861 Roo.form.Action.ACTION_TYPES = {
9862 'load' : Roo.form.Action.Load,
9863 'submit' : Roo.form.Action.Submit
9872 * @class Roo.bootstrap.Form
9873 * @extends Roo.bootstrap.Component
9874 * Bootstrap Form class
9875 * @cfg {String} method GET | POST (default POST)
9876 * @cfg {String} labelAlign top | left (default top)
9877 * @cfg {String} align left | right - for navbars
9878 * @cfg {Boolean} loadMask load mask when submit (default true)
9883 * @param {Object} config The config object
9887 Roo.bootstrap.Form = function(config){
9889 Roo.bootstrap.Form.superclass.constructor.call(this, config);
9891 Roo.bootstrap.Form.popover.apply();
9895 * @event clientvalidation
9896 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9897 * @param {Form} this
9898 * @param {Boolean} valid true if the form has passed client-side validation
9900 clientvalidation: true,
9902 * @event beforeaction
9903 * Fires before any action is performed. Return false to cancel the action.
9904 * @param {Form} this
9905 * @param {Action} action The action to be performed
9909 * @event actionfailed
9910 * Fires when an action fails.
9911 * @param {Form} this
9912 * @param {Action} action The action that failed
9914 actionfailed : true,
9916 * @event actioncomplete
9917 * Fires when an action is completed.
9918 * @param {Form} this
9919 * @param {Action} action The action that completed
9921 actioncomplete : true
9925 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
9928 * @cfg {String} method
9929 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9934 * The URL to use for form actions if one isn't supplied in the action options.
9937 * @cfg {Boolean} fileUpload
9938 * Set to true if this form is a file upload.
9942 * @cfg {Object} baseParams
9943 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9947 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9951 * @cfg {Sting} align (left|right) for navbar forms
9956 activeAction : null,
9959 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9960 * element by passing it or its id or mask the form itself by passing in true.
9963 waitMsgTarget : false,
9968 * @cfg {Boolean} errorMask (true|false) default false
9973 * @cfg {Number} maskOffset Default 100
9978 * @cfg {Boolean} maskBody
9982 getAutoCreate : function(){
9986 method : this.method || 'POST',
9987 id : this.id || Roo.id(),
9990 if (this.parent().xtype.match(/^Nav/)) {
9991 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9995 if (this.labelAlign == 'left' ) {
9996 cfg.cls += ' form-horizontal';
10002 initEvents : function()
10004 this.el.on('submit', this.onSubmit, this);
10005 // this was added as random key presses on the form where triggering form submit.
10006 this.el.on('keypress', function(e) {
10007 if (e.getCharCode() != 13) {
10010 // we might need to allow it for textareas.. and some other items.
10011 // check e.getTarget().
10013 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
10017 Roo.log("keypress blocked");
10019 e.preventDefault();
10025 onSubmit : function(e){
10030 * Returns true if client-side validation on the form is successful.
10033 isValid : function(){
10034 var items = this.getItems();
10036 var target = false;
10038 items.each(function(f){
10044 Roo.log('invalid field: ' + f.name);
10048 if(!target && f.el.isVisible(true)){
10054 if(this.errorMask && !valid){
10055 Roo.bootstrap.Form.popover.mask(this, target);
10062 * Returns true if any fields in this form have changed since their original load.
10065 isDirty : function(){
10067 var items = this.getItems();
10068 items.each(function(f){
10078 * Performs a predefined action (submit or load) or custom actions you define on this form.
10079 * @param {String} actionName The name of the action type
10080 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
10081 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
10082 * accept other config options):
10084 Property Type Description
10085 ---------------- --------------- ----------------------------------------------------------------------------------
10086 url String The url for the action (defaults to the form's url)
10087 method String The form method to use (defaults to the form's method, or POST if not defined)
10088 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
10089 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
10090 validate the form on the client (defaults to false)
10092 * @return {BasicForm} this
10094 doAction : function(action, options){
10095 if(typeof action == 'string'){
10096 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
10098 if(this.fireEvent('beforeaction', this, action) !== false){
10099 this.beforeAction(action);
10100 action.run.defer(100, action);
10106 beforeAction : function(action){
10107 var o = action.options;
10112 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
10114 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10117 // not really supported yet.. ??
10119 //if(this.waitMsgTarget === true){
10120 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10121 //}else if(this.waitMsgTarget){
10122 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
10123 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
10125 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
10131 afterAction : function(action, success){
10132 this.activeAction = null;
10133 var o = action.options;
10138 Roo.get(document.body).unmask();
10144 //if(this.waitMsgTarget === true){
10145 // this.el.unmask();
10146 //}else if(this.waitMsgTarget){
10147 // this.waitMsgTarget.unmask();
10149 // Roo.MessageBox.updateProgress(1);
10150 // Roo.MessageBox.hide();
10157 Roo.callback(o.success, o.scope, [this, action]);
10158 this.fireEvent('actioncomplete', this, action);
10162 // failure condition..
10163 // we have a scenario where updates need confirming.
10164 // eg. if a locking scenario exists..
10165 // we look for { errors : { needs_confirm : true }} in the response.
10167 (typeof(action.result) != 'undefined') &&
10168 (typeof(action.result.errors) != 'undefined') &&
10169 (typeof(action.result.errors.needs_confirm) != 'undefined')
10172 Roo.log("not supported yet");
10175 Roo.MessageBox.confirm(
10176 "Change requires confirmation",
10177 action.result.errorMsg,
10182 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
10192 Roo.callback(o.failure, o.scope, [this, action]);
10193 // show an error message if no failed handler is set..
10194 if (!this.hasListener('actionfailed')) {
10195 Roo.log("need to add dialog support");
10197 Roo.MessageBox.alert("Error",
10198 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
10199 action.result.errorMsg :
10200 "Saving Failed, please check your entries or try again"
10205 this.fireEvent('actionfailed', this, action);
10210 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
10211 * @param {String} id The value to search for
10214 findField : function(id){
10215 var items = this.getItems();
10216 var field = items.get(id);
10218 items.each(function(f){
10219 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
10226 return field || null;
10229 * Mark fields in this form invalid in bulk.
10230 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
10231 * @return {BasicForm} this
10233 markInvalid : function(errors){
10234 if(errors instanceof Array){
10235 for(var i = 0, len = errors.length; i < len; i++){
10236 var fieldError = errors[i];
10237 var f = this.findField(fieldError.id);
10239 f.markInvalid(fieldError.msg);
10245 if(typeof errors[id] != 'function' && (field = this.findField(id))){
10246 field.markInvalid(errors[id]);
10250 //Roo.each(this.childForms || [], function (f) {
10251 // f.markInvalid(errors);
10258 * Set values for fields in this form in bulk.
10259 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
10260 * @return {BasicForm} this
10262 setValues : function(values){
10263 if(values instanceof Array){ // array of objects
10264 for(var i = 0, len = values.length; i < len; i++){
10266 var f = this.findField(v.id);
10268 f.setValue(v.value);
10269 if(this.trackResetOnLoad){
10270 f.originalValue = f.getValue();
10274 }else{ // object hash
10277 if(typeof values[id] != 'function' && (field = this.findField(id))){
10279 if (field.setFromData &&
10280 field.valueField &&
10281 field.displayField &&
10282 // combos' with local stores can
10283 // be queried via setValue()
10284 // to set their value..
10285 (field.store && !field.store.isLocal)
10289 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
10290 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
10291 field.setFromData(sd);
10293 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
10295 field.setFromData(values);
10298 field.setValue(values[id]);
10302 if(this.trackResetOnLoad){
10303 field.originalValue = field.getValue();
10309 //Roo.each(this.childForms || [], function (f) {
10310 // f.setValues(values);
10317 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
10318 * they are returned as an array.
10319 * @param {Boolean} asString
10322 getValues : function(asString){
10323 //if (this.childForms) {
10324 // copy values from the child forms
10325 // Roo.each(this.childForms, function (f) {
10326 // this.setValues(f.getValues());
10332 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10333 if(asString === true){
10336 return Roo.urlDecode(fs);
10340 * Returns the fields in this form as an object with key/value pairs.
10341 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10344 getFieldValues : function(with_hidden)
10346 var items = this.getItems();
10348 items.each(function(f){
10350 if (!f.getName()) {
10354 var v = f.getValue();
10356 if (f.inputType =='radio') {
10357 if (typeof(ret[f.getName()]) == 'undefined') {
10358 ret[f.getName()] = ''; // empty..
10361 if (!f.el.dom.checked) {
10365 v = f.el.dom.value;
10369 if(f.xtype == 'MoneyField'){
10370 ret[f.currencyName] = f.getCurrency();
10373 // not sure if this supported any more..
10374 if ((typeof(v) == 'object') && f.getRawValue) {
10375 v = f.getRawValue() ; // dates..
10377 // combo boxes where name != hiddenName...
10378 if (f.name !== false && f.name != '' && f.name != f.getName()) {
10379 ret[f.name] = f.getRawValue();
10381 ret[f.getName()] = v;
10388 * Clears all invalid messages in this form.
10389 * @return {BasicForm} this
10391 clearInvalid : function(){
10392 var items = this.getItems();
10394 items.each(function(f){
10402 * Resets this form.
10403 * @return {BasicForm} this
10405 reset : function(){
10406 var items = this.getItems();
10407 items.each(function(f){
10411 Roo.each(this.childForms || [], function (f) {
10419 getItems : function()
10421 var r=new Roo.util.MixedCollection(false, function(o){
10422 return o.id || (o.id = Roo.id());
10424 var iter = function(el) {
10431 Roo.each(el.items,function(e) {
10440 hideFields : function(items)
10442 Roo.each(items, function(i){
10444 var f = this.findField(i);
10455 showFields : function(items)
10457 Roo.each(items, function(i){
10459 var f = this.findField(i);
10472 Roo.apply(Roo.bootstrap.Form, {
10488 intervalID : false,
10494 if(this.isApplied){
10499 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10500 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10501 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10502 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10505 this.maskEl.top.enableDisplayMode("block");
10506 this.maskEl.left.enableDisplayMode("block");
10507 this.maskEl.bottom.enableDisplayMode("block");
10508 this.maskEl.right.enableDisplayMode("block");
10510 this.toolTip = new Roo.bootstrap.Tooltip({
10511 cls : 'roo-form-error-popover',
10513 'left' : ['r-l', [-2,0], 'right'],
10514 'right' : ['l-r', [2,0], 'left'],
10515 'bottom' : ['tl-bl', [0,2], 'top'],
10516 'top' : [ 'bl-tl', [0,-2], 'bottom']
10520 this.toolTip.render(Roo.get(document.body));
10522 this.toolTip.el.enableDisplayMode("block");
10524 Roo.get(document.body).on('click', function(){
10528 Roo.get(document.body).on('touchstart', function(){
10532 this.isApplied = true
10535 mask : function(form, target)
10539 this.target = target;
10541 if(!this.form.errorMask || !target.el){
10545 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10547 Roo.log(scrollable);
10549 var ot = this.target.el.calcOffsetsTo(scrollable);
10551 var scrollTo = ot[1] - this.form.maskOffset;
10553 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10555 scrollable.scrollTo('top', scrollTo);
10557 var box = this.target.el.getBox();
10559 var zIndex = Roo.bootstrap.Modal.zIndex++;
10562 this.maskEl.top.setStyle('position', 'absolute');
10563 this.maskEl.top.setStyle('z-index', zIndex);
10564 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10565 this.maskEl.top.setLeft(0);
10566 this.maskEl.top.setTop(0);
10567 this.maskEl.top.show();
10569 this.maskEl.left.setStyle('position', 'absolute');
10570 this.maskEl.left.setStyle('z-index', zIndex);
10571 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10572 this.maskEl.left.setLeft(0);
10573 this.maskEl.left.setTop(box.y - this.padding);
10574 this.maskEl.left.show();
10576 this.maskEl.bottom.setStyle('position', 'absolute');
10577 this.maskEl.bottom.setStyle('z-index', zIndex);
10578 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10579 this.maskEl.bottom.setLeft(0);
10580 this.maskEl.bottom.setTop(box.bottom + this.padding);
10581 this.maskEl.bottom.show();
10583 this.maskEl.right.setStyle('position', 'absolute');
10584 this.maskEl.right.setStyle('z-index', zIndex);
10585 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10586 this.maskEl.right.setLeft(box.right + this.padding);
10587 this.maskEl.right.setTop(box.y - this.padding);
10588 this.maskEl.right.show();
10590 this.toolTip.bindEl = this.target.el;
10592 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10594 var tip = this.target.blankText;
10596 if(this.target.getValue() !== '' ) {
10598 if (this.target.invalidText.length) {
10599 tip = this.target.invalidText;
10600 } else if (this.target.regexText.length){
10601 tip = this.target.regexText;
10605 this.toolTip.show(tip);
10607 this.intervalID = window.setInterval(function() {
10608 Roo.bootstrap.Form.popover.unmask();
10611 window.onwheel = function(){ return false;};
10613 (function(){ this.isMasked = true; }).defer(500, this);
10617 unmask : function()
10619 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10623 this.maskEl.top.setStyle('position', 'absolute');
10624 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10625 this.maskEl.top.hide();
10627 this.maskEl.left.setStyle('position', 'absolute');
10628 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10629 this.maskEl.left.hide();
10631 this.maskEl.bottom.setStyle('position', 'absolute');
10632 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10633 this.maskEl.bottom.hide();
10635 this.maskEl.right.setStyle('position', 'absolute');
10636 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10637 this.maskEl.right.hide();
10639 this.toolTip.hide();
10641 this.toolTip.el.hide();
10643 window.onwheel = function(){ return true;};
10645 if(this.intervalID){
10646 window.clearInterval(this.intervalID);
10647 this.intervalID = false;
10650 this.isMasked = false;
10660 * Ext JS Library 1.1.1
10661 * Copyright(c) 2006-2007, Ext JS, LLC.
10663 * Originally Released Under LGPL - original licence link has changed is not relivant.
10666 * <script type="text/javascript">
10669 * @class Roo.form.VTypes
10670 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10673 Roo.form.VTypes = function(){
10674 // closure these in so they are only created once.
10675 var alpha = /^[a-zA-Z_]+$/;
10676 var alphanum = /^[a-zA-Z0-9_]+$/;
10677 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10678 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10680 // All these messages and functions are configurable
10683 * The function used to validate email addresses
10684 * @param {String} value The email address
10686 'email' : function(v){
10687 return email.test(v);
10690 * The error text to display when the email validation function returns false
10693 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10695 * The keystroke filter mask to be applied on email input
10698 'emailMask' : /[a-z0-9_\.\-@]/i,
10701 * The function used to validate URLs
10702 * @param {String} value The URL
10704 'url' : function(v){
10705 return url.test(v);
10708 * The error text to display when the url validation function returns false
10711 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10714 * The function used to validate alpha values
10715 * @param {String} value The value
10717 'alpha' : function(v){
10718 return alpha.test(v);
10721 * The error text to display when the alpha validation function returns false
10724 'alphaText' : 'This field should only contain letters and _',
10726 * The keystroke filter mask to be applied on alpha input
10729 'alphaMask' : /[a-z_]/i,
10732 * The function used to validate alphanumeric values
10733 * @param {String} value The value
10735 'alphanum' : function(v){
10736 return alphanum.test(v);
10739 * The error text to display when the alphanumeric validation function returns false
10742 'alphanumText' : 'This field should only contain letters, numbers and _',
10744 * The keystroke filter mask to be applied on alphanumeric input
10747 'alphanumMask' : /[a-z0-9_]/i
10757 * @class Roo.bootstrap.Input
10758 * @extends Roo.bootstrap.Component
10759 * Bootstrap Input class
10760 * @cfg {Boolean} disabled is it disabled
10761 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
10762 * @cfg {String} name name of the input
10763 * @cfg {string} fieldLabel - the label associated
10764 * @cfg {string} placeholder - placeholder to put in text.
10765 * @cfg {string} before - input group add on before
10766 * @cfg {string} after - input group add on after
10767 * @cfg {string} size - (lg|sm) or leave empty..
10768 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10769 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10770 * @cfg {Number} md colspan out of 12 for computer-sized screens
10771 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10772 * @cfg {string} value default value of the input
10773 * @cfg {Number} labelWidth set the width of label
10774 * @cfg {Number} labellg set the width of label (1-12)
10775 * @cfg {Number} labelmd set the width of label (1-12)
10776 * @cfg {Number} labelsm set the width of label (1-12)
10777 * @cfg {Number} labelxs set the width of label (1-12)
10778 * @cfg {String} labelAlign (top|left)
10779 * @cfg {Boolean} readOnly Specifies that the field should be read-only
10780 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10781 * @cfg {String} indicatorpos (left|right) default left
10782 * @cfg {String} capture (user|camera) use for file input only. (default empty)
10783 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10784 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10786 * @cfg {String} align (left|center|right) Default left
10787 * @cfg {Boolean} forceFeedback (true|false) Default false
10790 * Create a new Input
10791 * @param {Object} config The config object
10794 Roo.bootstrap.Input = function(config){
10796 Roo.bootstrap.Input.superclass.constructor.call(this, config);
10801 * Fires when this field receives input focus.
10802 * @param {Roo.form.Field} this
10807 * Fires when this field loses input focus.
10808 * @param {Roo.form.Field} this
10812 * @event specialkey
10813 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
10814 * {@link Roo.EventObject#getKey} to determine which key was pressed.
10815 * @param {Roo.form.Field} this
10816 * @param {Roo.EventObject} e The event object
10821 * Fires just before the field blurs if the field value has changed.
10822 * @param {Roo.form.Field} this
10823 * @param {Mixed} newValue The new value
10824 * @param {Mixed} oldValue The original value
10829 * Fires after the field has been marked as invalid.
10830 * @param {Roo.form.Field} this
10831 * @param {String} msg The validation message
10836 * Fires after the field has been validated with no errors.
10837 * @param {Roo.form.Field} this
10842 * Fires after the key up
10843 * @param {Roo.form.Field} this
10844 * @param {Roo.EventObject} e The event Object
10849 * Fires after the user pastes into input
10850 * @param {Roo.form.Field} this
10851 * @param {Roo.EventObject} e The event Object
10857 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
10859 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10860 automatic validation (defaults to "keyup").
10862 validationEvent : "keyup",
10864 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10866 validateOnBlur : true,
10868 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10870 validationDelay : 250,
10872 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10874 focusClass : "x-form-focus", // not needed???
10878 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10880 invalidClass : "has-warning",
10883 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10885 validClass : "has-success",
10888 * @cfg {Boolean} hasFeedback (true|false) default true
10890 hasFeedback : true,
10893 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10895 invalidFeedbackClass : "glyphicon-warning-sign",
10898 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10900 validFeedbackClass : "glyphicon-ok",
10903 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10905 selectOnFocus : false,
10908 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10912 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10917 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10919 disableKeyFilter : false,
10922 * @cfg {Boolean} disabled True to disable the field (defaults to false).
10926 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10930 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10932 blankText : "Please complete this mandatory field",
10935 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10939 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10941 maxLength : Number.MAX_VALUE,
10943 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10945 minLengthText : "The minimum length for this field is {0}",
10947 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10949 maxLengthText : "The maximum length for this field is {0}",
10953 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10954 * If available, this function will be called only after the basic validators all return true, and will be passed the
10955 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10959 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10960 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10961 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
10965 * @cfg {String} regexText -- Depricated - use Invalid Text
10970 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10976 autocomplete: false,
10980 inputType : 'text',
10983 placeholder: false,
10988 preventMark: false,
10989 isFormField : true,
10992 labelAlign : false,
10995 formatedValue : false,
10996 forceFeedback : false,
10998 indicatorpos : 'left',
11008 parentLabelAlign : function()
11011 while (parent.parent()) {
11012 parent = parent.parent();
11013 if (typeof(parent.labelAlign) !='undefined') {
11014 return parent.labelAlign;
11021 getAutoCreate : function()
11023 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11029 if(this.inputType != 'hidden'){
11030 cfg.cls = 'form-group' //input-group
11036 type : this.inputType,
11037 value : this.value,
11038 cls : 'form-control',
11039 placeholder : this.placeholder || '',
11040 autocomplete : this.autocomplete || 'new-password'
11042 if (this.inputType == 'file') {
11043 input.style = 'overflow:hidden'; // why not in CSS?
11046 if(this.capture.length){
11047 input.capture = this.capture;
11050 if(this.accept.length){
11051 input.accept = this.accept + "/*";
11055 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
11058 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11059 input.maxLength = this.maxLength;
11062 if (this.disabled) {
11063 input.disabled=true;
11066 if (this.readOnly) {
11067 input.readonly=true;
11071 input.name = this.name;
11075 input.cls += ' input-' + this.size;
11079 ['xs','sm','md','lg'].map(function(size){
11080 if (settings[size]) {
11081 cfg.cls += ' col-' + size + '-' + settings[size];
11085 var inputblock = input;
11089 cls: 'glyphicon form-control-feedback'
11092 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11095 cls : 'has-feedback',
11103 if (this.before || this.after) {
11106 cls : 'input-group',
11110 if (this.before && typeof(this.before) == 'string') {
11112 inputblock.cn.push({
11114 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
11118 if (this.before && typeof(this.before) == 'object') {
11119 this.before = Roo.factory(this.before);
11121 inputblock.cn.push({
11123 cls : 'roo-input-before input-group-prepend input-group-' +
11124 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
11128 inputblock.cn.push(input);
11130 if (this.after && typeof(this.after) == 'string') {
11131 inputblock.cn.push({
11133 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
11137 if (this.after && typeof(this.after) == 'object') {
11138 this.after = Roo.factory(this.after);
11140 inputblock.cn.push({
11142 cls : 'roo-input-after input-group-append input-group-' +
11143 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
11147 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11148 inputblock.cls += ' has-feedback';
11149 inputblock.cn.push(feedback);
11154 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11155 tooltip : 'This field is required'
11157 if (this.allowBlank ) {
11158 indicator.style = this.allowBlank ? ' display:none' : '';
11160 if (align ==='left' && this.fieldLabel.length) {
11162 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
11169 cls : 'control-label col-form-label',
11170 html : this.fieldLabel
11181 var labelCfg = cfg.cn[1];
11182 var contentCfg = cfg.cn[2];
11184 if(this.indicatorpos == 'right'){
11189 cls : 'control-label col-form-label',
11193 html : this.fieldLabel
11207 labelCfg = cfg.cn[0];
11208 contentCfg = cfg.cn[1];
11212 if(this.labelWidth > 12){
11213 labelCfg.style = "width: " + this.labelWidth + 'px';
11216 if(this.labelWidth < 13 && this.labelmd == 0){
11217 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
11220 if(this.labellg > 0){
11221 labelCfg.cls += ' col-lg-' + this.labellg;
11222 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11225 if(this.labelmd > 0){
11226 labelCfg.cls += ' col-md-' + this.labelmd;
11227 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11230 if(this.labelsm > 0){
11231 labelCfg.cls += ' col-sm-' + this.labelsm;
11232 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11235 if(this.labelxs > 0){
11236 labelCfg.cls += ' col-xs-' + this.labelxs;
11237 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11241 } else if ( this.fieldLabel.length) {
11248 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
11249 tooltip : 'This field is required',
11250 style : this.allowBlank ? ' display:none' : ''
11254 //cls : 'input-group-addon',
11255 html : this.fieldLabel
11263 if(this.indicatorpos == 'right'){
11268 //cls : 'input-group-addon',
11269 html : this.fieldLabel
11274 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
11275 tooltip : 'This field is required',
11276 style : this.allowBlank ? ' display:none' : ''
11296 if (this.parentType === 'Navbar' && this.parent().bar) {
11297 cfg.cls += ' navbar-form';
11300 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
11301 // on BS4 we do this only if not form
11302 cfg.cls += ' navbar-form';
11310 * return the real input element.
11312 inputEl: function ()
11314 return this.el.select('input.form-control',true).first();
11317 tooltipEl : function()
11319 return this.inputEl();
11322 indicatorEl : function()
11324 if (Roo.bootstrap.version == 4) {
11325 return false; // not enabled in v4 yet.
11328 var indicator = this.el.select('i.roo-required-indicator',true).first();
11338 setDisabled : function(v)
11340 var i = this.inputEl().dom;
11342 i.removeAttribute('disabled');
11346 i.setAttribute('disabled','true');
11348 initEvents : function()
11351 this.inputEl().on("keydown" , this.fireKey, this);
11352 this.inputEl().on("focus", this.onFocus, this);
11353 this.inputEl().on("blur", this.onBlur, this);
11355 this.inputEl().relayEvent('keyup', this);
11356 this.inputEl().relayEvent('paste', this);
11358 this.indicator = this.indicatorEl();
11360 if(this.indicator){
11361 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
11364 // reference to original value for reset
11365 this.originalValue = this.getValue();
11366 //Roo.form.TextField.superclass.initEvents.call(this);
11367 if(this.validationEvent == 'keyup'){
11368 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11369 this.inputEl().on('keyup', this.filterValidation, this);
11371 else if(this.validationEvent !== false){
11372 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11375 if(this.selectOnFocus){
11376 this.on("focus", this.preFocus, this);
11379 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11380 this.inputEl().on("keypress", this.filterKeys, this);
11382 this.inputEl().relayEvent('keypress', this);
11385 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
11386 this.el.on("click", this.autoSize, this);
11389 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11390 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11393 if (typeof(this.before) == 'object') {
11394 this.before.render(this.el.select('.roo-input-before',true).first());
11396 if (typeof(this.after) == 'object') {
11397 this.after.render(this.el.select('.roo-input-after',true).first());
11400 this.inputEl().on('change', this.onChange, this);
11403 filterValidation : function(e){
11404 if(!e.isNavKeyPress()){
11405 this.validationTask.delay(this.validationDelay);
11409 * Validates the field value
11410 * @return {Boolean} True if the value is valid, else false
11412 validate : function(){
11413 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11414 if(this.disabled || this.validateValue(this.getRawValue())){
11419 this.markInvalid();
11425 * Validates a value according to the field's validation rules and marks the field as invalid
11426 * if the validation fails
11427 * @param {Mixed} value The value to validate
11428 * @return {Boolean} True if the value is valid, else false
11430 validateValue : function(value)
11432 if(this.getVisibilityEl().hasClass('hidden')){
11436 if(value.length < 1) { // if it's blank
11437 if(this.allowBlank){
11443 if(value.length < this.minLength){
11446 if(value.length > this.maxLength){
11450 var vt = Roo.form.VTypes;
11451 if(!vt[this.vtype](value, this)){
11455 if(typeof this.validator == "function"){
11456 var msg = this.validator(value);
11460 if (typeof(msg) == 'string') {
11461 this.invalidText = msg;
11465 if(this.regex && !this.regex.test(value)){
11473 fireKey : function(e){
11474 //Roo.log('field ' + e.getKey());
11475 if(e.isNavKeyPress()){
11476 this.fireEvent("specialkey", this, e);
11479 focus : function (selectText){
11481 this.inputEl().focus();
11482 if(selectText === true){
11483 this.inputEl().dom.select();
11489 onFocus : function(){
11490 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11491 // this.el.addClass(this.focusClass);
11493 if(!this.hasFocus){
11494 this.hasFocus = true;
11495 this.startValue = this.getValue();
11496 this.fireEvent("focus", this);
11500 beforeBlur : Roo.emptyFn,
11504 onBlur : function(){
11506 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11507 //this.el.removeClass(this.focusClass);
11509 this.hasFocus = false;
11510 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11513 var v = this.getValue();
11514 if(String(v) !== String(this.startValue)){
11515 this.fireEvent('change', this, v, this.startValue);
11517 this.fireEvent("blur", this);
11520 onChange : function(e)
11522 var v = this.getValue();
11523 if(String(v) !== String(this.startValue)){
11524 this.fireEvent('change', this, v, this.startValue);
11530 * Resets the current field value to the originally loaded value and clears any validation messages
11532 reset : function(){
11533 this.setValue(this.originalValue);
11537 * Returns the name of the field
11538 * @return {Mixed} name The name field
11540 getName: function(){
11544 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
11545 * @return {Mixed} value The field value
11547 getValue : function(){
11549 var v = this.inputEl().getValue();
11554 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
11555 * @return {Mixed} value The field value
11557 getRawValue : function(){
11558 var v = this.inputEl().getValue();
11564 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
11565 * @param {Mixed} value The value to set
11567 setRawValue : function(v){
11568 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11571 selectText : function(start, end){
11572 var v = this.getRawValue();
11574 start = start === undefined ? 0 : start;
11575 end = end === undefined ? v.length : end;
11576 var d = this.inputEl().dom;
11577 if(d.setSelectionRange){
11578 d.setSelectionRange(start, end);
11579 }else if(d.createTextRange){
11580 var range = d.createTextRange();
11581 range.moveStart("character", start);
11582 range.moveEnd("character", v.length-end);
11589 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
11590 * @param {Mixed} value The value to set
11592 setValue : function(v){
11595 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11601 processValue : function(value){
11602 if(this.stripCharsRe){
11603 var newValue = value.replace(this.stripCharsRe, '');
11604 if(newValue !== value){
11605 this.setRawValue(newValue);
11612 preFocus : function(){
11614 if(this.selectOnFocus){
11615 this.inputEl().dom.select();
11618 filterKeys : function(e){
11619 var k = e.getKey();
11620 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11623 var c = e.getCharCode(), cc = String.fromCharCode(c);
11624 if(Roo.isIE && (e.isSpecialKey() || !cc)){
11627 if(!this.maskRe.test(cc)){
11632 * Clear any invalid styles/messages for this field
11634 clearInvalid : function(){
11636 if(!this.el || this.preventMark){ // not rendered
11641 this.el.removeClass([this.invalidClass, 'is-invalid']);
11643 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11645 var feedback = this.el.select('.form-control-feedback', true).first();
11648 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11653 if(this.indicator){
11654 this.indicator.removeClass('visible');
11655 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11658 this.fireEvent('valid', this);
11662 * Mark this field as valid
11664 markValid : function()
11666 if(!this.el || this.preventMark){ // not rendered...
11670 this.el.removeClass([this.invalidClass, this.validClass]);
11671 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11673 var feedback = this.el.select('.form-control-feedback', true).first();
11676 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11679 if(this.indicator){
11680 this.indicator.removeClass('visible');
11681 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11689 if(this.allowBlank && !this.getRawValue().length){
11692 if (Roo.bootstrap.version == 3) {
11693 this.el.addClass(this.validClass);
11695 this.inputEl().addClass('is-valid');
11698 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11700 var feedback = this.el.select('.form-control-feedback', true).first();
11703 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11704 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11709 this.fireEvent('valid', this);
11713 * Mark this field as invalid
11714 * @param {String} msg The validation message
11716 markInvalid : function(msg)
11718 if(!this.el || this.preventMark){ // not rendered
11722 this.el.removeClass([this.invalidClass, this.validClass]);
11723 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11725 var feedback = this.el.select('.form-control-feedback', true).first();
11728 this.el.select('.form-control-feedback', true).first().removeClass(
11729 [this.invalidFeedbackClass, this.validFeedbackClass]);
11736 if(this.allowBlank && !this.getRawValue().length){
11740 if(this.indicator){
11741 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11742 this.indicator.addClass('visible');
11744 if (Roo.bootstrap.version == 3) {
11745 this.el.addClass(this.invalidClass);
11747 this.inputEl().addClass('is-invalid');
11752 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11754 var feedback = this.el.select('.form-control-feedback', true).first();
11757 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11759 if(this.getValue().length || this.forceFeedback){
11760 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11767 this.fireEvent('invalid', this, msg);
11770 SafariOnKeyDown : function(event)
11772 // this is a workaround for a password hang bug on chrome/ webkit.
11773 if (this.inputEl().dom.type != 'password') {
11777 var isSelectAll = false;
11779 if(this.inputEl().dom.selectionEnd > 0){
11780 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11782 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11783 event.preventDefault();
11788 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11790 event.preventDefault();
11791 // this is very hacky as keydown always get's upper case.
11793 var cc = String.fromCharCode(event.getCharCode());
11794 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
11798 adjustWidth : function(tag, w){
11799 tag = tag.toLowerCase();
11800 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11801 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11802 if(tag == 'input'){
11805 if(tag == 'textarea'){
11808 }else if(Roo.isOpera){
11809 if(tag == 'input'){
11812 if(tag == 'textarea'){
11820 setFieldLabel : function(v)
11822 if(!this.rendered){
11826 if(this.indicatorEl()){
11827 var ar = this.el.select('label > span',true);
11829 if (ar.elements.length) {
11830 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11831 this.fieldLabel = v;
11835 var br = this.el.select('label',true);
11837 if(br.elements.length) {
11838 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11839 this.fieldLabel = v;
11843 Roo.log('Cannot Found any of label > span || label in input');
11847 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11848 this.fieldLabel = v;
11863 * @class Roo.bootstrap.TextArea
11864 * @extends Roo.bootstrap.Input
11865 * Bootstrap TextArea class
11866 * @cfg {Number} cols Specifies the visible width of a text area
11867 * @cfg {Number} rows Specifies the visible number of lines in a text area
11868 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11869 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11870 * @cfg {string} html text
11873 * Create a new TextArea
11874 * @param {Object} config The config object
11877 Roo.bootstrap.TextArea = function(config){
11878 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11882 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
11892 getAutoCreate : function(){
11894 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11900 if(this.inputType != 'hidden'){
11901 cfg.cls = 'form-group' //input-group
11909 value : this.value || '',
11910 html: this.html || '',
11911 cls : 'form-control',
11912 placeholder : this.placeholder || ''
11916 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11917 input.maxLength = this.maxLength;
11921 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11925 input.cols = this.cols;
11928 if (this.readOnly) {
11929 input.readonly = true;
11933 input.name = this.name;
11937 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11941 ['xs','sm','md','lg'].map(function(size){
11942 if (settings[size]) {
11943 cfg.cls += ' col-' + size + '-' + settings[size];
11947 var inputblock = input;
11949 if(this.hasFeedback && !this.allowBlank){
11953 cls: 'glyphicon form-control-feedback'
11957 cls : 'has-feedback',
11966 if (this.before || this.after) {
11969 cls : 'input-group',
11973 inputblock.cn.push({
11975 cls : 'input-group-addon',
11980 inputblock.cn.push(input);
11982 if(this.hasFeedback && !this.allowBlank){
11983 inputblock.cls += ' has-feedback';
11984 inputblock.cn.push(feedback);
11988 inputblock.cn.push({
11990 cls : 'input-group-addon',
11997 if (align ==='left' && this.fieldLabel.length) {
12002 cls : 'control-label',
12003 html : this.fieldLabel
12014 if(this.labelWidth > 12){
12015 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
12018 if(this.labelWidth < 13 && this.labelmd == 0){
12019 this.labelmd = this.labelWidth;
12022 if(this.labellg > 0){
12023 cfg.cn[0].cls += ' col-lg-' + this.labellg;
12024 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
12027 if(this.labelmd > 0){
12028 cfg.cn[0].cls += ' col-md-' + this.labelmd;
12029 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
12032 if(this.labelsm > 0){
12033 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
12034 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
12037 if(this.labelxs > 0){
12038 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
12039 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
12042 } else if ( this.fieldLabel.length) {
12047 //cls : 'input-group-addon',
12048 html : this.fieldLabel
12066 if (this.disabled) {
12067 input.disabled=true;
12074 * return the real textarea element.
12076 inputEl: function ()
12078 return this.el.select('textarea.form-control',true).first();
12082 * Clear any invalid styles/messages for this field
12084 clearInvalid : function()
12087 if(!this.el || this.preventMark){ // not rendered
12091 var label = this.el.select('label', true).first();
12092 var icon = this.el.select('i.fa-star', true).first();
12097 this.el.removeClass( this.validClass);
12098 this.inputEl().removeClass('is-invalid');
12100 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12102 var feedback = this.el.select('.form-control-feedback', true).first();
12105 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12110 this.fireEvent('valid', this);
12114 * Mark this field as valid
12116 markValid : function()
12118 if(!this.el || this.preventMark){ // not rendered
12122 this.el.removeClass([this.invalidClass, this.validClass]);
12123 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12125 var feedback = this.el.select('.form-control-feedback', true).first();
12128 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12131 if(this.disabled || this.allowBlank){
12135 var label = this.el.select('label', true).first();
12136 var icon = this.el.select('i.fa-star', true).first();
12141 if (Roo.bootstrap.version == 3) {
12142 this.el.addClass(this.validClass);
12144 this.inputEl().addClass('is-valid');
12148 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12150 var feedback = this.el.select('.form-control-feedback', true).first();
12153 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12154 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12159 this.fireEvent('valid', this);
12163 * Mark this field as invalid
12164 * @param {String} msg The validation message
12166 markInvalid : function(msg)
12168 if(!this.el || this.preventMark){ // not rendered
12172 this.el.removeClass([this.invalidClass, this.validClass]);
12173 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12175 var feedback = this.el.select('.form-control-feedback', true).first();
12178 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12181 if(this.disabled || this.allowBlank){
12185 var label = this.el.select('label', true).first();
12186 var icon = this.el.select('i.fa-star', true).first();
12188 if(!this.getValue().length && label && !icon){
12189 this.el.createChild({
12191 cls : 'text-danger fa fa-lg fa-star',
12192 tooltip : 'This field is required',
12193 style : 'margin-right:5px;'
12197 if (Roo.bootstrap.version == 3) {
12198 this.el.addClass(this.invalidClass);
12200 this.inputEl().addClass('is-invalid');
12203 // fixme ... this may be depricated need to test..
12204 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12206 var feedback = this.el.select('.form-control-feedback', true).first();
12209 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12211 if(this.getValue().length || this.forceFeedback){
12212 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12219 this.fireEvent('invalid', this, msg);
12227 * trigger field - base class for combo..
12232 * @class Roo.bootstrap.TriggerField
12233 * @extends Roo.bootstrap.Input
12234 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
12235 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
12236 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
12237 * for which you can provide a custom implementation. For example:
12239 var trigger = new Roo.bootstrap.TriggerField();
12240 trigger.onTriggerClick = myTriggerFn;
12241 trigger.applyTo('my-field');
12244 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
12245 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
12246 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
12247 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
12248 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
12251 * Create a new TriggerField.
12252 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
12253 * to the base TextField)
12255 Roo.bootstrap.TriggerField = function(config){
12256 this.mimicing = false;
12257 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
12260 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
12262 * @cfg {String} triggerClass A CSS class to apply to the trigger
12265 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
12270 * @cfg {Boolean} removable (true|false) special filter default false
12274 /** @cfg {Boolean} grow @hide */
12275 /** @cfg {Number} growMin @hide */
12276 /** @cfg {Number} growMax @hide */
12282 autoSize: Roo.emptyFn,
12286 deferHeight : true,
12289 actionMode : 'wrap',
12294 getAutoCreate : function(){
12296 var align = this.labelAlign || this.parentLabelAlign();
12301 cls: 'form-group' //input-group
12308 type : this.inputType,
12309 cls : 'form-control',
12310 autocomplete: 'new-password',
12311 placeholder : this.placeholder || ''
12315 input.name = this.name;
12318 input.cls += ' input-' + this.size;
12321 if (this.disabled) {
12322 input.disabled=true;
12325 var inputblock = input;
12327 if(this.hasFeedback && !this.allowBlank){
12331 cls: 'glyphicon form-control-feedback'
12334 if(this.removable && !this.editable ){
12336 cls : 'has-feedback',
12342 cls : 'roo-combo-removable-btn close'
12349 cls : 'has-feedback',
12358 if(this.removable && !this.editable ){
12360 cls : 'roo-removable',
12366 cls : 'roo-combo-removable-btn close'
12373 if (this.before || this.after) {
12376 cls : 'input-group',
12380 inputblock.cn.push({
12382 cls : 'input-group-addon input-group-prepend input-group-text',
12387 inputblock.cn.push(input);
12389 if(this.hasFeedback && !this.allowBlank){
12390 inputblock.cls += ' has-feedback';
12391 inputblock.cn.push(feedback);
12395 inputblock.cn.push({
12397 cls : 'input-group-addon input-group-append input-group-text',
12406 var ibwrap = inputblock;
12411 cls: 'roo-select2-choices',
12415 cls: 'roo-select2-search-field',
12427 cls: 'roo-select2-container input-group',
12432 cls: 'form-hidden-field'
12438 if(!this.multiple && this.showToggleBtn){
12444 if (this.caret != false) {
12447 cls: 'fa fa-' + this.caret
12454 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12456 Roo.bootstrap.version == 3 ? caret : '',
12459 cls: 'combobox-clear',
12473 combobox.cls += ' roo-select2-container-multi';
12477 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12478 tooltip : 'This field is required'
12480 if (Roo.bootstrap.version == 4) {
12483 style : 'display:none'
12488 if (align ==='left' && this.fieldLabel.length) {
12490 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12497 cls : 'control-label',
12498 html : this.fieldLabel
12510 var labelCfg = cfg.cn[1];
12511 var contentCfg = cfg.cn[2];
12513 if(this.indicatorpos == 'right'){
12518 cls : 'control-label',
12522 html : this.fieldLabel
12536 labelCfg = cfg.cn[0];
12537 contentCfg = cfg.cn[1];
12540 if(this.labelWidth > 12){
12541 labelCfg.style = "width: " + this.labelWidth + 'px';
12544 if(this.labelWidth < 13 && this.labelmd == 0){
12545 this.labelmd = this.labelWidth;
12548 if(this.labellg > 0){
12549 labelCfg.cls += ' col-lg-' + this.labellg;
12550 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12553 if(this.labelmd > 0){
12554 labelCfg.cls += ' col-md-' + this.labelmd;
12555 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12558 if(this.labelsm > 0){
12559 labelCfg.cls += ' col-sm-' + this.labelsm;
12560 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12563 if(this.labelxs > 0){
12564 labelCfg.cls += ' col-xs-' + this.labelxs;
12565 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12568 } else if ( this.fieldLabel.length) {
12569 // Roo.log(" label");
12574 //cls : 'input-group-addon',
12575 html : this.fieldLabel
12583 if(this.indicatorpos == 'right'){
12591 html : this.fieldLabel
12605 // Roo.log(" no label && no align");
12612 ['xs','sm','md','lg'].map(function(size){
12613 if (settings[size]) {
12614 cfg.cls += ' col-' + size + '-' + settings[size];
12625 onResize : function(w, h){
12626 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12627 // if(typeof w == 'number'){
12628 // var x = w - this.trigger.getWidth();
12629 // this.inputEl().setWidth(this.adjustWidth('input', x));
12630 // this.trigger.setStyle('left', x+'px');
12635 adjustSize : Roo.BoxComponent.prototype.adjustSize,
12638 getResizeEl : function(){
12639 return this.inputEl();
12643 getPositionEl : function(){
12644 return this.inputEl();
12648 alignErrorIcon : function(){
12649 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12653 initEvents : function(){
12657 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12658 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12659 if(!this.multiple && this.showToggleBtn){
12660 this.trigger = this.el.select('span.dropdown-toggle',true).first();
12661 if(this.hideTrigger){
12662 this.trigger.setDisplayed(false);
12664 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12668 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12671 if(this.removable && !this.editable && !this.tickable){
12672 var close = this.closeTriggerEl();
12675 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12676 close.on('click', this.removeBtnClick, this, close);
12680 //this.trigger.addClassOnOver('x-form-trigger-over');
12681 //this.trigger.addClassOnClick('x-form-trigger-click');
12684 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12688 closeTriggerEl : function()
12690 var close = this.el.select('.roo-combo-removable-btn', true).first();
12691 return close ? close : false;
12694 removeBtnClick : function(e, h, el)
12696 e.preventDefault();
12698 if(this.fireEvent("remove", this) !== false){
12700 this.fireEvent("afterremove", this)
12704 createList : function()
12706 this.list = Roo.get(document.body).createChild({
12707 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12708 cls: 'typeahead typeahead-long dropdown-menu shadow',
12709 style: 'display:none'
12712 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12717 initTrigger : function(){
12722 onDestroy : function(){
12724 this.trigger.removeAllListeners();
12725 // this.trigger.remove();
12728 // this.wrap.remove();
12730 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12734 onFocus : function(){
12735 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12737 if(!this.mimicing){
12738 this.wrap.addClass('x-trigger-wrap-focus');
12739 this.mimicing = true;
12740 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12741 if(this.monitorTab){
12742 this.el.on("keydown", this.checkTab, this);
12749 checkTab : function(e){
12750 if(e.getKey() == e.TAB){
12751 this.triggerBlur();
12756 onBlur : function(){
12761 mimicBlur : function(e, t){
12763 if(!this.wrap.contains(t) && this.validateBlur()){
12764 this.triggerBlur();
12770 triggerBlur : function(){
12771 this.mimicing = false;
12772 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12773 if(this.monitorTab){
12774 this.el.un("keydown", this.checkTab, this);
12776 //this.wrap.removeClass('x-trigger-wrap-focus');
12777 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12781 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12782 validateBlur : function(e, t){
12787 onDisable : function(){
12788 this.inputEl().dom.disabled = true;
12789 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12791 // this.wrap.addClass('x-item-disabled');
12796 onEnable : function(){
12797 this.inputEl().dom.disabled = false;
12798 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12800 // this.el.removeClass('x-item-disabled');
12805 onShow : function(){
12806 var ae = this.getActionEl();
12809 ae.dom.style.display = '';
12810 ae.dom.style.visibility = 'visible';
12816 onHide : function(){
12817 var ae = this.getActionEl();
12818 ae.dom.style.display = 'none';
12822 * The function that should handle the trigger's click event. This method does nothing by default until overridden
12823 * by an implementing function.
12825 * @param {EventObject} e
12827 onTriggerClick : Roo.emptyFn
12835 * @class Roo.bootstrap.CardUploader
12836 * @extends Roo.bootstrap.Button
12837 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12838 * @cfg {Number} errorTimeout default 3000
12839 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
12840 * @cfg {Array} html The button text.
12844 * Create a new CardUploader
12845 * @param {Object} config The config object
12848 Roo.bootstrap.CardUploader = function(config){
12852 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12855 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
12863 * When a image is clicked on - and needs to display a slideshow or similar..
12864 * @param {Roo.bootstrap.Card} this
12865 * @param {Object} The image information data
12871 * When a the download link is clicked
12872 * @param {Roo.bootstrap.Card} this
12873 * @param {Object} The image information data contains
12880 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
12883 errorTimeout : 3000,
12887 fileCollection : false,
12890 getAutoCreate : function()
12894 cls :'form-group' ,
12899 //cls : 'input-group-addon',
12900 html : this.fieldLabel
12908 value : this.value,
12909 cls : 'd-none form-control'
12914 multiple : 'multiple',
12916 cls : 'd-none roo-card-upload-selector'
12920 cls : 'roo-card-uploader-button-container w-100 mb-2'
12923 cls : 'card-columns roo-card-uploader-container'
12933 getChildContainer : function() /// what children are added to.
12935 return this.containerEl;
12938 getButtonContainer : function() /// what children are added to.
12940 return this.el.select(".roo-card-uploader-button-container").first();
12943 initEvents : function()
12946 Roo.bootstrap.Input.prototype.initEvents.call(this);
12950 xns: Roo.bootstrap,
12953 container_method : 'getButtonContainer' ,
12954 html : this.html, // fix changable?
12957 'click' : function(btn, e) {
12966 this.urlAPI = (window.createObjectURL && window) ||
12967 (window.URL && URL.revokeObjectURL && URL) ||
12968 (window.webkitURL && webkitURL);
12973 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12975 this.selectorEl.on('change', this.onFileSelected, this);
12978 this.images.forEach(function(img) {
12981 this.images = false;
12983 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12989 onClick : function(e)
12991 e.preventDefault();
12993 this.selectorEl.dom.click();
12997 onFileSelected : function(e)
12999 e.preventDefault();
13001 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
13005 Roo.each(this.selectorEl.dom.files, function(file){
13006 this.addFile(file);
13015 addFile : function(file)
13018 if(typeof(file) === 'string'){
13019 throw "Add file by name?"; // should not happen
13023 if(!file || !this.urlAPI){
13033 var url = _this.urlAPI.createObjectURL( file);
13036 id : Roo.bootstrap.CardUploader.ID--,
13037 is_uploaded : false,
13041 mimetype : file.type,
13049 * addCard - add an Attachment to the uploader
13050 * @param data - the data about the image to upload
13054 title : "Title of file",
13055 is_uploaded : false,
13056 src : "http://.....",
13057 srcfile : { the File upload object },
13058 mimetype : file.type,
13061 .. any other data...
13067 addCard : function (data)
13069 // hidden input element?
13070 // if the file is not an image...
13071 //then we need to use something other that and header_image
13076 xns : Roo.bootstrap,
13077 xtype : 'CardFooter',
13080 xns : Roo.bootstrap,
13086 xns : Roo.bootstrap,
13088 html : String.format("<small>{0}</small>", data.title),
13089 cls : 'col-10 text-left',
13094 click : function() {
13096 t.fireEvent( "download", t, data );
13102 xns : Roo.bootstrap,
13104 style: 'max-height: 28px; ',
13110 click : function() {
13111 t.removeCard(data.id)
13123 var cn = this.addxtype(
13126 xns : Roo.bootstrap,
13129 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
13130 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
13131 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
13136 initEvents : function() {
13137 Roo.bootstrap.Card.prototype.initEvents.call(this);
13139 this.imgEl = this.el.select('.card-img-top').first();
13141 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
13142 this.imgEl.set({ 'pointer' : 'cursor' });
13145 this.getCardFooter().addClass('p-1');
13152 // dont' really need ot update items.
13153 // this.items.push(cn);
13154 this.fileCollection.add(cn);
13156 if (!data.srcfile) {
13157 this.updateInput();
13162 var reader = new FileReader();
13163 reader.addEventListener("load", function() {
13164 data.srcdata = reader.result;
13167 reader.readAsDataURL(data.srcfile);
13172 removeCard : function(id)
13175 var card = this.fileCollection.get(id);
13176 card.data.is_deleted = 1;
13177 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
13178 //this.fileCollection.remove(card);
13179 //this.items = this.items.filter(function(e) { return e != card });
13180 // dont' really need ot update items.
13181 card.el.dom.parentNode.removeChild(card.el.dom);
13182 this.updateInput();
13188 this.fileCollection.each(function(card) {
13189 if (card.el.dom && card.el.dom.parentNode) {
13190 card.el.dom.parentNode.removeChild(card.el.dom);
13193 this.fileCollection.clear();
13194 this.updateInput();
13197 updateInput : function()
13200 this.fileCollection.each(function(e) {
13204 this.inputEl().dom.value = JSON.stringify(data);
13214 Roo.bootstrap.CardUploader.ID = -1;/*
13216 * Ext JS Library 1.1.1
13217 * Copyright(c) 2006-2007, Ext JS, LLC.
13219 * Originally Released Under LGPL - original licence link has changed is not relivant.
13222 * <script type="text/javascript">
13227 * @class Roo.data.SortTypes
13229 * Defines the default sorting (casting?) comparison functions used when sorting data.
13231 Roo.data.SortTypes = {
13233 * Default sort that does nothing
13234 * @param {Mixed} s The value being converted
13235 * @return {Mixed} The comparison value
13237 none : function(s){
13242 * The regular expression used to strip tags
13246 stripTagsRE : /<\/?[^>]+>/gi,
13249 * Strips all HTML tags to sort on text only
13250 * @param {Mixed} s The value being converted
13251 * @return {String} The comparison value
13253 asText : function(s){
13254 return String(s).replace(this.stripTagsRE, "");
13258 * Strips all HTML tags to sort on text only - Case insensitive
13259 * @param {Mixed} s The value being converted
13260 * @return {String} The comparison value
13262 asUCText : function(s){
13263 return String(s).toUpperCase().replace(this.stripTagsRE, "");
13267 * Case insensitive string
13268 * @param {Mixed} s The value being converted
13269 * @return {String} The comparison value
13271 asUCString : function(s) {
13272 return String(s).toUpperCase();
13277 * @param {Mixed} s The value being converted
13278 * @return {Number} The comparison value
13280 asDate : function(s) {
13284 if(s instanceof Date){
13285 return s.getTime();
13287 return Date.parse(String(s));
13292 * @param {Mixed} s The value being converted
13293 * @return {Float} The comparison value
13295 asFloat : function(s) {
13296 var val = parseFloat(String(s).replace(/,/g, ""));
13305 * @param {Mixed} s The value being converted
13306 * @return {Number} The comparison value
13308 asInt : function(s) {
13309 var val = parseInt(String(s).replace(/,/g, ""));
13317 * Ext JS Library 1.1.1
13318 * Copyright(c) 2006-2007, Ext JS, LLC.
13320 * Originally Released Under LGPL - original licence link has changed is not relivant.
13323 * <script type="text/javascript">
13327 * @class Roo.data.Record
13328 * Instances of this class encapsulate both record <em>definition</em> information, and record
13329 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13330 * to access Records cached in an {@link Roo.data.Store} object.<br>
13332 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13333 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13336 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13338 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13339 * {@link #create}. The parameters are the same.
13340 * @param {Array} data An associative Array of data values keyed by the field name.
13341 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13342 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13343 * not specified an integer id is generated.
13345 Roo.data.Record = function(data, id){
13346 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13351 * Generate a constructor for a specific record layout.
13352 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13353 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13354 * Each field definition object may contain the following properties: <ul>
13355 * <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,
13356 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13357 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13358 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13359 * is being used, then this is a string containing the javascript expression to reference the data relative to
13360 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13361 * to the data item relative to the record element. If the mapping expression is the same as the field name,
13362 * this may be omitted.</p></li>
13363 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13364 * <ul><li>auto (Default, implies no conversion)</li>
13369 * <li>date</li></ul></p></li>
13370 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13371 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13372 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13373 * by the Reader into an object that will be stored in the Record. It is passed the
13374 * following parameters:<ul>
13375 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13377 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13379 * <br>usage:<br><pre><code>
13380 var TopicRecord = Roo.data.Record.create(
13381 {name: 'title', mapping: 'topic_title'},
13382 {name: 'author', mapping: 'username'},
13383 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13384 {name: 'lastPost', mapping: 'post_time', type: 'date'},
13385 {name: 'lastPoster', mapping: 'user2'},
13386 {name: 'excerpt', mapping: 'post_text'}
13389 var myNewRecord = new TopicRecord({
13390 title: 'Do my job please',
13393 lastPost: new Date(),
13394 lastPoster: 'Animal',
13395 excerpt: 'No way dude!'
13397 myStore.add(myNewRecord);
13402 Roo.data.Record.create = function(o){
13403 var f = function(){
13404 f.superclass.constructor.apply(this, arguments);
13406 Roo.extend(f, Roo.data.Record);
13407 var p = f.prototype;
13408 p.fields = new Roo.util.MixedCollection(false, function(field){
13411 for(var i = 0, len = o.length; i < len; i++){
13412 p.fields.add(new Roo.data.Field(o[i]));
13414 f.getField = function(name){
13415 return p.fields.get(name);
13420 Roo.data.Record.AUTO_ID = 1000;
13421 Roo.data.Record.EDIT = 'edit';
13422 Roo.data.Record.REJECT = 'reject';
13423 Roo.data.Record.COMMIT = 'commit';
13425 Roo.data.Record.prototype = {
13427 * Readonly flag - true if this record has been modified.
13436 join : function(store){
13437 this.store = store;
13441 * Set the named field to the specified value.
13442 * @param {String} name The name of the field to set.
13443 * @param {Object} value The value to set the field to.
13445 set : function(name, value){
13446 if(this.data[name] == value){
13450 if(!this.modified){
13451 this.modified = {};
13453 if(typeof this.modified[name] == 'undefined'){
13454 this.modified[name] = this.data[name];
13456 this.data[name] = value;
13457 if(!this.editing && this.store){
13458 this.store.afterEdit(this);
13463 * Get the value of the named field.
13464 * @param {String} name The name of the field to get the value of.
13465 * @return {Object} The value of the field.
13467 get : function(name){
13468 return this.data[name];
13472 beginEdit : function(){
13473 this.editing = true;
13474 this.modified = {};
13478 cancelEdit : function(){
13479 this.editing = false;
13480 delete this.modified;
13484 endEdit : function(){
13485 this.editing = false;
13486 if(this.dirty && this.store){
13487 this.store.afterEdit(this);
13492 * Usually called by the {@link Roo.data.Store} which owns the Record.
13493 * Rejects all changes made to the Record since either creation, or the last commit operation.
13494 * Modified fields are reverted to their original values.
13496 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13497 * of reject operations.
13499 reject : function(){
13500 var m = this.modified;
13502 if(typeof m[n] != "function"){
13503 this.data[n] = m[n];
13506 this.dirty = false;
13507 delete this.modified;
13508 this.editing = false;
13510 this.store.afterReject(this);
13515 * Usually called by the {@link Roo.data.Store} which owns the Record.
13516 * Commits all changes made to the Record since either creation, or the last commit operation.
13518 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13519 * of commit operations.
13521 commit : function(){
13522 this.dirty = false;
13523 delete this.modified;
13524 this.editing = false;
13526 this.store.afterCommit(this);
13531 hasError : function(){
13532 return this.error != null;
13536 clearError : function(){
13541 * Creates a copy of this record.
13542 * @param {String} id (optional) A new record id if you don't want to use this record's id
13545 copy : function(newId) {
13546 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13550 * Ext JS Library 1.1.1
13551 * Copyright(c) 2006-2007, Ext JS, LLC.
13553 * Originally Released Under LGPL - original licence link has changed is not relivant.
13556 * <script type="text/javascript">
13562 * @class Roo.data.Store
13563 * @extends Roo.util.Observable
13564 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13565 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13567 * 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
13568 * has no knowledge of the format of the data returned by the Proxy.<br>
13570 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13571 * instances from the data object. These records are cached and made available through accessor functions.
13573 * Creates a new Store.
13574 * @param {Object} config A config object containing the objects needed for the Store to access data,
13575 * and read the data into Records.
13577 Roo.data.Store = function(config){
13578 this.data = new Roo.util.MixedCollection(false);
13579 this.data.getKey = function(o){
13582 this.baseParams = {};
13584 this.paramNames = {
13589 "multisort" : "_multisort"
13592 if(config && config.data){
13593 this.inlineData = config.data;
13594 delete config.data;
13597 Roo.apply(this, config);
13599 if(this.reader){ // reader passed
13600 this.reader = Roo.factory(this.reader, Roo.data);
13601 this.reader.xmodule = this.xmodule || false;
13602 if(!this.recordType){
13603 this.recordType = this.reader.recordType;
13605 if(this.reader.onMetaChange){
13606 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13610 if(this.recordType){
13611 this.fields = this.recordType.prototype.fields;
13613 this.modified = [];
13617 * @event datachanged
13618 * Fires when the data cache has changed, and a widget which is using this Store
13619 * as a Record cache should refresh its view.
13620 * @param {Store} this
13622 datachanged : true,
13624 * @event metachange
13625 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13626 * @param {Store} this
13627 * @param {Object} meta The JSON metadata
13632 * Fires when Records have been added to the Store
13633 * @param {Store} this
13634 * @param {Roo.data.Record[]} records The array of Records added
13635 * @param {Number} index The index at which the record(s) were added
13640 * Fires when a Record has been removed from the Store
13641 * @param {Store} this
13642 * @param {Roo.data.Record} record The Record that was removed
13643 * @param {Number} index The index at which the record was removed
13648 * Fires when a Record has been updated
13649 * @param {Store} this
13650 * @param {Roo.data.Record} record The Record that was updated
13651 * @param {String} operation The update operation being performed. Value may be one of:
13653 Roo.data.Record.EDIT
13654 Roo.data.Record.REJECT
13655 Roo.data.Record.COMMIT
13661 * Fires when the data cache has been cleared.
13662 * @param {Store} this
13666 * @event beforeload
13667 * Fires before a request is made for a new data object. If the beforeload handler returns false
13668 * the load action will be canceled.
13669 * @param {Store} this
13670 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13674 * @event beforeloadadd
13675 * Fires after a new set of Records has been loaded.
13676 * @param {Store} this
13677 * @param {Roo.data.Record[]} records The Records that were loaded
13678 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13680 beforeloadadd : true,
13683 * Fires after a new set of Records has been loaded, before they are added to the store.
13684 * @param {Store} this
13685 * @param {Roo.data.Record[]} records The Records that were loaded
13686 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13687 * @params {Object} return from reader
13691 * @event loadexception
13692 * Fires if an exception occurs in the Proxy during loading.
13693 * Called with the signature of the Proxy's "loadexception" event.
13694 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13697 * @param {Object} return from JsonData.reader() - success, totalRecords, records
13698 * @param {Object} load options
13699 * @param {Object} jsonData from your request (normally this contains the Exception)
13701 loadexception : true
13705 this.proxy = Roo.factory(this.proxy, Roo.data);
13706 this.proxy.xmodule = this.xmodule || false;
13707 this.relayEvents(this.proxy, ["loadexception"]);
13709 this.sortToggle = {};
13710 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13712 Roo.data.Store.superclass.constructor.call(this);
13714 if(this.inlineData){
13715 this.loadData(this.inlineData);
13716 delete this.inlineData;
13720 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13722 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
13723 * without a remote query - used by combo/forms at present.
13727 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13730 * @cfg {Array} data Inline data to be loaded when the store is initialized.
13733 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13734 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13737 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13738 * on any HTTP request
13741 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13744 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13748 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13749 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13751 remoteSort : false,
13754 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13755 * loaded or when a record is removed. (defaults to false).
13757 pruneModifiedRecords : false,
13760 lastOptions : null,
13763 * Add Records to the Store and fires the add event.
13764 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13766 add : function(records){
13767 records = [].concat(records);
13768 for(var i = 0, len = records.length; i < len; i++){
13769 records[i].join(this);
13771 var index = this.data.length;
13772 this.data.addAll(records);
13773 this.fireEvent("add", this, records, index);
13777 * Remove a Record from the Store and fires the remove event.
13778 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13780 remove : function(record){
13781 var index = this.data.indexOf(record);
13782 this.data.removeAt(index);
13784 if(this.pruneModifiedRecords){
13785 this.modified.remove(record);
13787 this.fireEvent("remove", this, record, index);
13791 * Remove all Records from the Store and fires the clear event.
13793 removeAll : function(){
13795 if(this.pruneModifiedRecords){
13796 this.modified = [];
13798 this.fireEvent("clear", this);
13802 * Inserts Records to the Store at the given index and fires the add event.
13803 * @param {Number} index The start index at which to insert the passed Records.
13804 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13806 insert : function(index, records){
13807 records = [].concat(records);
13808 for(var i = 0, len = records.length; i < len; i++){
13809 this.data.insert(index, records[i]);
13810 records[i].join(this);
13812 this.fireEvent("add", this, records, index);
13816 * Get the index within the cache of the passed Record.
13817 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13818 * @return {Number} The index of the passed Record. Returns -1 if not found.
13820 indexOf : function(record){
13821 return this.data.indexOf(record);
13825 * Get the index within the cache of the Record with the passed id.
13826 * @param {String} id The id of the Record to find.
13827 * @return {Number} The index of the Record. Returns -1 if not found.
13829 indexOfId : function(id){
13830 return this.data.indexOfKey(id);
13834 * Get the Record with the specified id.
13835 * @param {String} id The id of the Record to find.
13836 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13838 getById : function(id){
13839 return this.data.key(id);
13843 * Get the Record at the specified index.
13844 * @param {Number} index The index of the Record to find.
13845 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13847 getAt : function(index){
13848 return this.data.itemAt(index);
13852 * Returns a range of Records between specified indices.
13853 * @param {Number} startIndex (optional) The starting index (defaults to 0)
13854 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13855 * @return {Roo.data.Record[]} An array of Records
13857 getRange : function(start, end){
13858 return this.data.getRange(start, end);
13862 storeOptions : function(o){
13863 o = Roo.apply({}, o);
13866 this.lastOptions = o;
13870 * Loads the Record cache from the configured Proxy using the configured Reader.
13872 * If using remote paging, then the first load call must specify the <em>start</em>
13873 * and <em>limit</em> properties in the options.params property to establish the initial
13874 * position within the dataset, and the number of Records to cache on each read from the Proxy.
13876 * <strong>It is important to note that for remote data sources, loading is asynchronous,
13877 * and this call will return before the new data has been loaded. Perform any post-processing
13878 * in a callback function, or in a "load" event handler.</strong>
13880 * @param {Object} options An object containing properties which control loading options:<ul>
13881 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13882 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13883 * passed the following arguments:<ul>
13884 * <li>r : Roo.data.Record[]</li>
13885 * <li>options: Options object from the load call</li>
13886 * <li>success: Boolean success indicator</li></ul></li>
13887 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13888 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13891 load : function(options){
13892 options = options || {};
13893 if(this.fireEvent("beforeload", this, options) !== false){
13894 this.storeOptions(options);
13895 var p = Roo.apply(options.params || {}, this.baseParams);
13896 // if meta was not loaded from remote source.. try requesting it.
13897 if (!this.reader.metaFromRemote) {
13898 p._requestMeta = 1;
13900 if(this.sortInfo && this.remoteSort){
13901 var pn = this.paramNames;
13902 p[pn["sort"]] = this.sortInfo.field;
13903 p[pn["dir"]] = this.sortInfo.direction;
13905 if (this.multiSort) {
13906 var pn = this.paramNames;
13907 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13910 this.proxy.load(p, this.reader, this.loadRecords, this, options);
13915 * Reloads the Record cache from the configured Proxy using the configured Reader and
13916 * the options from the last load operation performed.
13917 * @param {Object} options (optional) An object containing properties which may override the options
13918 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13919 * the most recently used options are reused).
13921 reload : function(options){
13922 this.load(Roo.applyIf(options||{}, this.lastOptions));
13926 // Called as a callback by the Reader during a load operation.
13927 loadRecords : function(o, options, success){
13928 if(!o || success === false){
13929 if(success !== false){
13930 this.fireEvent("load", this, [], options, o);
13932 if(options.callback){
13933 options.callback.call(options.scope || this, [], options, false);
13937 // if data returned failure - throw an exception.
13938 if (o.success === false) {
13939 // show a message if no listener is registered.
13940 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13941 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13943 // loadmask wil be hooked into this..
13944 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13947 var r = o.records, t = o.totalRecords || r.length;
13949 this.fireEvent("beforeloadadd", this, r, options, o);
13951 if(!options || options.add !== true){
13952 if(this.pruneModifiedRecords){
13953 this.modified = [];
13955 for(var i = 0, len = r.length; i < len; i++){
13959 this.data = this.snapshot;
13960 delete this.snapshot;
13963 this.data.addAll(r);
13964 this.totalLength = t;
13966 this.fireEvent("datachanged", this);
13968 this.totalLength = Math.max(t, this.data.length+r.length);
13972 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13974 var e = new Roo.data.Record({});
13976 e.set(this.parent.displayField, this.parent.emptyTitle);
13977 e.set(this.parent.valueField, '');
13982 this.fireEvent("load", this, r, options, o);
13983 if(options.callback){
13984 options.callback.call(options.scope || this, r, options, true);
13990 * Loads data from a passed data block. A Reader which understands the format of the data
13991 * must have been configured in the constructor.
13992 * @param {Object} data The data block from which to read the Records. The format of the data expected
13993 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13994 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13996 loadData : function(o, append){
13997 var r = this.reader.readRecords(o);
13998 this.loadRecords(r, {add: append}, true);
14002 * using 'cn' the nested child reader read the child array into it's child stores.
14003 * @param {Object} rec The record with a 'children array
14005 loadDataFromChildren : function(rec)
14007 this.loadData(this.reader.toLoadData(rec));
14012 * Gets the number of cached records.
14014 * <em>If using paging, this may not be the total size of the dataset. If the data object
14015 * used by the Reader contains the dataset size, then the getTotalCount() function returns
14016 * the data set size</em>
14018 getCount : function(){
14019 return this.data.length || 0;
14023 * Gets the total number of records in the dataset as returned by the server.
14025 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
14026 * the dataset size</em>
14028 getTotalCount : function(){
14029 return this.totalLength || 0;
14033 * Returns the sort state of the Store as an object with two properties:
14035 field {String} The name of the field by which the Records are sorted
14036 direction {String} The sort order, "ASC" or "DESC"
14039 getSortState : function(){
14040 return this.sortInfo;
14044 applySort : function(){
14045 if(this.sortInfo && !this.remoteSort){
14046 var s = this.sortInfo, f = s.field;
14047 var st = this.fields.get(f).sortType;
14048 var fn = function(r1, r2){
14049 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
14050 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
14052 this.data.sort(s.direction, fn);
14053 if(this.snapshot && this.snapshot != this.data){
14054 this.snapshot.sort(s.direction, fn);
14060 * Sets the default sort column and order to be used by the next load operation.
14061 * @param {String} fieldName The name of the field to sort by.
14062 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14064 setDefaultSort : function(field, dir){
14065 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
14069 * Sort the Records.
14070 * If remote sorting is used, the sort is performed on the server, and the cache is
14071 * reloaded. If local sorting is used, the cache is sorted internally.
14072 * @param {String} fieldName The name of the field to sort by.
14073 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14075 sort : function(fieldName, dir){
14076 var f = this.fields.get(fieldName);
14078 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
14080 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
14081 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
14086 this.sortToggle[f.name] = dir;
14087 this.sortInfo = {field: f.name, direction: dir};
14088 if(!this.remoteSort){
14090 this.fireEvent("datachanged", this);
14092 this.load(this.lastOptions);
14097 * Calls the specified function for each of the Records in the cache.
14098 * @param {Function} fn The function to call. The Record is passed as the first parameter.
14099 * Returning <em>false</em> aborts and exits the iteration.
14100 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
14102 each : function(fn, scope){
14103 this.data.each(fn, scope);
14107 * Gets all records modified since the last commit. Modified records are persisted across load operations
14108 * (e.g., during paging).
14109 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
14111 getModifiedRecords : function(){
14112 return this.modified;
14116 createFilterFn : function(property, value, anyMatch){
14117 if(!value.exec){ // not a regex
14118 value = String(value);
14119 if(value.length == 0){
14122 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
14124 return function(r){
14125 return value.test(r.data[property]);
14130 * Sums the value of <i>property</i> for each record between start and end and returns the result.
14131 * @param {String} property A field on your records
14132 * @param {Number} start The record index to start at (defaults to 0)
14133 * @param {Number} end The last record index to include (defaults to length - 1)
14134 * @return {Number} The sum
14136 sum : function(property, start, end){
14137 var rs = this.data.items, v = 0;
14138 start = start || 0;
14139 end = (end || end === 0) ? end : rs.length-1;
14141 for(var i = start; i <= end; i++){
14142 v += (rs[i].data[property] || 0);
14148 * Filter the records by a specified property.
14149 * @param {String} field A field on your records
14150 * @param {String/RegExp} value Either a string that the field
14151 * should start with or a RegExp to test against the field
14152 * @param {Boolean} anyMatch True to match any part not just the beginning
14154 filter : function(property, value, anyMatch){
14155 var fn = this.createFilterFn(property, value, anyMatch);
14156 return fn ? this.filterBy(fn) : this.clearFilter();
14160 * Filter by a function. The specified function will be called with each
14161 * record in this data source. If the function returns true the record is included,
14162 * otherwise it is filtered.
14163 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14164 * @param {Object} scope (optional) The scope of the function (defaults to this)
14166 filterBy : function(fn, scope){
14167 this.snapshot = this.snapshot || this.data;
14168 this.data = this.queryBy(fn, scope||this);
14169 this.fireEvent("datachanged", this);
14173 * Query the records by a specified property.
14174 * @param {String} field A field on your records
14175 * @param {String/RegExp} value Either a string that the field
14176 * should start with or a RegExp to test against the field
14177 * @param {Boolean} anyMatch True to match any part not just the beginning
14178 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14180 query : function(property, value, anyMatch){
14181 var fn = this.createFilterFn(property, value, anyMatch);
14182 return fn ? this.queryBy(fn) : this.data.clone();
14186 * Query by a function. The specified function will be called with each
14187 * record in this data source. If the function returns true the record is included
14189 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14190 * @param {Object} scope (optional) The scope of the function (defaults to this)
14191 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14193 queryBy : function(fn, scope){
14194 var data = this.snapshot || this.data;
14195 return data.filterBy(fn, scope||this);
14199 * Collects unique values for a particular dataIndex from this store.
14200 * @param {String} dataIndex The property to collect
14201 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
14202 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
14203 * @return {Array} An array of the unique values
14205 collect : function(dataIndex, allowNull, bypassFilter){
14206 var d = (bypassFilter === true && this.snapshot) ?
14207 this.snapshot.items : this.data.items;
14208 var v, sv, r = [], l = {};
14209 for(var i = 0, len = d.length; i < len; i++){
14210 v = d[i].data[dataIndex];
14212 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
14221 * Revert to a view of the Record cache with no filtering applied.
14222 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
14224 clearFilter : function(suppressEvent){
14225 if(this.snapshot && this.snapshot != this.data){
14226 this.data = this.snapshot;
14227 delete this.snapshot;
14228 if(suppressEvent !== true){
14229 this.fireEvent("datachanged", this);
14235 afterEdit : function(record){
14236 if(this.modified.indexOf(record) == -1){
14237 this.modified.push(record);
14239 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
14243 afterReject : function(record){
14244 this.modified.remove(record);
14245 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
14249 afterCommit : function(record){
14250 this.modified.remove(record);
14251 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
14255 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
14256 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
14258 commitChanges : function(){
14259 var m = this.modified.slice(0);
14260 this.modified = [];
14261 for(var i = 0, len = m.length; i < len; i++){
14267 * Cancel outstanding changes on all changed records.
14269 rejectChanges : function(){
14270 var m = this.modified.slice(0);
14271 this.modified = [];
14272 for(var i = 0, len = m.length; i < len; i++){
14277 onMetaChange : function(meta, rtype, o){
14278 this.recordType = rtype;
14279 this.fields = rtype.prototype.fields;
14280 delete this.snapshot;
14281 this.sortInfo = meta.sortInfo || this.sortInfo;
14282 this.modified = [];
14283 this.fireEvent('metachange', this, this.reader.meta);
14286 moveIndex : function(data, type)
14288 var index = this.indexOf(data);
14290 var newIndex = index + type;
14294 this.insert(newIndex, data);
14299 * Ext JS Library 1.1.1
14300 * Copyright(c) 2006-2007, Ext JS, LLC.
14302 * Originally Released Under LGPL - original licence link has changed is not relivant.
14305 * <script type="text/javascript">
14309 * @class Roo.data.SimpleStore
14310 * @extends Roo.data.Store
14311 * Small helper class to make creating Stores from Array data easier.
14312 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
14313 * @cfg {Array} fields An array of field definition objects, or field name strings.
14314 * @cfg {Object} an existing reader (eg. copied from another store)
14315 * @cfg {Array} data The multi-dimensional array of data
14317 * @param {Object} config
14319 Roo.data.SimpleStore = function(config)
14321 Roo.data.SimpleStore.superclass.constructor.call(this, {
14323 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
14326 Roo.data.Record.create(config.fields)
14328 proxy : new Roo.data.MemoryProxy(config.data)
14332 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
14334 * Ext JS Library 1.1.1
14335 * Copyright(c) 2006-2007, Ext JS, LLC.
14337 * Originally Released Under LGPL - original licence link has changed is not relivant.
14340 * <script type="text/javascript">
14345 * @extends Roo.data.Store
14346 * @class Roo.data.JsonStore
14347 * Small helper class to make creating Stores for JSON data easier. <br/>
14349 var store = new Roo.data.JsonStore({
14350 url: 'get-images.php',
14352 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14355 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14356 * JsonReader and HttpProxy (unless inline data is provided).</b>
14357 * @cfg {Array} fields An array of field definition objects, or field name strings.
14359 * @param {Object} config
14361 Roo.data.JsonStore = function(c){
14362 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14363 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14364 reader: new Roo.data.JsonReader(c, c.fields)
14367 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14369 * Ext JS Library 1.1.1
14370 * Copyright(c) 2006-2007, Ext JS, LLC.
14372 * Originally Released Under LGPL - original licence link has changed is not relivant.
14375 * <script type="text/javascript">
14379 Roo.data.Field = function(config){
14380 if(typeof config == "string"){
14381 config = {name: config};
14383 Roo.apply(this, config);
14386 this.type = "auto";
14389 var st = Roo.data.SortTypes;
14390 // named sortTypes are supported, here we look them up
14391 if(typeof this.sortType == "string"){
14392 this.sortType = st[this.sortType];
14395 // set default sortType for strings and dates
14396 if(!this.sortType){
14399 this.sortType = st.asUCString;
14402 this.sortType = st.asDate;
14405 this.sortType = st.none;
14410 var stripRe = /[\$,%]/g;
14412 // prebuilt conversion function for this field, instead of
14413 // switching every time we're reading a value
14415 var cv, dateFormat = this.dateFormat;
14420 cv = function(v){ return v; };
14423 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14427 return v !== undefined && v !== null && v !== '' ?
14428 parseInt(String(v).replace(stripRe, ""), 10) : '';
14433 return v !== undefined && v !== null && v !== '' ?
14434 parseFloat(String(v).replace(stripRe, ""), 10) : '';
14439 cv = function(v){ return v === true || v === "true" || v == 1; };
14446 if(v instanceof Date){
14450 if(dateFormat == "timestamp"){
14451 return new Date(v*1000);
14453 return Date.parseDate(v, dateFormat);
14455 var parsed = Date.parse(v);
14456 return parsed ? new Date(parsed) : null;
14465 Roo.data.Field.prototype = {
14473 * Ext JS Library 1.1.1
14474 * Copyright(c) 2006-2007, Ext JS, LLC.
14476 * Originally Released Under LGPL - original licence link has changed is not relivant.
14479 * <script type="text/javascript">
14482 // Base class for reading structured data from a data source. This class is intended to be
14483 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14486 * @class Roo.data.DataReader
14487 * Base class for reading structured data from a data source. This class is intended to be
14488 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14491 Roo.data.DataReader = function(meta, recordType){
14495 this.recordType = recordType instanceof Array ?
14496 Roo.data.Record.create(recordType) : recordType;
14499 Roo.data.DataReader.prototype = {
14502 readerType : 'Data',
14504 * Create an empty record
14505 * @param {Object} data (optional) - overlay some values
14506 * @return {Roo.data.Record} record created.
14508 newRow : function(d) {
14510 this.recordType.prototype.fields.each(function(c) {
14512 case 'int' : da[c.name] = 0; break;
14513 case 'date' : da[c.name] = new Date(); break;
14514 case 'float' : da[c.name] = 0.0; break;
14515 case 'boolean' : da[c.name] = false; break;
14516 default : da[c.name] = ""; break;
14520 return new this.recordType(Roo.apply(da, d));
14526 * Ext JS Library 1.1.1
14527 * Copyright(c) 2006-2007, Ext JS, LLC.
14529 * Originally Released Under LGPL - original licence link has changed is not relivant.
14532 * <script type="text/javascript">
14536 * @class Roo.data.DataProxy
14537 * @extends Roo.data.Observable
14538 * This class is an abstract base class for implementations which provide retrieval of
14539 * unformatted data objects.<br>
14541 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14542 * (of the appropriate type which knows how to parse the data object) to provide a block of
14543 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14545 * Custom implementations must implement the load method as described in
14546 * {@link Roo.data.HttpProxy#load}.
14548 Roo.data.DataProxy = function(){
14551 * @event beforeload
14552 * Fires before a network request is made to retrieve a data object.
14553 * @param {Object} This DataProxy object.
14554 * @param {Object} params The params parameter to the load function.
14559 * Fires before the load method's callback is called.
14560 * @param {Object} This DataProxy object.
14561 * @param {Object} o The data object.
14562 * @param {Object} arg The callback argument object passed to the load function.
14566 * @event loadexception
14567 * Fires if an Exception occurs during data retrieval.
14568 * @param {Object} This DataProxy object.
14569 * @param {Object} o The data object.
14570 * @param {Object} arg The callback argument object passed to the load function.
14571 * @param {Object} e The Exception.
14573 loadexception : true
14575 Roo.data.DataProxy.superclass.constructor.call(this);
14578 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14581 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14585 * Ext JS Library 1.1.1
14586 * Copyright(c) 2006-2007, Ext JS, LLC.
14588 * Originally Released Under LGPL - original licence link has changed is not relivant.
14591 * <script type="text/javascript">
14594 * @class Roo.data.MemoryProxy
14595 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14596 * to the Reader when its load method is called.
14598 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14600 Roo.data.MemoryProxy = function(data){
14604 Roo.data.MemoryProxy.superclass.constructor.call(this);
14608 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14611 * Load data from the requested source (in this case an in-memory
14612 * data object passed to the constructor), read the data object into
14613 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14614 * process that block using the passed callback.
14615 * @param {Object} params This parameter is not used by the MemoryProxy class.
14616 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14617 * object into a block of Roo.data.Records.
14618 * @param {Function} callback The function into which to pass the block of Roo.data.records.
14619 * The function must be passed <ul>
14620 * <li>The Record block object</li>
14621 * <li>The "arg" argument from the load function</li>
14622 * <li>A boolean success indicator</li>
14624 * @param {Object} scope The scope in which to call the callback
14625 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14627 load : function(params, reader, callback, scope, arg){
14628 params = params || {};
14631 result = reader.readRecords(params.data ? params.data :this.data);
14633 this.fireEvent("loadexception", this, arg, null, e);
14634 callback.call(scope, null, arg, false);
14637 callback.call(scope, result, arg, true);
14641 update : function(params, records){
14646 * Ext JS Library 1.1.1
14647 * Copyright(c) 2006-2007, Ext JS, LLC.
14649 * Originally Released Under LGPL - original licence link has changed is not relivant.
14652 * <script type="text/javascript">
14655 * @class Roo.data.HttpProxy
14656 * @extends Roo.data.DataProxy
14657 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14658 * configured to reference a certain URL.<br><br>
14660 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14661 * from which the running page was served.<br><br>
14663 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14665 * Be aware that to enable the browser to parse an XML document, the server must set
14666 * the Content-Type header in the HTTP response to "text/xml".
14668 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14669 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
14670 * will be used to make the request.
14672 Roo.data.HttpProxy = function(conn){
14673 Roo.data.HttpProxy.superclass.constructor.call(this);
14674 // is conn a conn config or a real conn?
14676 this.useAjax = !conn || !conn.events;
14680 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14681 // thse are take from connection...
14684 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14687 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14688 * extra parameters to each request made by this object. (defaults to undefined)
14691 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14692 * to each request made by this object. (defaults to undefined)
14695 * @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)
14698 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14701 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14707 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14711 * Return the {@link Roo.data.Connection} object being used by this Proxy.
14712 * @return {Connection} The Connection object. This object may be used to subscribe to events on
14713 * a finer-grained basis than the DataProxy events.
14715 getConnection : function(){
14716 return this.useAjax ? Roo.Ajax : this.conn;
14720 * Load data from the configured {@link Roo.data.Connection}, read the data object into
14721 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14722 * process that block using the passed callback.
14723 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14724 * for the request to the remote server.
14725 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14726 * object into a block of Roo.data.Records.
14727 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14728 * The function must be passed <ul>
14729 * <li>The Record block object</li>
14730 * <li>The "arg" argument from the load function</li>
14731 * <li>A boolean success indicator</li>
14733 * @param {Object} scope The scope in which to call the callback
14734 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14736 load : function(params, reader, callback, scope, arg){
14737 if(this.fireEvent("beforeload", this, params) !== false){
14739 params : params || {},
14741 callback : callback,
14746 callback : this.loadResponse,
14750 Roo.applyIf(o, this.conn);
14751 if(this.activeRequest){
14752 Roo.Ajax.abort(this.activeRequest);
14754 this.activeRequest = Roo.Ajax.request(o);
14756 this.conn.request(o);
14759 callback.call(scope||this, null, arg, false);
14764 loadResponse : function(o, success, response){
14765 delete this.activeRequest;
14767 this.fireEvent("loadexception", this, o, response);
14768 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14773 result = o.reader.read(response);
14775 this.fireEvent("loadexception", this, o, response, e);
14776 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14780 this.fireEvent("load", this, o, o.request.arg);
14781 o.request.callback.call(o.request.scope, result, o.request.arg, true);
14785 update : function(dataSet){
14790 updateResponse : function(dataSet){
14795 * Ext JS Library 1.1.1
14796 * Copyright(c) 2006-2007, Ext JS, LLC.
14798 * Originally Released Under LGPL - original licence link has changed is not relivant.
14801 * <script type="text/javascript">
14805 * @class Roo.data.ScriptTagProxy
14806 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14807 * other than the originating domain of the running page.<br><br>
14809 * <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
14810 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14812 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14813 * source code that is used as the source inside a <script> tag.<br><br>
14815 * In order for the browser to process the returned data, the server must wrap the data object
14816 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14817 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14818 * depending on whether the callback name was passed:
14821 boolean scriptTag = false;
14822 String cb = request.getParameter("callback");
14825 response.setContentType("text/javascript");
14827 response.setContentType("application/x-json");
14829 Writer out = response.getWriter();
14831 out.write(cb + "(");
14833 out.print(dataBlock.toJsonString());
14840 * @param {Object} config A configuration object.
14842 Roo.data.ScriptTagProxy = function(config){
14843 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14844 Roo.apply(this, config);
14845 this.head = document.getElementsByTagName("head")[0];
14848 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14850 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14852 * @cfg {String} url The URL from which to request the data object.
14855 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14859 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14860 * the server the name of the callback function set up by the load call to process the returned data object.
14861 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14862 * javascript output which calls this named function passing the data object as its only parameter.
14864 callbackParam : "callback",
14866 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14867 * name to the request.
14872 * Load data from the configured URL, read the data object into
14873 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14874 * process that block using the passed callback.
14875 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14876 * for the request to the remote server.
14877 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14878 * object into a block of Roo.data.Records.
14879 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14880 * The function must be passed <ul>
14881 * <li>The Record block object</li>
14882 * <li>The "arg" argument from the load function</li>
14883 * <li>A boolean success indicator</li>
14885 * @param {Object} scope The scope in which to call the callback
14886 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14888 load : function(params, reader, callback, scope, arg){
14889 if(this.fireEvent("beforeload", this, params) !== false){
14891 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14893 var url = this.url;
14894 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14896 url += "&_dc=" + (new Date().getTime());
14898 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14901 cb : "stcCallback"+transId,
14902 scriptId : "stcScript"+transId,
14906 callback : callback,
14912 window[trans.cb] = function(o){
14913 conn.handleResponse(o, trans);
14916 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14918 if(this.autoAbort !== false){
14922 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14924 var script = document.createElement("script");
14925 script.setAttribute("src", url);
14926 script.setAttribute("type", "text/javascript");
14927 script.setAttribute("id", trans.scriptId);
14928 this.head.appendChild(script);
14930 this.trans = trans;
14932 callback.call(scope||this, null, arg, false);
14937 isLoading : function(){
14938 return this.trans ? true : false;
14942 * Abort the current server request.
14944 abort : function(){
14945 if(this.isLoading()){
14946 this.destroyTrans(this.trans);
14951 destroyTrans : function(trans, isLoaded){
14952 this.head.removeChild(document.getElementById(trans.scriptId));
14953 clearTimeout(trans.timeoutId);
14955 window[trans.cb] = undefined;
14957 delete window[trans.cb];
14960 // if hasn't been loaded, wait for load to remove it to prevent script error
14961 window[trans.cb] = function(){
14962 window[trans.cb] = undefined;
14964 delete window[trans.cb];
14971 handleResponse : function(o, trans){
14972 this.trans = false;
14973 this.destroyTrans(trans, true);
14976 result = trans.reader.readRecords(o);
14978 this.fireEvent("loadexception", this, o, trans.arg, e);
14979 trans.callback.call(trans.scope||window, null, trans.arg, false);
14982 this.fireEvent("load", this, o, trans.arg);
14983 trans.callback.call(trans.scope||window, result, trans.arg, true);
14987 handleFailure : function(trans){
14988 this.trans = false;
14989 this.destroyTrans(trans, false);
14990 this.fireEvent("loadexception", this, null, trans.arg);
14991 trans.callback.call(trans.scope||window, null, trans.arg, false);
14995 * Ext JS Library 1.1.1
14996 * Copyright(c) 2006-2007, Ext JS, LLC.
14998 * Originally Released Under LGPL - original licence link has changed is not relivant.
15001 * <script type="text/javascript">
15005 * @class Roo.data.JsonReader
15006 * @extends Roo.data.DataReader
15007 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
15008 * based on mappings in a provided Roo.data.Record constructor.
15010 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
15011 * in the reply previously.
15016 var RecordDef = Roo.data.Record.create([
15017 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
15018 {name: 'occupation'} // This field will use "occupation" as the mapping.
15020 var myReader = new Roo.data.JsonReader({
15021 totalProperty: "results", // The property which contains the total dataset size (optional)
15022 root: "rows", // The property which contains an Array of row objects
15023 id: "id" // The property within each row object that provides an ID for the record (optional)
15027 * This would consume a JSON file like this:
15029 { 'results': 2, 'rows': [
15030 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
15031 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
15034 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
15035 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
15036 * paged from the remote server.
15037 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
15038 * @cfg {String} root name of the property which contains the Array of row objects.
15039 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15040 * @cfg {Array} fields Array of field definition objects
15042 * Create a new JsonReader
15043 * @param {Object} meta Metadata configuration options
15044 * @param {Object} recordType Either an Array of field definition objects,
15045 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
15047 Roo.data.JsonReader = function(meta, recordType){
15050 // set some defaults:
15051 Roo.applyIf(meta, {
15052 totalProperty: 'total',
15053 successProperty : 'success',
15058 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15060 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
15062 readerType : 'Json',
15065 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
15066 * Used by Store query builder to append _requestMeta to params.
15069 metaFromRemote : false,
15071 * This method is only used by a DataProxy which has retrieved data from a remote server.
15072 * @param {Object} response The XHR object which contains the JSON data in its responseText.
15073 * @return {Object} data A data block which is used by an Roo.data.Store object as
15074 * a cache of Roo.data.Records.
15076 read : function(response){
15077 var json = response.responseText;
15079 var o = /* eval:var:o */ eval("("+json+")");
15081 throw {message: "JsonReader.read: Json object not found"};
15087 this.metaFromRemote = true;
15088 this.meta = o.metaData;
15089 this.recordType = Roo.data.Record.create(o.metaData.fields);
15090 this.onMetaChange(this.meta, this.recordType, o);
15092 return this.readRecords(o);
15095 // private function a store will implement
15096 onMetaChange : function(meta, recordType, o){
15103 simpleAccess: function(obj, subsc) {
15110 getJsonAccessor: function(){
15112 return function(expr) {
15114 return(re.test(expr))
15115 ? new Function("obj", "return obj." + expr)
15120 return Roo.emptyFn;
15125 * Create a data block containing Roo.data.Records from an XML document.
15126 * @param {Object} o An object which contains an Array of row objects in the property specified
15127 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
15128 * which contains the total size of the dataset.
15129 * @return {Object} data A data block which is used by an Roo.data.Store object as
15130 * a cache of Roo.data.Records.
15132 readRecords : function(o){
15134 * After any data loads, the raw JSON data is available for further custom processing.
15138 var s = this.meta, Record = this.recordType,
15139 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
15141 // Generate extraction functions for the totalProperty, the root, the id, and for each field
15143 if(s.totalProperty) {
15144 this.getTotal = this.getJsonAccessor(s.totalProperty);
15146 if(s.successProperty) {
15147 this.getSuccess = this.getJsonAccessor(s.successProperty);
15149 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
15151 var g = this.getJsonAccessor(s.id);
15152 this.getId = function(rec) {
15154 return (r === undefined || r === "") ? null : r;
15157 this.getId = function(){return null;};
15160 for(var jj = 0; jj < fl; jj++){
15162 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
15163 this.ef[jj] = this.getJsonAccessor(map);
15167 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
15168 if(s.totalProperty){
15169 var vt = parseInt(this.getTotal(o), 10);
15174 if(s.successProperty){
15175 var vs = this.getSuccess(o);
15176 if(vs === false || vs === 'false'){
15181 for(var i = 0; i < c; i++){
15184 var id = this.getId(n);
15185 for(var j = 0; j < fl; j++){
15187 var v = this.ef[j](n);
15189 Roo.log('missing convert for ' + f.name);
15193 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
15195 var record = new Record(values, id);
15197 records[i] = record;
15203 totalRecords : totalRecords
15206 // used when loading children.. @see loadDataFromChildren
15207 toLoadData: function(rec)
15209 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15210 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15211 return { data : data, total : data.length };
15216 * Ext JS Library 1.1.1
15217 * Copyright(c) 2006-2007, Ext JS, LLC.
15219 * Originally Released Under LGPL - original licence link has changed is not relivant.
15222 * <script type="text/javascript">
15226 * @class Roo.data.ArrayReader
15227 * @extends Roo.data.DataReader
15228 * Data reader class to create an Array of Roo.data.Record objects from an Array.
15229 * Each element of that Array represents a row of data fields. The
15230 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
15231 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
15235 var RecordDef = Roo.data.Record.create([
15236 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
15237 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
15239 var myReader = new Roo.data.ArrayReader({
15240 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
15244 * This would consume an Array like this:
15246 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
15250 * Create a new JsonReader
15251 * @param {Object} meta Metadata configuration options.
15252 * @param {Object|Array} recordType Either an Array of field definition objects
15254 * @cfg {Array} fields Array of field definition objects
15255 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15256 * as specified to {@link Roo.data.Record#create},
15257 * or an {@link Roo.data.Record} object
15260 * created using {@link Roo.data.Record#create}.
15262 Roo.data.ArrayReader = function(meta, recordType)
15264 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15267 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
15270 * Create a data block containing Roo.data.Records from an XML document.
15271 * @param {Object} o An Array of row objects which represents the dataset.
15272 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
15273 * a cache of Roo.data.Records.
15275 readRecords : function(o)
15277 var sid = this.meta ? this.meta.id : null;
15278 var recordType = this.recordType, fields = recordType.prototype.fields;
15281 for(var i = 0; i < root.length; i++){
15284 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
15285 for(var j = 0, jlen = fields.length; j < jlen; j++){
15286 var f = fields.items[j];
15287 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
15288 var v = n[k] !== undefined ? n[k] : f.defaultValue;
15290 values[f.name] = v;
15292 var record = new recordType(values, id);
15294 records[records.length] = record;
15298 totalRecords : records.length
15301 // used when loading children.. @see loadDataFromChildren
15302 toLoadData: function(rec)
15304 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15305 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15316 * @class Roo.bootstrap.ComboBox
15317 * @extends Roo.bootstrap.TriggerField
15318 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
15319 * @cfg {Boolean} append (true|false) default false
15320 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
15321 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
15322 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
15323 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
15324 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
15325 * @cfg {Boolean} animate default true
15326 * @cfg {Boolean} emptyResultText only for touch device
15327 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15328 * @cfg {String} emptyTitle default ''
15329 * @cfg {Number} width fixed with? experimental
15331 * Create a new ComboBox.
15332 * @param {Object} config Configuration options
15334 Roo.bootstrap.ComboBox = function(config){
15335 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15339 * Fires when the dropdown list is expanded
15340 * @param {Roo.bootstrap.ComboBox} combo This combo box
15345 * Fires when the dropdown list is collapsed
15346 * @param {Roo.bootstrap.ComboBox} combo This combo box
15350 * @event beforeselect
15351 * Fires before a list item is selected. Return false to cancel the selection.
15352 * @param {Roo.bootstrap.ComboBox} combo This combo box
15353 * @param {Roo.data.Record} record The data record returned from the underlying store
15354 * @param {Number} index The index of the selected item in the dropdown list
15356 'beforeselect' : true,
15359 * Fires when a list item is selected
15360 * @param {Roo.bootstrap.ComboBox} combo This combo box
15361 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15362 * @param {Number} index The index of the selected item in the dropdown list
15366 * @event beforequery
15367 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15368 * The event object passed has these properties:
15369 * @param {Roo.bootstrap.ComboBox} combo This combo box
15370 * @param {String} query The query
15371 * @param {Boolean} forceAll true to force "all" query
15372 * @param {Boolean} cancel true to cancel the query
15373 * @param {Object} e The query event object
15375 'beforequery': true,
15378 * Fires when the 'add' icon is pressed (add a listener to enable add button)
15379 * @param {Roo.bootstrap.ComboBox} combo This combo box
15384 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15385 * @param {Roo.bootstrap.ComboBox} combo This combo box
15386 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15391 * Fires when the remove value from the combobox array
15392 * @param {Roo.bootstrap.ComboBox} combo This combo box
15396 * @event afterremove
15397 * Fires when the remove value from the combobox array
15398 * @param {Roo.bootstrap.ComboBox} combo This combo box
15400 'afterremove' : true,
15402 * @event specialfilter
15403 * Fires when specialfilter
15404 * @param {Roo.bootstrap.ComboBox} combo This combo box
15406 'specialfilter' : true,
15409 * Fires when tick the element
15410 * @param {Roo.bootstrap.ComboBox} combo This combo box
15414 * @event touchviewdisplay
15415 * Fires when touch view require special display (default is using displayField)
15416 * @param {Roo.bootstrap.ComboBox} combo This combo box
15417 * @param {Object} cfg set html .
15419 'touchviewdisplay' : true
15424 this.tickItems = [];
15426 this.selectedIndex = -1;
15427 if(this.mode == 'local'){
15428 if(config.queryDelay === undefined){
15429 this.queryDelay = 10;
15431 if(config.minChars === undefined){
15437 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15440 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15441 * rendering into an Roo.Editor, defaults to false)
15444 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15445 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15448 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15451 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15452 * the dropdown list (defaults to undefined, with no header element)
15456 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
15460 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15462 listWidth: undefined,
15464 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15465 * mode = 'remote' or 'text' if mode = 'local')
15467 displayField: undefined,
15470 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15471 * mode = 'remote' or 'value' if mode = 'local').
15472 * Note: use of a valueField requires the user make a selection
15473 * in order for a value to be mapped.
15475 valueField: undefined,
15477 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15482 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15483 * field's data value (defaults to the underlying DOM element's name)
15485 hiddenName: undefined,
15487 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15491 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15493 selectedClass: 'active',
15496 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15500 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15501 * anchor positions (defaults to 'tl-bl')
15503 listAlign: 'tl-bl?',
15505 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15509 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
15510 * query specified by the allQuery config option (defaults to 'query')
15512 triggerAction: 'query',
15514 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15515 * (defaults to 4, does not apply if editable = false)
15519 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15520 * delay (typeAheadDelay) if it matches a known value (defaults to false)
15524 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15525 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15529 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15530 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
15534 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
15535 * when editable = true (defaults to false)
15537 selectOnFocus:false,
15539 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15541 queryParam: 'query',
15543 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
15544 * when mode = 'remote' (defaults to 'Loading...')
15546 loadingText: 'Loading...',
15548 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15552 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15556 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15557 * traditional select (defaults to true)
15561 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15565 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15569 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15570 * listWidth has a higher value)
15574 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15575 * allow the user to set arbitrary text into the field (defaults to false)
15577 forceSelection:false,
15579 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15580 * if typeAhead = true (defaults to 250)
15582 typeAheadDelay : 250,
15584 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15585 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15587 valueNotFoundText : undefined,
15589 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15591 blockFocus : false,
15594 * @cfg {Boolean} disableClear Disable showing of clear button.
15596 disableClear : false,
15598 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
15600 alwaysQuery : false,
15603 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
15608 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15610 invalidClass : "has-warning",
15613 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15615 validClass : "has-success",
15618 * @cfg {Boolean} specialFilter (true|false) special filter default false
15620 specialFilter : false,
15623 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15625 mobileTouchView : true,
15628 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15630 useNativeIOS : false,
15633 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15635 mobile_restrict_height : false,
15637 ios_options : false,
15649 btnPosition : 'right',
15650 triggerList : true,
15651 showToggleBtn : true,
15653 emptyResultText: 'Empty',
15654 triggerText : 'Select',
15658 // element that contains real text value.. (when hidden is used..)
15660 getAutoCreate : function()
15665 * Render classic select for iso
15668 if(Roo.isIOS && this.useNativeIOS){
15669 cfg = this.getAutoCreateNativeIOS();
15677 if(Roo.isTouch && this.mobileTouchView){
15678 cfg = this.getAutoCreateTouchView();
15685 if(!this.tickable){
15686 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15691 * ComboBox with tickable selections
15694 var align = this.labelAlign || this.parentLabelAlign();
15697 cls : 'form-group roo-combobox-tickable' //input-group
15700 var btn_text_select = '';
15701 var btn_text_done = '';
15702 var btn_text_cancel = '';
15704 if (this.btn_text_show) {
15705 btn_text_select = 'Select';
15706 btn_text_done = 'Done';
15707 btn_text_cancel = 'Cancel';
15712 cls : 'tickable-buttons',
15717 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15718 //html : this.triggerText
15719 html: btn_text_select
15725 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15727 html: btn_text_done
15733 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15735 html: btn_text_cancel
15741 buttons.cn.unshift({
15743 cls: 'roo-select2-search-field-input'
15749 Roo.each(buttons.cn, function(c){
15751 c.cls += ' btn-' + _this.size;
15754 if (_this.disabled) {
15761 style : 'display: contents',
15766 cls: 'form-hidden-field'
15770 cls: 'roo-select2-choices',
15774 cls: 'roo-select2-search-field',
15785 cls: 'roo-select2-container input-group roo-select2-container-multi',
15791 // cls: 'typeahead typeahead-long dropdown-menu',
15792 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
15797 if(this.hasFeedback && !this.allowBlank){
15801 cls: 'glyphicon form-control-feedback'
15804 combobox.cn.push(feedback);
15811 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15812 tooltip : 'This field is required'
15814 if (Roo.bootstrap.version == 4) {
15817 style : 'display:none'
15820 if (align ==='left' && this.fieldLabel.length) {
15822 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
15829 cls : 'control-label col-form-label',
15830 html : this.fieldLabel
15842 var labelCfg = cfg.cn[1];
15843 var contentCfg = cfg.cn[2];
15846 if(this.indicatorpos == 'right'){
15852 cls : 'control-label col-form-label',
15856 html : this.fieldLabel
15872 labelCfg = cfg.cn[0];
15873 contentCfg = cfg.cn[1];
15877 if(this.labelWidth > 12){
15878 labelCfg.style = "width: " + this.labelWidth + 'px';
15880 if(this.width * 1 > 0){
15881 contentCfg.style = "width: " + this.width + 'px';
15883 if(this.labelWidth < 13 && this.labelmd == 0){
15884 this.labelmd = this.labelWidth;
15887 if(this.labellg > 0){
15888 labelCfg.cls += ' col-lg-' + this.labellg;
15889 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15892 if(this.labelmd > 0){
15893 labelCfg.cls += ' col-md-' + this.labelmd;
15894 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15897 if(this.labelsm > 0){
15898 labelCfg.cls += ' col-sm-' + this.labelsm;
15899 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15902 if(this.labelxs > 0){
15903 labelCfg.cls += ' col-xs-' + this.labelxs;
15904 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15908 } else if ( this.fieldLabel.length) {
15909 // Roo.log(" label");
15914 //cls : 'input-group-addon',
15915 html : this.fieldLabel
15920 if(this.indicatorpos == 'right'){
15924 //cls : 'input-group-addon',
15925 html : this.fieldLabel
15935 // Roo.log(" no label && no align");
15942 ['xs','sm','md','lg'].map(function(size){
15943 if (settings[size]) {
15944 cfg.cls += ' col-' + size + '-' + settings[size];
15952 _initEventsCalled : false,
15955 initEvents: function()
15957 if (this._initEventsCalled) { // as we call render... prevent looping...
15960 this._initEventsCalled = true;
15963 throw "can not find store for combo";
15966 this.indicator = this.indicatorEl();
15968 this.store = Roo.factory(this.store, Roo.data);
15969 this.store.parent = this;
15971 // if we are building from html. then this element is so complex, that we can not really
15972 // use the rendered HTML.
15973 // so we have to trash and replace the previous code.
15974 if (Roo.XComponent.build_from_html) {
15975 // remove this element....
15976 var e = this.el.dom, k=0;
15977 while (e ) { e = e.previousSibling; ++k;}
15982 this.rendered = false;
15984 this.render(this.parent().getChildContainer(true), k);
15987 if(Roo.isIOS && this.useNativeIOS){
15988 this.initIOSView();
15996 if(Roo.isTouch && this.mobileTouchView){
15997 this.initTouchView();
16002 this.initTickableEvents();
16006 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
16008 if(this.hiddenName){
16010 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16012 this.hiddenField.dom.value =
16013 this.hiddenValue !== undefined ? this.hiddenValue :
16014 this.value !== undefined ? this.value : '';
16016 // prevent input submission
16017 this.el.dom.removeAttribute('name');
16018 this.hiddenField.dom.setAttribute('name', this.hiddenName);
16023 // this.el.dom.setAttribute('autocomplete', 'off');
16026 var cls = 'x-combo-list';
16028 //this.list = new Roo.Layer({
16029 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
16035 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16036 _this.list.setWidth(lw);
16039 this.list.on('mouseover', this.onViewOver, this);
16040 this.list.on('mousemove', this.onViewMove, this);
16041 this.list.on('scroll', this.onViewScroll, this);
16044 this.list.swallowEvent('mousewheel');
16045 this.assetHeight = 0;
16048 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
16049 this.assetHeight += this.header.getHeight();
16052 this.innerList = this.list.createChild({cls:cls+'-inner'});
16053 this.innerList.on('mouseover', this.onViewOver, this);
16054 this.innerList.on('mousemove', this.onViewMove, this);
16055 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16057 if(this.allowBlank && !this.pageSize && !this.disableClear){
16058 this.footer = this.list.createChild({cls:cls+'-ft'});
16059 this.pageTb = new Roo.Toolbar(this.footer);
16063 this.footer = this.list.createChild({cls:cls+'-ft'});
16064 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
16065 {pageSize: this.pageSize});
16069 if (this.pageTb && this.allowBlank && !this.disableClear) {
16071 this.pageTb.add(new Roo.Toolbar.Fill(), {
16072 cls: 'x-btn-icon x-btn-clear',
16074 handler: function()
16077 _this.clearValue();
16078 _this.onSelect(false, -1);
16083 this.assetHeight += this.footer.getHeight();
16088 this.tpl = Roo.bootstrap.version == 4 ?
16089 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
16090 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
16093 this.view = new Roo.View(this.list, this.tpl, {
16094 singleSelect:true, store: this.store, selectedClass: this.selectedClass
16096 //this.view.wrapEl.setDisplayed(false);
16097 this.view.on('click', this.onViewClick, this);
16100 this.store.on('beforeload', this.onBeforeLoad, this);
16101 this.store.on('load', this.onLoad, this);
16102 this.store.on('loadexception', this.onLoadException, this);
16104 if(this.resizable){
16105 this.resizer = new Roo.Resizable(this.list, {
16106 pinned:true, handles:'se'
16108 this.resizer.on('resize', function(r, w, h){
16109 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
16110 this.listWidth = w;
16111 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
16112 this.restrictHeight();
16114 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
16117 if(!this.editable){
16118 this.editable = true;
16119 this.setEditable(false);
16124 if (typeof(this.events.add.listeners) != 'undefined') {
16126 this.addicon = this.wrap.createChild(
16127 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
16129 this.addicon.on('click', function(e) {
16130 this.fireEvent('add', this);
16133 if (typeof(this.events.edit.listeners) != 'undefined') {
16135 this.editicon = this.wrap.createChild(
16136 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
16137 if (this.addicon) {
16138 this.editicon.setStyle('margin-left', '40px');
16140 this.editicon.on('click', function(e) {
16142 // we fire even if inothing is selected..
16143 this.fireEvent('edit', this, this.lastData );
16149 this.keyNav = new Roo.KeyNav(this.inputEl(), {
16150 "up" : function(e){
16151 this.inKeyMode = true;
16155 "down" : function(e){
16156 if(!this.isExpanded()){
16157 this.onTriggerClick();
16159 this.inKeyMode = true;
16164 "enter" : function(e){
16165 // this.onViewClick();
16169 if(this.fireEvent("specialkey", this, e)){
16170 this.onViewClick(false);
16176 "esc" : function(e){
16180 "tab" : function(e){
16183 if(this.fireEvent("specialkey", this, e)){
16184 this.onViewClick(false);
16192 doRelay : function(foo, bar, hname){
16193 if(hname == 'down' || this.scope.isExpanded()){
16194 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16203 this.queryDelay = Math.max(this.queryDelay || 10,
16204 this.mode == 'local' ? 10 : 250);
16207 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16209 if(this.typeAhead){
16210 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16212 if(this.editable !== false){
16213 this.inputEl().on("keyup", this.onKeyUp, this);
16215 if(this.forceSelection){
16216 this.inputEl().on('blur', this.doForce, this);
16220 this.choices = this.el.select('ul.roo-select2-choices', true).first();
16221 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16225 initTickableEvents: function()
16229 if(this.hiddenName){
16231 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16233 this.hiddenField.dom.value =
16234 this.hiddenValue !== undefined ? this.hiddenValue :
16235 this.value !== undefined ? this.value : '';
16237 // prevent input submission
16238 this.el.dom.removeAttribute('name');
16239 this.hiddenField.dom.setAttribute('name', this.hiddenName);
16244 // this.list = this.el.select('ul.dropdown-menu',true).first();
16246 this.choices = this.el.select('ul.roo-select2-choices', true).first();
16247 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16248 if(this.triggerList){
16249 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
16252 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
16253 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
16255 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
16256 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
16258 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
16259 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
16261 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
16262 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
16263 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
16266 this.cancelBtn.hide();
16271 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16272 _this.list.setWidth(lw);
16275 this.list.on('mouseover', this.onViewOver, this);
16276 this.list.on('mousemove', this.onViewMove, this);
16278 this.list.on('scroll', this.onViewScroll, this);
16281 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
16282 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
16285 this.view = new Roo.View(this.list, this.tpl, {
16290 selectedClass: this.selectedClass
16293 //this.view.wrapEl.setDisplayed(false);
16294 this.view.on('click', this.onViewClick, this);
16298 this.store.on('beforeload', this.onBeforeLoad, this);
16299 this.store.on('load', this.onLoad, this);
16300 this.store.on('loadexception', this.onLoadException, this);
16303 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
16304 "up" : function(e){
16305 this.inKeyMode = true;
16309 "down" : function(e){
16310 this.inKeyMode = true;
16314 "enter" : function(e){
16315 if(this.fireEvent("specialkey", this, e)){
16316 this.onViewClick(false);
16322 "esc" : function(e){
16323 this.onTickableFooterButtonClick(e, false, false);
16326 "tab" : function(e){
16327 this.fireEvent("specialkey", this, e);
16329 this.onTickableFooterButtonClick(e, false, false);
16336 doRelay : function(e, fn, key){
16337 if(this.scope.isExpanded()){
16338 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16347 this.queryDelay = Math.max(this.queryDelay || 10,
16348 this.mode == 'local' ? 10 : 250);
16351 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16353 if(this.typeAhead){
16354 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16357 if(this.editable !== false){
16358 this.tickableInputEl().on("keyup", this.onKeyUp, this);
16361 this.indicator = this.indicatorEl();
16363 if(this.indicator){
16364 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16365 this.indicator.hide();
16370 onDestroy : function(){
16372 this.view.setStore(null);
16373 this.view.el.removeAllListeners();
16374 this.view.el.remove();
16375 this.view.purgeListeners();
16378 this.list.dom.innerHTML = '';
16382 this.store.un('beforeload', this.onBeforeLoad, this);
16383 this.store.un('load', this.onLoad, this);
16384 this.store.un('loadexception', this.onLoadException, this);
16386 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16390 fireKey : function(e){
16391 if(e.isNavKeyPress() && !this.list.isVisible()){
16392 this.fireEvent("specialkey", this, e);
16397 onResize: function(w, h)
16401 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16403 // if(typeof w != 'number'){
16404 // // we do not handle it!?!?
16407 // var tw = this.trigger.getWidth();
16408 // // tw += this.addicon ? this.addicon.getWidth() : 0;
16409 // // tw += this.editicon ? this.editicon.getWidth() : 0;
16411 // this.inputEl().setWidth( this.adjustWidth('input', x));
16413 // //this.trigger.setStyle('left', x+'px');
16415 // if(this.list && this.listWidth === undefined){
16416 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16417 // this.list.setWidth(lw);
16418 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16426 * Allow or prevent the user from directly editing the field text. If false is passed,
16427 * the user will only be able to select from the items defined in the dropdown list. This method
16428 * is the runtime equivalent of setting the 'editable' config option at config time.
16429 * @param {Boolean} value True to allow the user to directly edit the field text
16431 setEditable : function(value){
16432 if(value == this.editable){
16435 this.editable = value;
16437 this.inputEl().dom.setAttribute('readOnly', true);
16438 this.inputEl().on('mousedown', this.onTriggerClick, this);
16439 this.inputEl().addClass('x-combo-noedit');
16441 this.inputEl().dom.removeAttribute('readOnly');
16442 this.inputEl().un('mousedown', this.onTriggerClick, this);
16443 this.inputEl().removeClass('x-combo-noedit');
16449 onBeforeLoad : function(combo,opts){
16450 if(!this.hasFocus){
16454 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16456 this.restrictHeight();
16457 this.selectedIndex = -1;
16461 onLoad : function(){
16463 this.hasQuery = false;
16465 if(!this.hasFocus){
16469 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16470 this.loading.hide();
16473 if(this.store.getCount() > 0){
16476 this.restrictHeight();
16477 if(this.lastQuery == this.allQuery){
16478 if(this.editable && !this.tickable){
16479 this.inputEl().dom.select();
16483 !this.selectByValue(this.value, true) &&
16486 !this.store.lastOptions ||
16487 typeof(this.store.lastOptions.add) == 'undefined' ||
16488 this.store.lastOptions.add != true
16491 this.select(0, true);
16494 if(this.autoFocus){
16497 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16498 this.taTask.delay(this.typeAheadDelay);
16502 this.onEmptyResults();
16508 onLoadException : function()
16510 this.hasQuery = false;
16512 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16513 this.loading.hide();
16516 if(this.tickable && this.editable){
16521 // only causes errors at present
16522 //Roo.log(this.store.reader.jsonData);
16523 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16525 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16531 onTypeAhead : function(){
16532 if(this.store.getCount() > 0){
16533 var r = this.store.getAt(0);
16534 var newValue = r.data[this.displayField];
16535 var len = newValue.length;
16536 var selStart = this.getRawValue().length;
16538 if(selStart != len){
16539 this.setRawValue(newValue);
16540 this.selectText(selStart, newValue.length);
16546 onSelect : function(record, index){
16548 if(this.fireEvent('beforeselect', this, record, index) !== false){
16550 this.setFromData(index > -1 ? record.data : false);
16553 this.fireEvent('select', this, record, index);
16558 * Returns the currently selected field value or empty string if no value is set.
16559 * @return {String} value The selected value
16561 getValue : function()
16563 if(Roo.isIOS && this.useNativeIOS){
16564 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16568 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16571 if(this.valueField){
16572 return typeof this.value != 'undefined' ? this.value : '';
16574 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16578 getRawValue : function()
16580 if(Roo.isIOS && this.useNativeIOS){
16581 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16584 var v = this.inputEl().getValue();
16590 * Clears any text/value currently set in the field
16592 clearValue : function(){
16594 if(this.hiddenField){
16595 this.hiddenField.dom.value = '';
16598 this.setRawValue('');
16599 this.lastSelectionText = '';
16600 this.lastData = false;
16602 var close = this.closeTriggerEl();
16613 * Sets the specified value into the field. If the value finds a match, the corresponding record text
16614 * will be displayed in the field. If the value does not match the data value of an existing item,
16615 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16616 * Otherwise the field will be blank (although the value will still be set).
16617 * @param {String} value The value to match
16619 setValue : function(v)
16621 if(Roo.isIOS && this.useNativeIOS){
16622 this.setIOSValue(v);
16632 if(this.valueField){
16633 var r = this.findRecord(this.valueField, v);
16635 text = r.data[this.displayField];
16636 }else if(this.valueNotFoundText !== undefined){
16637 text = this.valueNotFoundText;
16640 this.lastSelectionText = text;
16641 if(this.hiddenField){
16642 this.hiddenField.dom.value = v;
16644 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16647 var close = this.closeTriggerEl();
16650 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16656 * @property {Object} the last set data for the element
16661 * Sets the value of the field based on a object which is related to the record format for the store.
16662 * @param {Object} value the value to set as. or false on reset?
16664 setFromData : function(o){
16671 var dv = ''; // display value
16672 var vv = ''; // value value..
16674 if (this.displayField) {
16675 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16677 // this is an error condition!!!
16678 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16681 if(this.valueField){
16682 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16685 var close = this.closeTriggerEl();
16688 if(dv.length || vv * 1 > 0){
16690 this.blockFocus=true;
16696 if(this.hiddenField){
16697 this.hiddenField.dom.value = vv;
16699 this.lastSelectionText = dv;
16700 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16704 // no hidden field.. - we store the value in 'value', but still display
16705 // display field!!!!
16706 this.lastSelectionText = dv;
16707 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16714 reset : function(){
16715 // overridden so that last data is reset..
16722 this.setValue(this.originalValue);
16723 //this.clearInvalid();
16724 this.lastData = false;
16726 this.view.clearSelections();
16732 findRecord : function(prop, value){
16734 if(this.store.getCount() > 0){
16735 this.store.each(function(r){
16736 if(r.data[prop] == value){
16746 getName: function()
16748 // returns hidden if it's set..
16749 if (!this.rendered) {return ''};
16750 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
16754 onViewMove : function(e, t){
16755 this.inKeyMode = false;
16759 onViewOver : function(e, t){
16760 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16763 var item = this.view.findItemFromChild(t);
16766 var index = this.view.indexOf(item);
16767 this.select(index, false);
16772 onViewClick : function(view, doFocus, el, e)
16774 var index = this.view.getSelectedIndexes()[0];
16776 var r = this.store.getAt(index);
16780 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16787 Roo.each(this.tickItems, function(v,k){
16789 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16791 _this.tickItems.splice(k, 1);
16793 if(typeof(e) == 'undefined' && view == false){
16794 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16806 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16807 this.tickItems.push(r.data);
16810 if(typeof(e) == 'undefined' && view == false){
16811 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16818 this.onSelect(r, index);
16820 if(doFocus !== false && !this.blockFocus){
16821 this.inputEl().focus();
16826 restrictHeight : function(){
16827 //this.innerList.dom.style.height = '';
16828 //var inner = this.innerList.dom;
16829 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16830 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16831 //this.list.beginUpdate();
16832 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16833 this.list.alignTo(this.inputEl(), this.listAlign);
16834 this.list.alignTo(this.inputEl(), this.listAlign);
16835 //this.list.endUpdate();
16839 onEmptyResults : function(){
16841 if(this.tickable && this.editable){
16842 this.hasFocus = false;
16843 this.restrictHeight();
16851 * Returns true if the dropdown list is expanded, else false.
16853 isExpanded : function(){
16854 return this.list.isVisible();
16858 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16859 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16860 * @param {String} value The data value of the item to select
16861 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16862 * selected item if it is not currently in view (defaults to true)
16863 * @return {Boolean} True if the value matched an item in the list, else false
16865 selectByValue : function(v, scrollIntoView){
16866 if(v !== undefined && v !== null){
16867 var r = this.findRecord(this.valueField || this.displayField, v);
16869 this.select(this.store.indexOf(r), scrollIntoView);
16877 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16878 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16879 * @param {Number} index The zero-based index of the list item to select
16880 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16881 * selected item if it is not currently in view (defaults to true)
16883 select : function(index, scrollIntoView){
16884 this.selectedIndex = index;
16885 this.view.select(index);
16886 if(scrollIntoView !== false){
16887 var el = this.view.getNode(index);
16889 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16892 this.list.scrollChildIntoView(el, false);
16898 selectNext : function(){
16899 var ct = this.store.getCount();
16901 if(this.selectedIndex == -1){
16903 }else if(this.selectedIndex < ct-1){
16904 this.select(this.selectedIndex+1);
16910 selectPrev : function(){
16911 var ct = this.store.getCount();
16913 if(this.selectedIndex == -1){
16915 }else if(this.selectedIndex != 0){
16916 this.select(this.selectedIndex-1);
16922 onKeyUp : function(e){
16923 if(this.editable !== false && !e.isSpecialKey()){
16924 this.lastKey = e.getKey();
16925 this.dqTask.delay(this.queryDelay);
16930 validateBlur : function(){
16931 return !this.list || !this.list.isVisible();
16935 initQuery : function(){
16937 var v = this.getRawValue();
16939 if(this.tickable && this.editable){
16940 v = this.tickableInputEl().getValue();
16947 doForce : function(){
16948 if(this.inputEl().dom.value.length > 0){
16949 this.inputEl().dom.value =
16950 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16956 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
16957 * query allowing the query action to be canceled if needed.
16958 * @param {String} query The SQL query to execute
16959 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16960 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
16961 * saved in the current store (defaults to false)
16963 doQuery : function(q, forceAll){
16965 if(q === undefined || q === null){
16970 forceAll: forceAll,
16974 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16979 forceAll = qe.forceAll;
16980 if(forceAll === true || (q.length >= this.minChars)){
16982 this.hasQuery = true;
16984 if(this.lastQuery != q || this.alwaysQuery){
16985 this.lastQuery = q;
16986 if(this.mode == 'local'){
16987 this.selectedIndex = -1;
16989 this.store.clearFilter();
16992 if(this.specialFilter){
16993 this.fireEvent('specialfilter', this);
16998 this.store.filter(this.displayField, q);
17001 this.store.fireEvent("datachanged", this.store);
17008 this.store.baseParams[this.queryParam] = q;
17010 var options = {params : this.getParams(q)};
17013 options.add = true;
17014 options.params.start = this.page * this.pageSize;
17017 this.store.load(options);
17020 * this code will make the page width larger, at the beginning, the list not align correctly,
17021 * we should expand the list on onLoad
17022 * so command out it
17027 this.selectedIndex = -1;
17032 this.loadNext = false;
17036 getParams : function(q){
17038 //p[this.queryParam] = q;
17042 p.limit = this.pageSize;
17048 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
17050 collapse : function(){
17051 if(!this.isExpanded()){
17057 this.hasFocus = false;
17061 this.cancelBtn.hide();
17062 this.trigger.show();
17065 this.tickableInputEl().dom.value = '';
17066 this.tickableInputEl().blur();
17071 Roo.get(document).un('mousedown', this.collapseIf, this);
17072 Roo.get(document).un('mousewheel', this.collapseIf, this);
17073 if (!this.editable) {
17074 Roo.get(document).un('keydown', this.listKeyPress, this);
17076 this.fireEvent('collapse', this);
17082 collapseIf : function(e){
17083 var in_combo = e.within(this.el);
17084 var in_list = e.within(this.list);
17085 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
17087 if (in_combo || in_list || is_list) {
17088 //e.stopPropagation();
17093 this.onTickableFooterButtonClick(e, false, false);
17101 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
17103 expand : function(){
17105 if(this.isExpanded() || !this.hasFocus){
17109 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
17110 this.list.setWidth(lw);
17116 this.restrictHeight();
17120 this.tickItems = Roo.apply([], this.item);
17123 this.cancelBtn.show();
17124 this.trigger.hide();
17127 this.tickableInputEl().focus();
17132 Roo.get(document).on('mousedown', this.collapseIf, this);
17133 Roo.get(document).on('mousewheel', this.collapseIf, this);
17134 if (!this.editable) {
17135 Roo.get(document).on('keydown', this.listKeyPress, this);
17138 this.fireEvent('expand', this);
17142 // Implements the default empty TriggerField.onTriggerClick function
17143 onTriggerClick : function(e)
17145 Roo.log('trigger click');
17147 if(this.disabled || !this.triggerList){
17152 this.loadNext = false;
17154 if(this.isExpanded()){
17156 if (!this.blockFocus) {
17157 this.inputEl().focus();
17161 this.hasFocus = true;
17162 if(this.triggerAction == 'all') {
17163 this.doQuery(this.allQuery, true);
17165 this.doQuery(this.getRawValue());
17167 if (!this.blockFocus) {
17168 this.inputEl().focus();
17173 onTickableTriggerClick : function(e)
17180 this.loadNext = false;
17181 this.hasFocus = true;
17183 if(this.triggerAction == 'all') {
17184 this.doQuery(this.allQuery, true);
17186 this.doQuery(this.getRawValue());
17190 onSearchFieldClick : function(e)
17192 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
17193 this.onTickableFooterButtonClick(e, false, false);
17197 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
17202 this.loadNext = false;
17203 this.hasFocus = true;
17205 if(this.triggerAction == 'all') {
17206 this.doQuery(this.allQuery, true);
17208 this.doQuery(this.getRawValue());
17212 listKeyPress : function(e)
17214 //Roo.log('listkeypress');
17215 // scroll to first matching element based on key pres..
17216 if (e.isSpecialKey()) {
17219 var k = String.fromCharCode(e.getKey()).toUpperCase();
17222 var csel = this.view.getSelectedNodes();
17223 var cselitem = false;
17225 var ix = this.view.indexOf(csel[0]);
17226 cselitem = this.store.getAt(ix);
17227 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
17233 this.store.each(function(v) {
17235 // start at existing selection.
17236 if (cselitem.id == v.id) {
17242 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
17243 match = this.store.indexOf(v);
17249 if (match === false) {
17250 return true; // no more action?
17253 this.view.select(match);
17254 var sn = Roo.get(this.view.getSelectedNodes()[0]);
17255 sn.scrollIntoView(sn.dom.parentNode, false);
17258 onViewScroll : function(e, t){
17260 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){
17264 this.hasQuery = true;
17266 this.loading = this.list.select('.loading', true).first();
17268 if(this.loading === null){
17269 this.list.createChild({
17271 cls: 'loading roo-select2-more-results roo-select2-active',
17272 html: 'Loading more results...'
17275 this.loading = this.list.select('.loading', true).first();
17277 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
17279 this.loading.hide();
17282 this.loading.show();
17287 this.loadNext = true;
17289 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
17294 addItem : function(o)
17296 var dv = ''; // display value
17298 if (this.displayField) {
17299 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17301 // this is an error condition!!!
17302 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
17309 var choice = this.choices.createChild({
17311 cls: 'roo-select2-search-choice',
17320 cls: 'roo-select2-search-choice-close fa fa-times',
17325 }, this.searchField);
17327 var close = choice.select('a.roo-select2-search-choice-close', true).first();
17329 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17337 this.inputEl().dom.value = '';
17342 onRemoveItem : function(e, _self, o)
17344 e.preventDefault();
17346 this.lastItem = Roo.apply([], this.item);
17348 var index = this.item.indexOf(o.data) * 1;
17351 Roo.log('not this item?!');
17355 this.item.splice(index, 1);
17360 this.fireEvent('remove', this, e);
17366 syncValue : function()
17368 if(!this.item.length){
17375 Roo.each(this.item, function(i){
17376 if(_this.valueField){
17377 value.push(i[_this.valueField]);
17384 this.value = value.join(',');
17386 if(this.hiddenField){
17387 this.hiddenField.dom.value = this.value;
17390 this.store.fireEvent("datachanged", this.store);
17395 clearItem : function()
17397 if(!this.multiple){
17403 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17411 if(this.tickable && !Roo.isTouch){
17412 this.view.refresh();
17416 inputEl: function ()
17418 if(Roo.isIOS && this.useNativeIOS){
17419 return this.el.select('select.roo-ios-select', true).first();
17422 if(Roo.isTouch && this.mobileTouchView){
17423 return this.el.select('input.form-control',true).first();
17427 return this.searchField;
17430 return this.el.select('input.form-control',true).first();
17433 onTickableFooterButtonClick : function(e, btn, el)
17435 e.preventDefault();
17437 this.lastItem = Roo.apply([], this.item);
17439 if(btn && btn.name == 'cancel'){
17440 this.tickItems = Roo.apply([], this.item);
17449 Roo.each(this.tickItems, function(o){
17457 validate : function()
17459 if(this.getVisibilityEl().hasClass('hidden')){
17463 var v = this.getRawValue();
17466 v = this.getValue();
17469 if(this.disabled || this.allowBlank || v.length){
17474 this.markInvalid();
17478 tickableInputEl : function()
17480 if(!this.tickable || !this.editable){
17481 return this.inputEl();
17484 return this.inputEl().select('.roo-select2-search-field-input', true).first();
17488 getAutoCreateTouchView : function()
17493 cls: 'form-group' //input-group
17499 type : this.inputType,
17500 cls : 'form-control x-combo-noedit',
17501 autocomplete: 'new-password',
17502 placeholder : this.placeholder || '',
17507 input.name = this.name;
17511 input.cls += ' input-' + this.size;
17514 if (this.disabled) {
17515 input.disabled = true;
17519 cls : 'roo-combobox-wrap',
17526 inputblock.cls += ' input-group';
17528 inputblock.cn.unshift({
17530 cls : 'input-group-addon input-group-prepend input-group-text',
17535 if(this.removable && !this.multiple){
17536 inputblock.cls += ' roo-removable';
17538 inputblock.cn.push({
17541 cls : 'roo-combo-removable-btn close'
17545 if(this.hasFeedback && !this.allowBlank){
17547 inputblock.cls += ' has-feedback';
17549 inputblock.cn.push({
17551 cls: 'glyphicon form-control-feedback'
17558 inputblock.cls += (this.before) ? '' : ' input-group';
17560 inputblock.cn.push({
17562 cls : 'input-group-addon input-group-append input-group-text',
17568 var ibwrap = inputblock;
17573 cls: 'roo-select2-choices',
17577 cls: 'roo-select2-search-field',
17590 cls: 'roo-select2-container input-group roo-touchview-combobox ',
17595 cls: 'form-hidden-field'
17601 if(!this.multiple && this.showToggleBtn){
17607 if (this.caret != false) {
17610 cls: 'fa fa-' + this.caret
17617 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17619 Roo.bootstrap.version == 3 ? caret : '',
17622 cls: 'combobox-clear',
17636 combobox.cls += ' roo-select2-container-multi';
17639 var required = this.allowBlank ? {
17641 style: 'display: none'
17644 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17645 tooltip : 'This field is required'
17648 var align = this.labelAlign || this.parentLabelAlign();
17650 if (align ==='left' && this.fieldLabel.length) {
17656 cls : 'control-label col-form-label',
17657 html : this.fieldLabel
17661 cls : 'roo-combobox-wrap ',
17668 var labelCfg = cfg.cn[1];
17669 var contentCfg = cfg.cn[2];
17672 if(this.indicatorpos == 'right'){
17677 cls : 'control-label col-form-label',
17681 html : this.fieldLabel
17687 cls : "roo-combobox-wrap ",
17695 labelCfg = cfg.cn[0];
17696 contentCfg = cfg.cn[1];
17701 if(this.labelWidth > 12){
17702 labelCfg.style = "width: " + this.labelWidth + 'px';
17705 if(this.labelWidth < 13 && this.labelmd == 0){
17706 this.labelmd = this.labelWidth;
17709 if(this.labellg > 0){
17710 labelCfg.cls += ' col-lg-' + this.labellg;
17711 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17714 if(this.labelmd > 0){
17715 labelCfg.cls += ' col-md-' + this.labelmd;
17716 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17719 if(this.labelsm > 0){
17720 labelCfg.cls += ' col-sm-' + this.labelsm;
17721 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17724 if(this.labelxs > 0){
17725 labelCfg.cls += ' col-xs-' + this.labelxs;
17726 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17730 } else if ( this.fieldLabel.length) {
17735 cls : 'control-label',
17736 html : this.fieldLabel
17747 if(this.indicatorpos == 'right'){
17751 cls : 'control-label',
17752 html : this.fieldLabel,
17770 var settings = this;
17772 ['xs','sm','md','lg'].map(function(size){
17773 if (settings[size]) {
17774 cfg.cls += ' col-' + size + '-' + settings[size];
17781 initTouchView : function()
17783 this.renderTouchView();
17785 this.touchViewEl.on('scroll', function(){
17786 this.el.dom.scrollTop = 0;
17789 this.originalValue = this.getValue();
17791 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17793 this.inputEl().on("click", this.showTouchView, this);
17794 if (this.triggerEl) {
17795 this.triggerEl.on("click", this.showTouchView, this);
17799 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17800 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17802 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17804 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17805 this.store.on('load', this.onTouchViewLoad, this);
17806 this.store.on('loadexception', this.onTouchViewLoadException, this);
17808 if(this.hiddenName){
17810 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17812 this.hiddenField.dom.value =
17813 this.hiddenValue !== undefined ? this.hiddenValue :
17814 this.value !== undefined ? this.value : '';
17816 this.el.dom.removeAttribute('name');
17817 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17821 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17822 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17825 if(this.removable && !this.multiple){
17826 var close = this.closeTriggerEl();
17828 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17829 close.on('click', this.removeBtnClick, this, close);
17833 * fix the bug in Safari iOS8
17835 this.inputEl().on("focus", function(e){
17836 document.activeElement.blur();
17839 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17846 renderTouchView : function()
17848 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17849 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17851 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17852 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17854 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17855 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17856 this.touchViewBodyEl.setStyle('overflow', 'auto');
17858 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17859 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17861 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17862 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17866 showTouchView : function()
17872 this.touchViewHeaderEl.hide();
17874 if(this.modalTitle.length){
17875 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17876 this.touchViewHeaderEl.show();
17879 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17880 this.touchViewEl.show();
17882 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17884 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17885 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17887 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17889 if(this.modalTitle.length){
17890 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17893 this.touchViewBodyEl.setHeight(bodyHeight);
17897 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17899 this.touchViewEl.addClass(['in','show']);
17902 if(this._touchViewMask){
17903 Roo.get(document.body).addClass("x-body-masked");
17904 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17905 this._touchViewMask.setStyle('z-index', 10000);
17906 this._touchViewMask.addClass('show');
17909 this.doTouchViewQuery();
17913 hideTouchView : function()
17915 this.touchViewEl.removeClass(['in','show']);
17919 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17921 this.touchViewEl.setStyle('display', 'none');
17924 if(this._touchViewMask){
17925 this._touchViewMask.removeClass('show');
17926 Roo.get(document.body).removeClass("x-body-masked");
17930 setTouchViewValue : function()
17937 Roo.each(this.tickItems, function(o){
17942 this.hideTouchView();
17945 doTouchViewQuery : function()
17954 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17958 if(!this.alwaysQuery || this.mode == 'local'){
17959 this.onTouchViewLoad();
17966 onTouchViewBeforeLoad : function(combo,opts)
17972 onTouchViewLoad : function()
17974 if(this.store.getCount() < 1){
17975 this.onTouchViewEmptyResults();
17979 this.clearTouchView();
17981 var rawValue = this.getRawValue();
17983 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17985 this.tickItems = [];
17987 this.store.data.each(function(d, rowIndex){
17988 var row = this.touchViewListGroup.createChild(template);
17990 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17991 row.addClass(d.data.cls);
17994 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17997 html : d.data[this.displayField]
18000 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
18001 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
18004 row.removeClass('selected');
18005 if(!this.multiple && this.valueField &&
18006 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
18009 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18010 row.addClass('selected');
18013 if(this.multiple && this.valueField &&
18014 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
18018 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18019 this.tickItems.push(d.data);
18022 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
18026 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
18028 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18030 if(this.modalTitle.length){
18031 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18034 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
18036 if(this.mobile_restrict_height && listHeight < bodyHeight){
18037 this.touchViewBodyEl.setHeight(listHeight);
18042 if(firstChecked && listHeight > bodyHeight){
18043 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
18048 onTouchViewLoadException : function()
18050 this.hideTouchView();
18053 onTouchViewEmptyResults : function()
18055 this.clearTouchView();
18057 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
18059 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
18063 clearTouchView : function()
18065 this.touchViewListGroup.dom.innerHTML = '';
18068 onTouchViewClick : function(e, el, o)
18070 e.preventDefault();
18073 var rowIndex = o.rowIndex;
18075 var r = this.store.getAt(rowIndex);
18077 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
18079 if(!this.multiple){
18080 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
18081 c.dom.removeAttribute('checked');
18084 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18086 this.setFromData(r.data);
18088 var close = this.closeTriggerEl();
18094 this.hideTouchView();
18096 this.fireEvent('select', this, r, rowIndex);
18101 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
18102 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
18103 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
18107 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18108 this.addItem(r.data);
18109 this.tickItems.push(r.data);
18113 getAutoCreateNativeIOS : function()
18116 cls: 'form-group' //input-group,
18121 cls : 'roo-ios-select'
18125 combobox.name = this.name;
18128 if (this.disabled) {
18129 combobox.disabled = true;
18132 var settings = this;
18134 ['xs','sm','md','lg'].map(function(size){
18135 if (settings[size]) {
18136 cfg.cls += ' col-' + size + '-' + settings[size];
18146 initIOSView : function()
18148 this.store.on('load', this.onIOSViewLoad, this);
18153 onIOSViewLoad : function()
18155 if(this.store.getCount() < 1){
18159 this.clearIOSView();
18161 if(this.allowBlank) {
18163 var default_text = '-- SELECT --';
18165 if(this.placeholder.length){
18166 default_text = this.placeholder;
18169 if(this.emptyTitle.length){
18170 default_text += ' - ' + this.emptyTitle + ' -';
18173 var opt = this.inputEl().createChild({
18176 html : default_text
18180 o[this.valueField] = 0;
18181 o[this.displayField] = default_text;
18183 this.ios_options.push({
18190 this.store.data.each(function(d, rowIndex){
18194 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
18195 html = d.data[this.displayField];
18200 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
18201 value = d.data[this.valueField];
18210 if(this.value == d.data[this.valueField]){
18211 option['selected'] = true;
18214 var opt = this.inputEl().createChild(option);
18216 this.ios_options.push({
18223 this.inputEl().on('change', function(){
18224 this.fireEvent('select', this);
18229 clearIOSView: function()
18231 this.inputEl().dom.innerHTML = '';
18233 this.ios_options = [];
18236 setIOSValue: function(v)
18240 if(!this.ios_options){
18244 Roo.each(this.ios_options, function(opts){
18246 opts.el.dom.removeAttribute('selected');
18248 if(opts.data[this.valueField] != v){
18252 opts.el.dom.setAttribute('selected', true);
18258 * @cfg {Boolean} grow
18262 * @cfg {Number} growMin
18266 * @cfg {Number} growMax
18275 Roo.apply(Roo.bootstrap.ComboBox, {
18279 cls: 'modal-header',
18301 cls: 'list-group-item',
18305 cls: 'roo-combobox-list-group-item-value'
18309 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
18323 listItemCheckbox : {
18325 cls: 'list-group-item',
18329 cls: 'roo-combobox-list-group-item-value'
18333 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18349 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18354 cls: 'modal-footer',
18362 cls: 'col-xs-6 text-left',
18365 cls: 'btn btn-danger roo-touch-view-cancel',
18371 cls: 'col-xs-6 text-right',
18374 cls: 'btn btn-success roo-touch-view-ok',
18385 Roo.apply(Roo.bootstrap.ComboBox, {
18387 touchViewTemplate : {
18389 cls: 'modal fade roo-combobox-touch-view',
18393 cls: 'modal-dialog',
18394 style : 'position:fixed', // we have to fix position....
18398 cls: 'modal-content',
18400 Roo.bootstrap.ComboBox.header,
18401 Roo.bootstrap.ComboBox.body,
18402 Roo.bootstrap.ComboBox.footer
18411 * Ext JS Library 1.1.1
18412 * Copyright(c) 2006-2007, Ext JS, LLC.
18414 * Originally Released Under LGPL - original licence link has changed is not relivant.
18417 * <script type="text/javascript">
18422 * @extends Roo.util.Observable
18423 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
18424 * This class also supports single and multi selection modes. <br>
18425 * Create a data model bound view:
18427 var store = new Roo.data.Store(...);
18429 var view = new Roo.View({
18431 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
18433 singleSelect: true,
18434 selectedClass: "ydataview-selected",
18438 // listen for node click?
18439 view.on("click", function(vw, index, node, e){
18440 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18444 dataModel.load("foobar.xml");
18446 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18448 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18449 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18451 * Note: old style constructor is still suported (container, template, config)
18454 * Create a new View
18455 * @param {Object} config The config object
18458 Roo.View = function(config, depreciated_tpl, depreciated_config){
18460 this.parent = false;
18462 if (typeof(depreciated_tpl) == 'undefined') {
18463 // new way.. - universal constructor.
18464 Roo.apply(this, config);
18465 this.el = Roo.get(this.el);
18468 this.el = Roo.get(config);
18469 this.tpl = depreciated_tpl;
18470 Roo.apply(this, depreciated_config);
18472 this.wrapEl = this.el.wrap().wrap();
18473 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18476 if(typeof(this.tpl) == "string"){
18477 this.tpl = new Roo.Template(this.tpl);
18479 // support xtype ctors..
18480 this.tpl = new Roo.factory(this.tpl, Roo);
18484 this.tpl.compile();
18489 * @event beforeclick
18490 * Fires before a click is processed. Returns false to cancel the default action.
18491 * @param {Roo.View} this
18492 * @param {Number} index The index of the target node
18493 * @param {HTMLElement} node The target node
18494 * @param {Roo.EventObject} e The raw event object
18496 "beforeclick" : true,
18499 * Fires when a template node is clicked.
18500 * @param {Roo.View} this
18501 * @param {Number} index The index of the target node
18502 * @param {HTMLElement} node The target node
18503 * @param {Roo.EventObject} e The raw event object
18508 * Fires when a template node is double clicked.
18509 * @param {Roo.View} this
18510 * @param {Number} index The index of the target node
18511 * @param {HTMLElement} node The target node
18512 * @param {Roo.EventObject} e The raw event object
18516 * @event contextmenu
18517 * Fires when a template node is right clicked.
18518 * @param {Roo.View} this
18519 * @param {Number} index The index of the target node
18520 * @param {HTMLElement} node The target node
18521 * @param {Roo.EventObject} e The raw event object
18523 "contextmenu" : true,
18525 * @event selectionchange
18526 * Fires when the selected nodes change.
18527 * @param {Roo.View} this
18528 * @param {Array} selections Array of the selected nodes
18530 "selectionchange" : true,
18533 * @event beforeselect
18534 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18535 * @param {Roo.View} this
18536 * @param {HTMLElement} node The node to be selected
18537 * @param {Array} selections Array of currently selected nodes
18539 "beforeselect" : true,
18541 * @event preparedata
18542 * Fires on every row to render, to allow you to change the data.
18543 * @param {Roo.View} this
18544 * @param {Object} data to be rendered (change this)
18546 "preparedata" : true
18554 "click": this.onClick,
18555 "dblclick": this.onDblClick,
18556 "contextmenu": this.onContextMenu,
18560 this.selections = [];
18562 this.cmp = new Roo.CompositeElementLite([]);
18564 this.store = Roo.factory(this.store, Roo.data);
18565 this.setStore(this.store, true);
18568 if ( this.footer && this.footer.xtype) {
18570 var fctr = this.wrapEl.appendChild(document.createElement("div"));
18572 this.footer.dataSource = this.store;
18573 this.footer.container = fctr;
18574 this.footer = Roo.factory(this.footer, Roo);
18575 fctr.insertFirst(this.el);
18577 // this is a bit insane - as the paging toolbar seems to detach the el..
18578 // dom.parentNode.parentNode.parentNode
18579 // they get detached?
18583 Roo.View.superclass.constructor.call(this);
18588 Roo.extend(Roo.View, Roo.util.Observable, {
18591 * @cfg {Roo.data.Store} store Data store to load data from.
18596 * @cfg {String|Roo.Element} el The container element.
18601 * @cfg {String|Roo.Template} tpl The template used by this View
18605 * @cfg {String} dataName the named area of the template to use as the data area
18606 * Works with domtemplates roo-name="name"
18610 * @cfg {String} selectedClass The css class to add to selected nodes
18612 selectedClass : "x-view-selected",
18614 * @cfg {String} emptyText The empty text to show when nothing is loaded.
18619 * @cfg {String} text to display on mask (default Loading)
18623 * @cfg {Boolean} multiSelect Allow multiple selection
18625 multiSelect : false,
18627 * @cfg {Boolean} singleSelect Allow single selection
18629 singleSelect: false,
18632 * @cfg {Boolean} toggleSelect - selecting
18634 toggleSelect : false,
18637 * @cfg {Boolean} tickable - selecting
18642 * Returns the element this view is bound to.
18643 * @return {Roo.Element}
18645 getEl : function(){
18646 return this.wrapEl;
18652 * Refreshes the view. - called by datachanged on the store. - do not call directly.
18654 refresh : function(){
18655 //Roo.log('refresh');
18658 // if we are using something like 'domtemplate', then
18659 // the what gets used is:
18660 // t.applySubtemplate(NAME, data, wrapping data..)
18661 // the outer template then get' applied with
18662 // the store 'extra data'
18663 // and the body get's added to the
18664 // roo-name="data" node?
18665 // <span class='roo-tpl-{name}'></span> ?????
18669 this.clearSelections();
18670 this.el.update("");
18672 var records = this.store.getRange();
18673 if(records.length < 1) {
18675 // is this valid?? = should it render a template??
18677 this.el.update(this.emptyText);
18681 if (this.dataName) {
18682 this.el.update(t.apply(this.store.meta)); //????
18683 el = this.el.child('.roo-tpl-' + this.dataName);
18686 for(var i = 0, len = records.length; i < len; i++){
18687 var data = this.prepareData(records[i].data, i, records[i]);
18688 this.fireEvent("preparedata", this, data, i, records[i]);
18690 var d = Roo.apply({}, data);
18693 Roo.apply(d, {'roo-id' : Roo.id()});
18697 Roo.each(this.parent.item, function(item){
18698 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18701 Roo.apply(d, {'roo-data-checked' : 'checked'});
18705 html[html.length] = Roo.util.Format.trim(
18707 t.applySubtemplate(this.dataName, d, this.store.meta) :
18714 el.update(html.join(""));
18715 this.nodes = el.dom.childNodes;
18716 this.updateIndexes(0);
18721 * Function to override to reformat the data that is sent to
18722 * the template for each node.
18723 * DEPRICATED - use the preparedata event handler.
18724 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18725 * a JSON object for an UpdateManager bound view).
18727 prepareData : function(data, index, record)
18729 this.fireEvent("preparedata", this, data, index, record);
18733 onUpdate : function(ds, record){
18734 // Roo.log('on update');
18735 this.clearSelections();
18736 var index = this.store.indexOf(record);
18737 var n = this.nodes[index];
18738 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18739 n.parentNode.removeChild(n);
18740 this.updateIndexes(index, index);
18746 onAdd : function(ds, records, index)
18748 //Roo.log(['on Add', ds, records, index] );
18749 this.clearSelections();
18750 if(this.nodes.length == 0){
18754 var n = this.nodes[index];
18755 for(var i = 0, len = records.length; i < len; i++){
18756 var d = this.prepareData(records[i].data, i, records[i]);
18758 this.tpl.insertBefore(n, d);
18761 this.tpl.append(this.el, d);
18764 this.updateIndexes(index);
18767 onRemove : function(ds, record, index){
18768 // Roo.log('onRemove');
18769 this.clearSelections();
18770 var el = this.dataName ?
18771 this.el.child('.roo-tpl-' + this.dataName) :
18774 el.dom.removeChild(this.nodes[index]);
18775 this.updateIndexes(index);
18779 * Refresh an individual node.
18780 * @param {Number} index
18782 refreshNode : function(index){
18783 this.onUpdate(this.store, this.store.getAt(index));
18786 updateIndexes : function(startIndex, endIndex){
18787 var ns = this.nodes;
18788 startIndex = startIndex || 0;
18789 endIndex = endIndex || ns.length - 1;
18790 for(var i = startIndex; i <= endIndex; i++){
18791 ns[i].nodeIndex = i;
18796 * Changes the data store this view uses and refresh the view.
18797 * @param {Store} store
18799 setStore : function(store, initial){
18800 if(!initial && this.store){
18801 this.store.un("datachanged", this.refresh);
18802 this.store.un("add", this.onAdd);
18803 this.store.un("remove", this.onRemove);
18804 this.store.un("update", this.onUpdate);
18805 this.store.un("clear", this.refresh);
18806 this.store.un("beforeload", this.onBeforeLoad);
18807 this.store.un("load", this.onLoad);
18808 this.store.un("loadexception", this.onLoad);
18812 store.on("datachanged", this.refresh, this);
18813 store.on("add", this.onAdd, this);
18814 store.on("remove", this.onRemove, this);
18815 store.on("update", this.onUpdate, this);
18816 store.on("clear", this.refresh, this);
18817 store.on("beforeload", this.onBeforeLoad, this);
18818 store.on("load", this.onLoad, this);
18819 store.on("loadexception", this.onLoad, this);
18827 * onbeforeLoad - masks the loading area.
18830 onBeforeLoad : function(store,opts)
18832 //Roo.log('onBeforeLoad');
18834 this.el.update("");
18836 this.el.mask(this.mask ? this.mask : "Loading" );
18838 onLoad : function ()
18845 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18846 * @param {HTMLElement} node
18847 * @return {HTMLElement} The template node
18849 findItemFromChild : function(node){
18850 var el = this.dataName ?
18851 this.el.child('.roo-tpl-' + this.dataName,true) :
18854 if(!node || node.parentNode == el){
18857 var p = node.parentNode;
18858 while(p && p != el){
18859 if(p.parentNode == el){
18868 onClick : function(e){
18869 var item = this.findItemFromChild(e.getTarget());
18871 var index = this.indexOf(item);
18872 if(this.onItemClick(item, index, e) !== false){
18873 this.fireEvent("click", this, index, item, e);
18876 this.clearSelections();
18881 onContextMenu : function(e){
18882 var item = this.findItemFromChild(e.getTarget());
18884 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18889 onDblClick : function(e){
18890 var item = this.findItemFromChild(e.getTarget());
18892 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18896 onItemClick : function(item, index, e)
18898 if(this.fireEvent("beforeclick", this, index, item, e) === false){
18901 if (this.toggleSelect) {
18902 var m = this.isSelected(item) ? 'unselect' : 'select';
18905 _t[m](item, true, false);
18908 if(this.multiSelect || this.singleSelect){
18909 if(this.multiSelect && e.shiftKey && this.lastSelection){
18910 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18912 this.select(item, this.multiSelect && e.ctrlKey);
18913 this.lastSelection = item;
18916 if(!this.tickable){
18917 e.preventDefault();
18925 * Get the number of selected nodes.
18928 getSelectionCount : function(){
18929 return this.selections.length;
18933 * Get the currently selected nodes.
18934 * @return {Array} An array of HTMLElements
18936 getSelectedNodes : function(){
18937 return this.selections;
18941 * Get the indexes of the selected nodes.
18944 getSelectedIndexes : function(){
18945 var indexes = [], s = this.selections;
18946 for(var i = 0, len = s.length; i < len; i++){
18947 indexes.push(s[i].nodeIndex);
18953 * Clear all selections
18954 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18956 clearSelections : function(suppressEvent){
18957 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18958 this.cmp.elements = this.selections;
18959 this.cmp.removeClass(this.selectedClass);
18960 this.selections = [];
18961 if(!suppressEvent){
18962 this.fireEvent("selectionchange", this, this.selections);
18968 * Returns true if the passed node is selected
18969 * @param {HTMLElement/Number} node The node or node index
18970 * @return {Boolean}
18972 isSelected : function(node){
18973 var s = this.selections;
18977 node = this.getNode(node);
18978 return s.indexOf(node) !== -1;
18983 * @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
18984 * @param {Boolean} keepExisting (optional) true to keep existing selections
18985 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18987 select : function(nodeInfo, keepExisting, suppressEvent){
18988 if(nodeInfo instanceof Array){
18990 this.clearSelections(true);
18992 for(var i = 0, len = nodeInfo.length; i < len; i++){
18993 this.select(nodeInfo[i], true, true);
18997 var node = this.getNode(nodeInfo);
18998 if(!node || this.isSelected(node)){
18999 return; // already selected.
19002 this.clearSelections(true);
19005 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
19006 Roo.fly(node).addClass(this.selectedClass);
19007 this.selections.push(node);
19008 if(!suppressEvent){
19009 this.fireEvent("selectionchange", this, this.selections);
19017 * @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
19018 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
19019 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
19021 unselect : function(nodeInfo, keepExisting, suppressEvent)
19023 if(nodeInfo instanceof Array){
19024 Roo.each(this.selections, function(s) {
19025 this.unselect(s, nodeInfo);
19029 var node = this.getNode(nodeInfo);
19030 if(!node || !this.isSelected(node)){
19031 //Roo.log("not selected");
19032 return; // not selected.
19036 Roo.each(this.selections, function(s) {
19038 Roo.fly(node).removeClass(this.selectedClass);
19045 this.selections= ns;
19046 this.fireEvent("selectionchange", this, this.selections);
19050 * Gets a template node.
19051 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19052 * @return {HTMLElement} The node or null if it wasn't found
19054 getNode : function(nodeInfo){
19055 if(typeof nodeInfo == "string"){
19056 return document.getElementById(nodeInfo);
19057 }else if(typeof nodeInfo == "number"){
19058 return this.nodes[nodeInfo];
19064 * Gets a range template nodes.
19065 * @param {Number} startIndex
19066 * @param {Number} endIndex
19067 * @return {Array} An array of nodes
19069 getNodes : function(start, end){
19070 var ns = this.nodes;
19071 start = start || 0;
19072 end = typeof end == "undefined" ? ns.length - 1 : end;
19075 for(var i = start; i <= end; i++){
19079 for(var i = start; i >= end; i--){
19087 * Finds the index of the passed node
19088 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19089 * @return {Number} The index of the node or -1
19091 indexOf : function(node){
19092 node = this.getNode(node);
19093 if(typeof node.nodeIndex == "number"){
19094 return node.nodeIndex;
19096 var ns = this.nodes;
19097 for(var i = 0, len = ns.length; i < len; i++){
19108 * based on jquery fullcalendar
19112 Roo.bootstrap = Roo.bootstrap || {};
19114 * @class Roo.bootstrap.Calendar
19115 * @extends Roo.bootstrap.Component
19116 * Bootstrap Calendar class
19117 * @cfg {Boolean} loadMask (true|false) default false
19118 * @cfg {Object} header generate the user specific header of the calendar, default false
19121 * Create a new Container
19122 * @param {Object} config The config object
19127 Roo.bootstrap.Calendar = function(config){
19128 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
19132 * Fires when a date is selected
19133 * @param {DatePicker} this
19134 * @param {Date} date The selected date
19138 * @event monthchange
19139 * Fires when the displayed month changes
19140 * @param {DatePicker} this
19141 * @param {Date} date The selected month
19143 'monthchange': true,
19145 * @event evententer
19146 * Fires when mouse over an event
19147 * @param {Calendar} this
19148 * @param {event} Event
19150 'evententer': true,
19152 * @event eventleave
19153 * Fires when the mouse leaves an
19154 * @param {Calendar} this
19157 'eventleave': true,
19159 * @event eventclick
19160 * Fires when the mouse click an
19161 * @param {Calendar} this
19170 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
19173 * @cfg {Number} startDay
19174 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
19182 getAutoCreate : function(){
19185 var fc_button = function(name, corner, style, content ) {
19186 return Roo.apply({},{
19188 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
19190 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
19193 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
19204 style : 'width:100%',
19211 cls : 'fc-header-left',
19213 fc_button('prev', 'left', 'arrow', '‹' ),
19214 fc_button('next', 'right', 'arrow', '›' ),
19215 { tag: 'span', cls: 'fc-header-space' },
19216 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
19224 cls : 'fc-header-center',
19228 cls: 'fc-header-title',
19231 html : 'month / year'
19239 cls : 'fc-header-right',
19241 /* fc_button('month', 'left', '', 'month' ),
19242 fc_button('week', '', '', 'week' ),
19243 fc_button('day', 'right', '', 'day' )
19255 header = this.header;
19258 var cal_heads = function() {
19260 // fixme - handle this.
19262 for (var i =0; i < Date.dayNames.length; i++) {
19263 var d = Date.dayNames[i];
19266 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
19267 html : d.substring(0,3)
19271 ret[0].cls += ' fc-first';
19272 ret[6].cls += ' fc-last';
19275 var cal_cell = function(n) {
19278 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
19283 cls: 'fc-day-number',
19287 cls: 'fc-day-content',
19291 style: 'position: relative;' // height: 17px;
19303 var cal_rows = function() {
19306 for (var r = 0; r < 6; r++) {
19313 for (var i =0; i < Date.dayNames.length; i++) {
19314 var d = Date.dayNames[i];
19315 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
19318 row.cn[0].cls+=' fc-first';
19319 row.cn[0].cn[0].style = 'min-height:90px';
19320 row.cn[6].cls+=' fc-last';
19324 ret[0].cls += ' fc-first';
19325 ret[4].cls += ' fc-prev-last';
19326 ret[5].cls += ' fc-last';
19333 cls: 'fc-border-separate',
19334 style : 'width:100%',
19342 cls : 'fc-first fc-last',
19360 cls : 'fc-content',
19361 style : "position: relative;",
19364 cls : 'fc-view fc-view-month fc-grid',
19365 style : 'position: relative',
19366 unselectable : 'on',
19369 cls : 'fc-event-container',
19370 style : 'position:absolute;z-index:8;top:0;left:0;'
19388 initEvents : function()
19391 throw "can not find store for calendar";
19397 style: "text-align:center",
19401 style: "background-color:white;width:50%;margin:250 auto",
19405 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
19416 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19418 var size = this.el.select('.fc-content', true).first().getSize();
19419 this.maskEl.setSize(size.width, size.height);
19420 this.maskEl.enableDisplayMode("block");
19421 if(!this.loadMask){
19422 this.maskEl.hide();
19425 this.store = Roo.factory(this.store, Roo.data);
19426 this.store.on('load', this.onLoad, this);
19427 this.store.on('beforeload', this.onBeforeLoad, this);
19431 this.cells = this.el.select('.fc-day',true);
19432 //Roo.log(this.cells);
19433 this.textNodes = this.el.query('.fc-day-number');
19434 this.cells.addClassOnOver('fc-state-hover');
19436 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19437 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19438 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19439 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19441 this.on('monthchange', this.onMonthChange, this);
19443 this.update(new Date().clearTime());
19446 resize : function() {
19447 var sz = this.el.getSize();
19449 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19450 this.el.select('.fc-day-content div',true).setHeight(34);
19455 showPrevMonth : function(e){
19456 this.update(this.activeDate.add("mo", -1));
19458 showToday : function(e){
19459 this.update(new Date().clearTime());
19462 showNextMonth : function(e){
19463 this.update(this.activeDate.add("mo", 1));
19467 showPrevYear : function(){
19468 this.update(this.activeDate.add("y", -1));
19472 showNextYear : function(){
19473 this.update(this.activeDate.add("y", 1));
19478 update : function(date)
19480 var vd = this.activeDate;
19481 this.activeDate = date;
19482 // if(vd && this.el){
19483 // var t = date.getTime();
19484 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19485 // Roo.log('using add remove');
19487 // this.fireEvent('monthchange', this, date);
19489 // this.cells.removeClass("fc-state-highlight");
19490 // this.cells.each(function(c){
19491 // if(c.dateValue == t){
19492 // c.addClass("fc-state-highlight");
19493 // setTimeout(function(){
19494 // try{c.dom.firstChild.focus();}catch(e){}
19504 var days = date.getDaysInMonth();
19506 var firstOfMonth = date.getFirstDateOfMonth();
19507 var startingPos = firstOfMonth.getDay()-this.startDay;
19509 if(startingPos < this.startDay){
19513 var pm = date.add(Date.MONTH, -1);
19514 var prevStart = pm.getDaysInMonth()-startingPos;
19516 this.cells = this.el.select('.fc-day',true);
19517 this.textNodes = this.el.query('.fc-day-number');
19518 this.cells.addClassOnOver('fc-state-hover');
19520 var cells = this.cells.elements;
19521 var textEls = this.textNodes;
19523 Roo.each(cells, function(cell){
19524 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19527 days += startingPos;
19529 // convert everything to numbers so it's fast
19530 var day = 86400000;
19531 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19534 //Roo.log(prevStart);
19536 var today = new Date().clearTime().getTime();
19537 var sel = date.clearTime().getTime();
19538 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19539 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19540 var ddMatch = this.disabledDatesRE;
19541 var ddText = this.disabledDatesText;
19542 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19543 var ddaysText = this.disabledDaysText;
19544 var format = this.format;
19546 var setCellClass = function(cal, cell){
19550 //Roo.log('set Cell Class');
19552 var t = d.getTime();
19556 cell.dateValue = t;
19558 cell.className += " fc-today";
19559 cell.className += " fc-state-highlight";
19560 cell.title = cal.todayText;
19563 // disable highlight in other month..
19564 //cell.className += " fc-state-highlight";
19569 cell.className = " fc-state-disabled";
19570 cell.title = cal.minText;
19574 cell.className = " fc-state-disabled";
19575 cell.title = cal.maxText;
19579 if(ddays.indexOf(d.getDay()) != -1){
19580 cell.title = ddaysText;
19581 cell.className = " fc-state-disabled";
19584 if(ddMatch && format){
19585 var fvalue = d.dateFormat(format);
19586 if(ddMatch.test(fvalue)){
19587 cell.title = ddText.replace("%0", fvalue);
19588 cell.className = " fc-state-disabled";
19592 if (!cell.initialClassName) {
19593 cell.initialClassName = cell.dom.className;
19596 cell.dom.className = cell.initialClassName + ' ' + cell.className;
19601 for(; i < startingPos; i++) {
19602 textEls[i].innerHTML = (++prevStart);
19603 d.setDate(d.getDate()+1);
19605 cells[i].className = "fc-past fc-other-month";
19606 setCellClass(this, cells[i]);
19611 for(; i < days; i++){
19612 intDay = i - startingPos + 1;
19613 textEls[i].innerHTML = (intDay);
19614 d.setDate(d.getDate()+1);
19616 cells[i].className = ''; // "x-date-active";
19617 setCellClass(this, cells[i]);
19621 for(; i < 42; i++) {
19622 textEls[i].innerHTML = (++extraDays);
19623 d.setDate(d.getDate()+1);
19625 cells[i].className = "fc-future fc-other-month";
19626 setCellClass(this, cells[i]);
19629 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19631 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19633 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19634 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19636 if(totalRows != 6){
19637 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19638 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19641 this.fireEvent('monthchange', this, date);
19645 if(!this.internalRender){
19646 var main = this.el.dom.firstChild;
19647 var w = main.offsetWidth;
19648 this.el.setWidth(w + this.el.getBorderWidth("lr"));
19649 Roo.fly(main).setWidth(w);
19650 this.internalRender = true;
19651 // opera does not respect the auto grow header center column
19652 // then, after it gets a width opera refuses to recalculate
19653 // without a second pass
19654 if(Roo.isOpera && !this.secondPass){
19655 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19656 this.secondPass = true;
19657 this.update.defer(10, this, [date]);
19664 findCell : function(dt) {
19665 dt = dt.clearTime().getTime();
19667 this.cells.each(function(c){
19668 //Roo.log("check " +c.dateValue + '?=' + dt);
19669 if(c.dateValue == dt){
19679 findCells : function(ev) {
19680 var s = ev.start.clone().clearTime().getTime();
19682 var e= ev.end.clone().clearTime().getTime();
19685 this.cells.each(function(c){
19686 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19688 if(c.dateValue > e){
19691 if(c.dateValue < s){
19700 // findBestRow: function(cells)
19704 // for (var i =0 ; i < cells.length;i++) {
19705 // ret = Math.max(cells[i].rows || 0,ret);
19712 addItem : function(ev)
19714 // look for vertical location slot in
19715 var cells = this.findCells(ev);
19717 // ev.row = this.findBestRow(cells);
19719 // work out the location.
19723 for(var i =0; i < cells.length; i++) {
19725 cells[i].row = cells[0].row;
19728 cells[i].row = cells[i].row + 1;
19738 if (crow.start.getY() == cells[i].getY()) {
19740 crow.end = cells[i];
19757 cells[0].events.push(ev);
19759 this.calevents.push(ev);
19762 clearEvents: function() {
19764 if(!this.calevents){
19768 Roo.each(this.cells.elements, function(c){
19774 Roo.each(this.calevents, function(e) {
19775 Roo.each(e.els, function(el) {
19776 el.un('mouseenter' ,this.onEventEnter, this);
19777 el.un('mouseleave' ,this.onEventLeave, this);
19782 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19788 renderEvents: function()
19792 this.cells.each(function(c) {
19801 if(c.row != c.events.length){
19802 r = 4 - (4 - (c.row - c.events.length));
19805 c.events = ev.slice(0, r);
19806 c.more = ev.slice(r);
19808 if(c.more.length && c.more.length == 1){
19809 c.events.push(c.more.pop());
19812 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19816 this.cells.each(function(c) {
19818 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19821 for (var e = 0; e < c.events.length; e++){
19822 var ev = c.events[e];
19823 var rows = ev.rows;
19825 for(var i = 0; i < rows.length; i++) {
19827 // how many rows should it span..
19830 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19831 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19833 unselectable : "on",
19836 cls: 'fc-event-inner',
19840 // cls: 'fc-event-time',
19841 // html : cells.length > 1 ? '' : ev.time
19845 cls: 'fc-event-title',
19846 html : String.format('{0}', ev.title)
19853 cls: 'ui-resizable-handle ui-resizable-e',
19854 html : '  '
19861 cfg.cls += ' fc-event-start';
19863 if ((i+1) == rows.length) {
19864 cfg.cls += ' fc-event-end';
19867 var ctr = _this.el.select('.fc-event-container',true).first();
19868 var cg = ctr.createChild(cfg);
19870 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19871 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19873 var r = (c.more.length) ? 1 : 0;
19874 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
19875 cg.setWidth(ebox.right - sbox.x -2);
19877 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19878 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19879 cg.on('click', _this.onEventClick, _this, ev);
19890 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19891 style : 'position: absolute',
19892 unselectable : "on",
19895 cls: 'fc-event-inner',
19899 cls: 'fc-event-title',
19907 cls: 'ui-resizable-handle ui-resizable-e',
19908 html : '  '
19914 var ctr = _this.el.select('.fc-event-container',true).first();
19915 var cg = ctr.createChild(cfg);
19917 var sbox = c.select('.fc-day-content',true).first().getBox();
19918 var ebox = c.select('.fc-day-content',true).first().getBox();
19920 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
19921 cg.setWidth(ebox.right - sbox.x -2);
19923 cg.on('click', _this.onMoreEventClick, _this, c.more);
19933 onEventEnter: function (e, el,event,d) {
19934 this.fireEvent('evententer', this, el, event);
19937 onEventLeave: function (e, el,event,d) {
19938 this.fireEvent('eventleave', this, el, event);
19941 onEventClick: function (e, el,event,d) {
19942 this.fireEvent('eventclick', this, el, event);
19945 onMonthChange: function () {
19949 onMoreEventClick: function(e, el, more)
19953 this.calpopover.placement = 'right';
19954 this.calpopover.setTitle('More');
19956 this.calpopover.setContent('');
19958 var ctr = this.calpopover.el.select('.popover-content', true).first();
19960 Roo.each(more, function(m){
19962 cls : 'fc-event-hori fc-event-draggable',
19965 var cg = ctr.createChild(cfg);
19967 cg.on('click', _this.onEventClick, _this, m);
19970 this.calpopover.show(el);
19975 onLoad: function ()
19977 this.calevents = [];
19980 if(this.store.getCount() > 0){
19981 this.store.data.each(function(d){
19984 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19985 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19986 time : d.data.start_time,
19987 title : d.data.title,
19988 description : d.data.description,
19989 venue : d.data.venue
19994 this.renderEvents();
19996 if(this.calevents.length && this.loadMask){
19997 this.maskEl.hide();
20001 onBeforeLoad: function()
20003 this.clearEvents();
20005 this.maskEl.show();
20019 * @class Roo.bootstrap.Popover
20020 * @extends Roo.bootstrap.Component
20021 * Bootstrap Popover class
20022 * @cfg {String} html contents of the popover (or false to use children..)
20023 * @cfg {String} title of popover (or false to hide)
20024 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
20025 * @cfg {String} trigger click || hover (or false to trigger manually)
20026 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
20027 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
20028 * - if false and it has a 'parent' then it will be automatically added to that element
20029 * - if string - Roo.get will be called
20030 * @cfg {Number} delay - delay before showing
20033 * Create a new Popover
20034 * @param {Object} config The config object
20037 Roo.bootstrap.Popover = function(config){
20038 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
20044 * After the popover show
20046 * @param {Roo.bootstrap.Popover} this
20051 * After the popover hide
20053 * @param {Roo.bootstrap.Popover} this
20059 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
20064 placement : 'right',
20065 trigger : 'hover', // hover
20071 can_build_overlaid : false,
20073 maskEl : false, // the mask element
20076 alignEl : false, // when show is called with an element - this get's stored.
20078 getChildContainer : function()
20080 return this.contentEl;
20083 getPopoverHeader : function()
20085 this.title = true; // flag not to hide it..
20086 this.headerEl.addClass('p-0');
20087 return this.headerEl
20091 getAutoCreate : function(){
20094 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
20095 style: 'display:block',
20101 cls : 'popover-inner ',
20105 cls: 'popover-title popover-header',
20106 html : this.title === false ? '' : this.title
20109 cls : 'popover-content popover-body ' + (this.cls || ''),
20110 html : this.html || ''
20121 * @param {string} the title
20123 setTitle: function(str)
20127 this.headerEl.dom.innerHTML = str;
20132 * @param {string} the body content
20134 setContent: function(str)
20137 if (this.contentEl) {
20138 this.contentEl.dom.innerHTML = str;
20142 // as it get's added to the bottom of the page.
20143 onRender : function(ct, position)
20145 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
20150 var cfg = Roo.apply({}, this.getAutoCreate());
20154 cfg.cls += ' ' + this.cls;
20157 cfg.style = this.style;
20159 //Roo.log("adding to ");
20160 this.el = Roo.get(document.body).createChild(cfg, position);
20161 // Roo.log(this.el);
20164 this.contentEl = this.el.select('.popover-content',true).first();
20165 this.headerEl = this.el.select('.popover-title',true).first();
20168 if(typeof(this.items) != 'undefined'){
20169 var items = this.items;
20172 for(var i =0;i < items.length;i++) {
20173 nitems.push(this.addxtype(Roo.apply({}, items[i])));
20177 this.items = nitems;
20179 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
20180 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
20187 resizeMask : function()
20189 this.maskEl.setSize(
20190 Roo.lib.Dom.getViewWidth(true),
20191 Roo.lib.Dom.getViewHeight(true)
20195 initEvents : function()
20199 Roo.bootstrap.Popover.register(this);
20202 this.arrowEl = this.el.select('.arrow',true).first();
20203 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
20204 this.el.enableDisplayMode('block');
20208 if (this.over === false && !this.parent()) {
20211 if (this.triggers === false) {
20216 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
20217 var triggers = this.trigger ? this.trigger.split(' ') : [];
20218 Roo.each(triggers, function(trigger) {
20220 if (trigger == 'click') {
20221 on_el.on('click', this.toggle, this);
20222 } else if (trigger != 'manual') {
20223 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
20224 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
20226 on_el.on(eventIn ,this.enter, this);
20227 on_el.on(eventOut, this.leave, this);
20237 toggle : function () {
20238 this.hoverState == 'in' ? this.leave() : this.enter();
20241 enter : function () {
20243 clearTimeout(this.timeout);
20245 this.hoverState = 'in';
20247 if (!this.delay || !this.delay.show) {
20252 this.timeout = setTimeout(function () {
20253 if (_t.hoverState == 'in') {
20256 }, this.delay.show)
20259 leave : function() {
20260 clearTimeout(this.timeout);
20262 this.hoverState = 'out';
20264 if (!this.delay || !this.delay.hide) {
20269 this.timeout = setTimeout(function () {
20270 if (_t.hoverState == 'out') {
20273 }, this.delay.hide)
20277 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
20278 * @param {string} (left|right|top|bottom) position
20280 show : function (on_el, placement)
20282 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
20283 on_el = on_el || false; // default to false
20286 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
20287 on_el = this.parent().el;
20288 } else if (this.over) {
20289 on_el = Roo.get(this.over);
20294 this.alignEl = Roo.get( on_el );
20297 this.render(document.body);
20303 if (this.title === false) {
20304 this.headerEl.hide();
20309 this.el.dom.style.display = 'block';
20312 if (this.alignEl) {
20313 this.updatePosition(this.placement, true);
20316 // this is usually just done by the builder = to show the popoup in the middle of the scren.
20317 var es = this.el.getSize();
20318 var x = Roo.lib.Dom.getViewWidth()/2;
20319 var y = Roo.lib.Dom.getViewHeight()/2;
20320 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
20325 //var arrow = this.el.select('.arrow',true).first();
20326 //arrow.set(align[2],
20328 this.el.addClass('in');
20332 this.hoverState = 'in';
20335 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
20336 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20337 this.maskEl.dom.style.display = 'block';
20338 this.maskEl.addClass('show');
20340 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20342 this.fireEvent('show', this);
20346 * fire this manually after loading a grid in the table for example
20347 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20348 * @param {Boolean} try and move it if we cant get right position.
20350 updatePosition : function(placement, try_move)
20352 // allow for calling with no parameters
20353 placement = placement ? placement : this.placement;
20354 try_move = typeof(try_move) == 'undefined' ? true : try_move;
20356 this.el.removeClass([
20357 'fade','top','bottom', 'left', 'right','in',
20358 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20360 this.el.addClass(placement + ' bs-popover-' + placement);
20362 if (!this.alignEl ) {
20366 switch (placement) {
20368 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20369 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20370 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20371 //normal display... or moved up/down.
20372 this.el.setXY(offset);
20373 var xy = this.alignEl.getAnchorXY('tr', false);
20375 this.arrowEl.setXY(xy);
20378 // continue through...
20379 return this.updatePosition('left', false);
20383 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20384 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20385 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20386 //normal display... or moved up/down.
20387 this.el.setXY(offset);
20388 var xy = this.alignEl.getAnchorXY('tl', false);
20389 xy[0]-=10;xy[1]+=5; // << fix me
20390 this.arrowEl.setXY(xy);
20394 return this.updatePosition('right', false);
20397 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20398 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20399 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20400 //normal display... or moved up/down.
20401 this.el.setXY(offset);
20402 var xy = this.alignEl.getAnchorXY('t', false);
20403 xy[1]-=10; // << fix me
20404 this.arrowEl.setXY(xy);
20408 return this.updatePosition('bottom', false);
20411 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20412 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20413 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20414 //normal display... or moved up/down.
20415 this.el.setXY(offset);
20416 var xy = this.alignEl.getAnchorXY('b', false);
20417 xy[1]+=2; // << fix me
20418 this.arrowEl.setXY(xy);
20422 return this.updatePosition('top', false);
20433 this.el.setXY([0,0]);
20434 this.el.removeClass('in');
20436 this.hoverState = null;
20437 this.maskEl.hide(); // always..
20438 this.fireEvent('hide', this);
20444 Roo.apply(Roo.bootstrap.Popover, {
20447 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20448 'right' : ['l-br', [10,0], 'right bs-popover-right'],
20449 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20450 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20455 clickHander : false,
20459 onMouseDown : function(e)
20461 if (this.popups.length && !e.getTarget(".roo-popover")) {
20462 /// what is nothing is showing..
20471 register : function(popup)
20473 if (!Roo.bootstrap.Popover.clickHandler) {
20474 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20476 // hide other popups.
20477 popup.on('show', Roo.bootstrap.Popover.onShow, popup);
20478 popup.on('hide', Roo.bootstrap.Popover.onHide, popup);
20479 this.hideAll(); //<< why?
20480 //this.popups.push(popup);
20482 hideAll : function()
20484 this.popups.forEach(function(p) {
20488 onShow : function() {
20489 Roo.bootstrap.Popover.popups.push(this);
20491 onHide : function() {
20492 Roo.bootstrap.Popover.popups.remove(this);
20498 * Card header - holder for the card header elements.
20503 * @class Roo.bootstrap.PopoverNav
20504 * @extends Roo.bootstrap.NavGroup
20505 * Bootstrap Popover header navigation class
20507 * Create a new Popover Header Navigation
20508 * @param {Object} config The config object
20511 Roo.bootstrap.PopoverNav = function(config){
20512 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20515 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
20518 container_method : 'getPopoverHeader'
20536 * @class Roo.bootstrap.Progress
20537 * @extends Roo.bootstrap.Component
20538 * Bootstrap Progress class
20539 * @cfg {Boolean} striped striped of the progress bar
20540 * @cfg {Boolean} active animated of the progress bar
20544 * Create a new Progress
20545 * @param {Object} config The config object
20548 Roo.bootstrap.Progress = function(config){
20549 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20552 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
20557 getAutoCreate : function(){
20565 cfg.cls += ' progress-striped';
20569 cfg.cls += ' active';
20588 * @class Roo.bootstrap.ProgressBar
20589 * @extends Roo.bootstrap.Component
20590 * Bootstrap ProgressBar class
20591 * @cfg {Number} aria_valuenow aria-value now
20592 * @cfg {Number} aria_valuemin aria-value min
20593 * @cfg {Number} aria_valuemax aria-value max
20594 * @cfg {String} label label for the progress bar
20595 * @cfg {String} panel (success | info | warning | danger )
20596 * @cfg {String} role role of the progress bar
20597 * @cfg {String} sr_only text
20601 * Create a new ProgressBar
20602 * @param {Object} config The config object
20605 Roo.bootstrap.ProgressBar = function(config){
20606 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20609 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
20613 aria_valuemax : 100,
20619 getAutoCreate : function()
20624 cls: 'progress-bar',
20625 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20637 cfg.role = this.role;
20640 if(this.aria_valuenow){
20641 cfg['aria-valuenow'] = this.aria_valuenow;
20644 if(this.aria_valuemin){
20645 cfg['aria-valuemin'] = this.aria_valuemin;
20648 if(this.aria_valuemax){
20649 cfg['aria-valuemax'] = this.aria_valuemax;
20652 if(this.label && !this.sr_only){
20653 cfg.html = this.label;
20657 cfg.cls += ' progress-bar-' + this.panel;
20663 update : function(aria_valuenow)
20665 this.aria_valuenow = aria_valuenow;
20667 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20682 * @class Roo.bootstrap.TabGroup
20683 * @extends Roo.bootstrap.Column
20684 * Bootstrap Column class
20685 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20686 * @cfg {Boolean} carousel true to make the group behave like a carousel
20687 * @cfg {Boolean} bullets show bullets for the panels
20688 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20689 * @cfg {Number} timer auto slide timer .. default 0 millisecond
20690 * @cfg {Boolean} showarrow (true|false) show arrow default true
20693 * Create a new TabGroup
20694 * @param {Object} config The config object
20697 Roo.bootstrap.TabGroup = function(config){
20698 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20700 this.navId = Roo.id();
20703 Roo.bootstrap.TabGroup.register(this);
20707 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
20710 transition : false,
20715 slideOnTouch : false,
20718 getAutoCreate : function()
20720 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20722 cfg.cls += ' tab-content';
20724 if (this.carousel) {
20725 cfg.cls += ' carousel slide';
20728 cls : 'carousel-inner',
20732 if(this.bullets && !Roo.isTouch){
20735 cls : 'carousel-bullets',
20739 if(this.bullets_cls){
20740 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20747 cfg.cn[0].cn.push(bullets);
20750 if(this.showarrow){
20751 cfg.cn[0].cn.push({
20753 class : 'carousel-arrow',
20757 class : 'carousel-prev',
20761 class : 'fa fa-chevron-left'
20767 class : 'carousel-next',
20771 class : 'fa fa-chevron-right'
20784 initEvents: function()
20786 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20787 // this.el.on("touchstart", this.onTouchStart, this);
20790 if(this.autoslide){
20793 this.slideFn = window.setInterval(function() {
20794 _this.showPanelNext();
20798 if(this.showarrow){
20799 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20800 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20806 // onTouchStart : function(e, el, o)
20808 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20812 // this.showPanelNext();
20816 getChildContainer : function()
20818 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20822 * register a Navigation item
20823 * @param {Roo.bootstrap.NavItem} the navitem to add
20825 register : function(item)
20827 this.tabs.push( item);
20828 item.navId = this.navId; // not really needed..
20833 getActivePanel : function()
20836 Roo.each(this.tabs, function(t) {
20846 getPanelByName : function(n)
20849 Roo.each(this.tabs, function(t) {
20850 if (t.tabId == n) {
20858 indexOfPanel : function(p)
20861 Roo.each(this.tabs, function(t,i) {
20862 if (t.tabId == p.tabId) {
20871 * show a specific panel
20872 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20873 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20875 showPanel : function (pan)
20877 if(this.transition || typeof(pan) == 'undefined'){
20878 Roo.log("waiting for the transitionend");
20882 if (typeof(pan) == 'number') {
20883 pan = this.tabs[pan];
20886 if (typeof(pan) == 'string') {
20887 pan = this.getPanelByName(pan);
20890 var cur = this.getActivePanel();
20893 Roo.log('pan or acitve pan is undefined');
20897 if (pan.tabId == this.getActivePanel().tabId) {
20901 if (false === cur.fireEvent('beforedeactivate')) {
20905 if(this.bullets > 0 && !Roo.isTouch){
20906 this.setActiveBullet(this.indexOfPanel(pan));
20909 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20911 //class="carousel-item carousel-item-next carousel-item-left"
20913 this.transition = true;
20914 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
20915 var lr = dir == 'next' ? 'left' : 'right';
20916 pan.el.addClass(dir); // or prev
20917 pan.el.addClass('carousel-item-' + dir); // or prev
20918 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20919 cur.el.addClass(lr); // or right
20920 pan.el.addClass(lr);
20921 cur.el.addClass('carousel-item-' +lr); // or right
20922 pan.el.addClass('carousel-item-' +lr);
20926 cur.el.on('transitionend', function() {
20927 Roo.log("trans end?");
20929 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20930 pan.setActive(true);
20932 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20933 cur.setActive(false);
20935 _this.transition = false;
20937 }, this, { single: true } );
20942 cur.setActive(false);
20943 pan.setActive(true);
20948 showPanelNext : function()
20950 var i = this.indexOfPanel(this.getActivePanel());
20952 if (i >= this.tabs.length - 1 && !this.autoslide) {
20956 if (i >= this.tabs.length - 1 && this.autoslide) {
20960 this.showPanel(this.tabs[i+1]);
20963 showPanelPrev : function()
20965 var i = this.indexOfPanel(this.getActivePanel());
20967 if (i < 1 && !this.autoslide) {
20971 if (i < 1 && this.autoslide) {
20972 i = this.tabs.length;
20975 this.showPanel(this.tabs[i-1]);
20979 addBullet: function()
20981 if(!this.bullets || Roo.isTouch){
20984 var ctr = this.el.select('.carousel-bullets',true).first();
20985 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20986 var bullet = ctr.createChild({
20987 cls : 'bullet bullet-' + i
20988 },ctr.dom.lastChild);
20993 bullet.on('click', (function(e, el, o, ii, t){
20995 e.preventDefault();
20997 this.showPanel(ii);
20999 if(this.autoslide && this.slideFn){
21000 clearInterval(this.slideFn);
21001 this.slideFn = window.setInterval(function() {
21002 _this.showPanelNext();
21006 }).createDelegate(this, [i, bullet], true));
21011 setActiveBullet : function(i)
21017 Roo.each(this.el.select('.bullet', true).elements, function(el){
21018 el.removeClass('selected');
21021 var bullet = this.el.select('.bullet-' + i, true).first();
21027 bullet.addClass('selected');
21038 Roo.apply(Roo.bootstrap.TabGroup, {
21042 * register a Navigation Group
21043 * @param {Roo.bootstrap.NavGroup} the navgroup to add
21045 register : function(navgrp)
21047 this.groups[navgrp.navId] = navgrp;
21051 * fetch a Navigation Group based on the navigation ID
21052 * if one does not exist , it will get created.
21053 * @param {string} the navgroup to add
21054 * @returns {Roo.bootstrap.NavGroup} the navgroup
21056 get: function(navId) {
21057 if (typeof(this.groups[navId]) == 'undefined') {
21058 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
21060 return this.groups[navId] ;
21075 * @class Roo.bootstrap.TabPanel
21076 * @extends Roo.bootstrap.Component
21077 * Bootstrap TabPanel class
21078 * @cfg {Boolean} active panel active
21079 * @cfg {String} html panel content
21080 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
21081 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
21082 * @cfg {String} href click to link..
21083 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
21087 * Create a new TabPanel
21088 * @param {Object} config The config object
21091 Roo.bootstrap.TabPanel = function(config){
21092 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
21096 * Fires when the active status changes
21097 * @param {Roo.bootstrap.TabPanel} this
21098 * @param {Boolean} state the new state
21103 * @event beforedeactivate
21104 * Fires before a tab is de-activated - can be used to do validation on a form.
21105 * @param {Roo.bootstrap.TabPanel} this
21106 * @return {Boolean} false if there is an error
21109 'beforedeactivate': true
21112 this.tabId = this.tabId || Roo.id();
21116 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
21123 touchSlide : false,
21124 getAutoCreate : function(){
21129 // item is needed for carousel - not sure if it has any effect otherwise
21130 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
21131 html: this.html || ''
21135 cfg.cls += ' active';
21139 cfg.tabId = this.tabId;
21147 initEvents: function()
21149 var p = this.parent();
21151 this.navId = this.navId || p.navId;
21153 if (typeof(this.navId) != 'undefined') {
21154 // not really needed.. but just in case.. parent should be a NavGroup.
21155 var tg = Roo.bootstrap.TabGroup.get(this.navId);
21159 var i = tg.tabs.length - 1;
21161 if(this.active && tg.bullets > 0 && i < tg.bullets){
21162 tg.setActiveBullet(i);
21166 this.el.on('click', this.onClick, this);
21168 if(Roo.isTouch && this.touchSlide){
21169 this.el.on("touchstart", this.onTouchStart, this);
21170 this.el.on("touchmove", this.onTouchMove, this);
21171 this.el.on("touchend", this.onTouchEnd, this);
21176 onRender : function(ct, position)
21178 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
21181 setActive : function(state)
21183 Roo.log("panel - set active " + this.tabId + "=" + state);
21185 this.active = state;
21187 this.el.removeClass('active');
21189 } else if (!this.el.hasClass('active')) {
21190 this.el.addClass('active');
21193 this.fireEvent('changed', this, state);
21196 onClick : function(e)
21198 e.preventDefault();
21200 if(!this.href.length){
21204 window.location.href = this.href;
21213 onTouchStart : function(e)
21215 this.swiping = false;
21217 this.startX = e.browserEvent.touches[0].clientX;
21218 this.startY = e.browserEvent.touches[0].clientY;
21221 onTouchMove : function(e)
21223 this.swiping = true;
21225 this.endX = e.browserEvent.touches[0].clientX;
21226 this.endY = e.browserEvent.touches[0].clientY;
21229 onTouchEnd : function(e)
21236 var tabGroup = this.parent();
21238 if(this.endX > this.startX){ // swiping right
21239 tabGroup.showPanelPrev();
21243 if(this.startX > this.endX){ // swiping left
21244 tabGroup.showPanelNext();
21263 * @class Roo.bootstrap.DateField
21264 * @extends Roo.bootstrap.Input
21265 * Bootstrap DateField class
21266 * @cfg {Number} weekStart default 0
21267 * @cfg {String} viewMode default empty, (months|years)
21268 * @cfg {String} minViewMode default empty, (months|years)
21269 * @cfg {Number} startDate default -Infinity
21270 * @cfg {Number} endDate default Infinity
21271 * @cfg {Boolean} todayHighlight default false
21272 * @cfg {Boolean} todayBtn default false
21273 * @cfg {Boolean} calendarWeeks default false
21274 * @cfg {Object} daysOfWeekDisabled default empty
21275 * @cfg {Boolean} singleMode default false (true | false)
21277 * @cfg {Boolean} keyboardNavigation default true
21278 * @cfg {String} language default en
21281 * Create a new DateField
21282 * @param {Object} config The config object
21285 Roo.bootstrap.DateField = function(config){
21286 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
21290 * Fires when this field show.
21291 * @param {Roo.bootstrap.DateField} this
21292 * @param {Mixed} date The date value
21297 * Fires when this field hide.
21298 * @param {Roo.bootstrap.DateField} this
21299 * @param {Mixed} date The date value
21304 * Fires when select a date.
21305 * @param {Roo.bootstrap.DateField} this
21306 * @param {Mixed} date The date value
21310 * @event beforeselect
21311 * Fires when before select a date.
21312 * @param {Roo.bootstrap.DateField} this
21313 * @param {Mixed} date The date value
21315 beforeselect : true
21319 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
21322 * @cfg {String} format
21323 * The default date format string which can be overriden for localization support. The format must be
21324 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
21328 * @cfg {String} altFormats
21329 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
21330 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21332 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21340 todayHighlight : false,
21346 keyboardNavigation: true,
21348 calendarWeeks: false,
21350 startDate: -Infinity,
21354 daysOfWeekDisabled: [],
21358 singleMode : false,
21360 UTCDate: function()
21362 return new Date(Date.UTC.apply(Date, arguments));
21365 UTCToday: function()
21367 var today = new Date();
21368 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21371 getDate: function() {
21372 var d = this.getUTCDate();
21373 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21376 getUTCDate: function() {
21380 setDate: function(d) {
21381 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21384 setUTCDate: function(d) {
21386 this.setValue(this.formatDate(this.date));
21389 onRender: function(ct, position)
21392 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21394 this.language = this.language || 'en';
21395 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21396 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21398 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21399 this.format = this.format || 'm/d/y';
21400 this.isInline = false;
21401 this.isInput = true;
21402 this.component = this.el.select('.add-on', true).first() || false;
21403 this.component = (this.component && this.component.length === 0) ? false : this.component;
21404 this.hasInput = this.component && this.inputEl().length;
21406 if (typeof(this.minViewMode === 'string')) {
21407 switch (this.minViewMode) {
21409 this.minViewMode = 1;
21412 this.minViewMode = 2;
21415 this.minViewMode = 0;
21420 if (typeof(this.viewMode === 'string')) {
21421 switch (this.viewMode) {
21434 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21436 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21438 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21440 this.picker().on('mousedown', this.onMousedown, this);
21441 this.picker().on('click', this.onClick, this);
21443 this.picker().addClass('datepicker-dropdown');
21445 this.startViewMode = this.viewMode;
21447 if(this.singleMode){
21448 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21449 v.setVisibilityMode(Roo.Element.DISPLAY);
21453 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21454 v.setStyle('width', '189px');
21458 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21459 if(!this.calendarWeeks){
21464 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21465 v.attr('colspan', function(i, val){
21466 return parseInt(val) + 1;
21471 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21473 this.setStartDate(this.startDate);
21474 this.setEndDate(this.endDate);
21476 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21483 if(this.isInline) {
21488 picker : function()
21490 return this.pickerEl;
21491 // return this.el.select('.datepicker', true).first();
21494 fillDow: function()
21496 var dowCnt = this.weekStart;
21505 if(this.calendarWeeks){
21513 while (dowCnt < this.weekStart + 7) {
21517 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21521 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21524 fillMonths: function()
21527 var months = this.picker().select('>.datepicker-months td', true).first();
21529 months.dom.innerHTML = '';
21535 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21538 months.createChild(month);
21545 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;
21547 if (this.date < this.startDate) {
21548 this.viewDate = new Date(this.startDate);
21549 } else if (this.date > this.endDate) {
21550 this.viewDate = new Date(this.endDate);
21552 this.viewDate = new Date(this.date);
21560 var d = new Date(this.viewDate),
21561 year = d.getUTCFullYear(),
21562 month = d.getUTCMonth(),
21563 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21564 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21565 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21566 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21567 currentDate = this.date && this.date.valueOf(),
21568 today = this.UTCToday();
21570 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21572 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21574 // this.picker.select('>tfoot th.today').
21575 // .text(dates[this.language].today)
21576 // .toggle(this.todayBtn !== false);
21578 this.updateNavArrows();
21581 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21583 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21585 prevMonth.setUTCDate(day);
21587 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21589 var nextMonth = new Date(prevMonth);
21591 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21593 nextMonth = nextMonth.valueOf();
21595 var fillMonths = false;
21597 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21599 while(prevMonth.valueOf() <= nextMonth) {
21602 if (prevMonth.getUTCDay() === this.weekStart) {
21604 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21612 if(this.calendarWeeks){
21613 // ISO 8601: First week contains first thursday.
21614 // ISO also states week starts on Monday, but we can be more abstract here.
21616 // Start of current week: based on weekstart/current date
21617 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21618 // Thursday of this week
21619 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21620 // First Thursday of year, year from thursday
21621 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21622 // Calendar week: ms between thursdays, div ms per day, div 7 days
21623 calWeek = (th - yth) / 864e5 / 7 + 1;
21625 fillMonths.cn.push({
21633 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21635 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21638 if (this.todayHighlight &&
21639 prevMonth.getUTCFullYear() == today.getFullYear() &&
21640 prevMonth.getUTCMonth() == today.getMonth() &&
21641 prevMonth.getUTCDate() == today.getDate()) {
21642 clsName += ' today';
21645 if (currentDate && prevMonth.valueOf() === currentDate) {
21646 clsName += ' active';
21649 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21650 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21651 clsName += ' disabled';
21654 fillMonths.cn.push({
21656 cls: 'day ' + clsName,
21657 html: prevMonth.getDate()
21660 prevMonth.setDate(prevMonth.getDate()+1);
21663 var currentYear = this.date && this.date.getUTCFullYear();
21664 var currentMonth = this.date && this.date.getUTCMonth();
21666 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21668 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21669 v.removeClass('active');
21671 if(currentYear === year && k === currentMonth){
21672 v.addClass('active');
21675 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21676 v.addClass('disabled');
21682 year = parseInt(year/10, 10) * 10;
21684 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21686 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21689 for (var i = -1; i < 11; i++) {
21690 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21692 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21700 showMode: function(dir)
21703 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21706 Roo.each(this.picker().select('>div',true).elements, function(v){
21707 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21710 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21715 if(this.isInline) {
21719 this.picker().removeClass(['bottom', 'top']);
21721 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21723 * place to the top of element!
21727 this.picker().addClass('top');
21728 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21733 this.picker().addClass('bottom');
21735 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21738 parseDate : function(value)
21740 if(!value || value instanceof Date){
21743 var v = Date.parseDate(value, this.format);
21744 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21745 v = Date.parseDate(value, 'Y-m-d');
21747 if(!v && this.altFormats){
21748 if(!this.altFormatsArray){
21749 this.altFormatsArray = this.altFormats.split("|");
21751 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21752 v = Date.parseDate(value, this.altFormatsArray[i]);
21758 formatDate : function(date, fmt)
21760 return (!date || !(date instanceof Date)) ?
21761 date : date.dateFormat(fmt || this.format);
21764 onFocus : function()
21766 Roo.bootstrap.DateField.superclass.onFocus.call(this);
21770 onBlur : function()
21772 Roo.bootstrap.DateField.superclass.onBlur.call(this);
21774 var d = this.inputEl().getValue();
21781 showPopup : function()
21783 this.picker().show();
21787 this.fireEvent('showpopup', this, this.date);
21790 hidePopup : function()
21792 if(this.isInline) {
21795 this.picker().hide();
21796 this.viewMode = this.startViewMode;
21799 this.fireEvent('hidepopup', this, this.date);
21803 onMousedown: function(e)
21805 e.stopPropagation();
21806 e.preventDefault();
21811 Roo.bootstrap.DateField.superclass.keyup.call(this);
21815 setValue: function(v)
21817 if(this.fireEvent('beforeselect', this, v) !== false){
21818 var d = new Date(this.parseDate(v) ).clearTime();
21820 if(isNaN(d.getTime())){
21821 this.date = this.viewDate = '';
21822 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21826 v = this.formatDate(d);
21828 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21830 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21834 this.fireEvent('select', this, this.date);
21838 getValue: function()
21840 return this.formatDate(this.date);
21843 fireKey: function(e)
21845 if (!this.picker().isVisible()){
21846 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21852 var dateChanged = false,
21854 newDate, newViewDate;
21859 e.preventDefault();
21863 if (!this.keyboardNavigation) {
21866 dir = e.keyCode == 37 ? -1 : 1;
21869 newDate = this.moveYear(this.date, dir);
21870 newViewDate = this.moveYear(this.viewDate, dir);
21871 } else if (e.shiftKey){
21872 newDate = this.moveMonth(this.date, dir);
21873 newViewDate = this.moveMonth(this.viewDate, dir);
21875 newDate = new Date(this.date);
21876 newDate.setUTCDate(this.date.getUTCDate() + dir);
21877 newViewDate = new Date(this.viewDate);
21878 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21880 if (this.dateWithinRange(newDate)){
21881 this.date = newDate;
21882 this.viewDate = newViewDate;
21883 this.setValue(this.formatDate(this.date));
21885 e.preventDefault();
21886 dateChanged = true;
21891 if (!this.keyboardNavigation) {
21894 dir = e.keyCode == 38 ? -1 : 1;
21896 newDate = this.moveYear(this.date, dir);
21897 newViewDate = this.moveYear(this.viewDate, dir);
21898 } else if (e.shiftKey){
21899 newDate = this.moveMonth(this.date, dir);
21900 newViewDate = this.moveMonth(this.viewDate, dir);
21902 newDate = new Date(this.date);
21903 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21904 newViewDate = new Date(this.viewDate);
21905 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21907 if (this.dateWithinRange(newDate)){
21908 this.date = newDate;
21909 this.viewDate = newViewDate;
21910 this.setValue(this.formatDate(this.date));
21912 e.preventDefault();
21913 dateChanged = true;
21917 this.setValue(this.formatDate(this.date));
21919 e.preventDefault();
21922 this.setValue(this.formatDate(this.date));
21936 onClick: function(e)
21938 e.stopPropagation();
21939 e.preventDefault();
21941 var target = e.getTarget();
21943 if(target.nodeName.toLowerCase() === 'i'){
21944 target = Roo.get(target).dom.parentNode;
21947 var nodeName = target.nodeName;
21948 var className = target.className;
21949 var html = target.innerHTML;
21950 //Roo.log(nodeName);
21952 switch(nodeName.toLowerCase()) {
21954 switch(className) {
21960 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21961 switch(this.viewMode){
21963 this.viewDate = this.moveMonth(this.viewDate, dir);
21967 this.viewDate = this.moveYear(this.viewDate, dir);
21973 var date = new Date();
21974 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21976 this.setValue(this.formatDate(this.date));
21983 if (className.indexOf('disabled') < 0) {
21984 if (!this.viewDate) {
21985 this.viewDate = new Date();
21987 this.viewDate.setUTCDate(1);
21988 if (className.indexOf('month') > -1) {
21989 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21991 var year = parseInt(html, 10) || 0;
21992 this.viewDate.setUTCFullYear(year);
21996 if(this.singleMode){
21997 this.setValue(this.formatDate(this.viewDate));
22008 //Roo.log(className);
22009 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
22010 var day = parseInt(html, 10) || 1;
22011 var year = (this.viewDate || new Date()).getUTCFullYear(),
22012 month = (this.viewDate || new Date()).getUTCMonth();
22014 if (className.indexOf('old') > -1) {
22021 } else if (className.indexOf('new') > -1) {
22029 //Roo.log([year,month,day]);
22030 this.date = this.UTCDate(year, month, day,0,0,0,0);
22031 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
22033 //Roo.log(this.formatDate(this.date));
22034 this.setValue(this.formatDate(this.date));
22041 setStartDate: function(startDate)
22043 this.startDate = startDate || -Infinity;
22044 if (this.startDate !== -Infinity) {
22045 this.startDate = this.parseDate(this.startDate);
22048 this.updateNavArrows();
22051 setEndDate: function(endDate)
22053 this.endDate = endDate || Infinity;
22054 if (this.endDate !== Infinity) {
22055 this.endDate = this.parseDate(this.endDate);
22058 this.updateNavArrows();
22061 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
22063 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
22064 if (typeof(this.daysOfWeekDisabled) !== 'object') {
22065 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
22067 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
22068 return parseInt(d, 10);
22071 this.updateNavArrows();
22074 updateNavArrows: function()
22076 if(this.singleMode){
22080 var d = new Date(this.viewDate),
22081 year = d.getUTCFullYear(),
22082 month = d.getUTCMonth();
22084 Roo.each(this.picker().select('.prev', true).elements, function(v){
22086 switch (this.viewMode) {
22089 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
22095 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
22102 Roo.each(this.picker().select('.next', true).elements, function(v){
22104 switch (this.viewMode) {
22107 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
22113 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
22121 moveMonth: function(date, dir)
22126 var new_date = new Date(date.valueOf()),
22127 day = new_date.getUTCDate(),
22128 month = new_date.getUTCMonth(),
22129 mag = Math.abs(dir),
22131 dir = dir > 0 ? 1 : -1;
22134 // If going back one month, make sure month is not current month
22135 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
22137 return new_date.getUTCMonth() == month;
22139 // If going forward one month, make sure month is as expected
22140 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
22142 return new_date.getUTCMonth() != new_month;
22144 new_month = month + dir;
22145 new_date.setUTCMonth(new_month);
22146 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
22147 if (new_month < 0 || new_month > 11) {
22148 new_month = (new_month + 12) % 12;
22151 // For magnitudes >1, move one month at a time...
22152 for (var i=0; i<mag; i++) {
22153 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
22154 new_date = this.moveMonth(new_date, dir);
22156 // ...then reset the day, keeping it in the new month
22157 new_month = new_date.getUTCMonth();
22158 new_date.setUTCDate(day);
22160 return new_month != new_date.getUTCMonth();
22163 // Common date-resetting loop -- if date is beyond end of month, make it
22166 new_date.setUTCDate(--day);
22167 new_date.setUTCMonth(new_month);
22172 moveYear: function(date, dir)
22174 return this.moveMonth(date, dir*12);
22177 dateWithinRange: function(date)
22179 return date >= this.startDate && date <= this.endDate;
22185 this.picker().remove();
22188 validateValue : function(value)
22190 if(this.getVisibilityEl().hasClass('hidden')){
22194 if(value.length < 1) {
22195 if(this.allowBlank){
22201 if(value.length < this.minLength){
22204 if(value.length > this.maxLength){
22208 var vt = Roo.form.VTypes;
22209 if(!vt[this.vtype](value, this)){
22213 if(typeof this.validator == "function"){
22214 var msg = this.validator(value);
22220 if(this.regex && !this.regex.test(value)){
22224 if(typeof(this.parseDate(value)) == 'undefined'){
22228 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
22232 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
22242 this.date = this.viewDate = '';
22244 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22249 Roo.apply(Roo.bootstrap.DateField, {
22260 html: '<i class="fa fa-arrow-left"/>'
22270 html: '<i class="fa fa-arrow-right"/>'
22312 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
22313 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
22314 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
22315 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22316 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
22329 navFnc: 'FullYear',
22334 navFnc: 'FullYear',
22339 Roo.apply(Roo.bootstrap.DateField, {
22343 cls: 'datepicker dropdown-menu roo-dynamic shadow',
22347 cls: 'datepicker-days',
22351 cls: 'table-condensed',
22353 Roo.bootstrap.DateField.head,
22357 Roo.bootstrap.DateField.footer
22364 cls: 'datepicker-months',
22368 cls: 'table-condensed',
22370 Roo.bootstrap.DateField.head,
22371 Roo.bootstrap.DateField.content,
22372 Roo.bootstrap.DateField.footer
22379 cls: 'datepicker-years',
22383 cls: 'table-condensed',
22385 Roo.bootstrap.DateField.head,
22386 Roo.bootstrap.DateField.content,
22387 Roo.bootstrap.DateField.footer
22406 * @class Roo.bootstrap.TimeField
22407 * @extends Roo.bootstrap.Input
22408 * Bootstrap DateField class
22412 * Create a new TimeField
22413 * @param {Object} config The config object
22416 Roo.bootstrap.TimeField = function(config){
22417 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22421 * Fires when this field show.
22422 * @param {Roo.bootstrap.DateField} thisthis
22423 * @param {Mixed} date The date value
22428 * Fires when this field hide.
22429 * @param {Roo.bootstrap.DateField} this
22430 * @param {Mixed} date The date value
22435 * Fires when select a date.
22436 * @param {Roo.bootstrap.DateField} this
22437 * @param {Mixed} date The date value
22443 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
22446 * @cfg {String} format
22447 * The default time format string which can be overriden for localization support. The format must be
22448 * valid according to {@link Date#parseDate} (defaults to 'H:i').
22452 getAutoCreate : function()
22454 this.after = '<i class="fa far fa-clock"></i>';
22455 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22459 onRender: function(ct, position)
22462 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22464 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22466 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22468 this.pop = this.picker().select('>.datepicker-time',true).first();
22469 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22471 this.picker().on('mousedown', this.onMousedown, this);
22472 this.picker().on('click', this.onClick, this);
22474 this.picker().addClass('datepicker-dropdown');
22479 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22480 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22481 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22482 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22483 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22484 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22488 fireKey: function(e){
22489 if (!this.picker().isVisible()){
22490 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22496 e.preventDefault();
22504 this.onTogglePeriod();
22507 this.onIncrementMinutes();
22510 this.onDecrementMinutes();
22519 onClick: function(e) {
22520 e.stopPropagation();
22521 e.preventDefault();
22524 picker : function()
22526 return this.pickerEl;
22529 fillTime: function()
22531 var time = this.pop.select('tbody', true).first();
22533 time.dom.innerHTML = '';
22548 cls: 'hours-up fa fas fa-chevron-up'
22568 cls: 'minutes-up fa fas fa-chevron-up'
22589 cls: 'timepicker-hour',
22604 cls: 'timepicker-minute',
22619 cls: 'btn btn-primary period',
22641 cls: 'hours-down fa fas fa-chevron-down'
22661 cls: 'minutes-down fa fas fa-chevron-down'
22679 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22686 var hours = this.time.getHours();
22687 var minutes = this.time.getMinutes();
22700 hours = hours - 12;
22704 hours = '0' + hours;
22708 minutes = '0' + minutes;
22711 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22712 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22713 this.pop.select('button', true).first().dom.innerHTML = period;
22719 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22721 var cls = ['bottom'];
22723 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22730 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22734 //this.picker().setXY(20000,20000);
22735 this.picker().addClass(cls.join('-'));
22739 Roo.each(cls, function(c){
22744 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
22745 //_this.picker().setTop(_this.inputEl().getHeight());
22749 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
22751 //_this.picker().setTop(0 - _this.picker().getHeight());
22756 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22760 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22768 onFocus : function()
22770 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22774 onBlur : function()
22776 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22782 this.picker().show();
22787 this.fireEvent('show', this, this.date);
22792 this.picker().hide();
22795 this.fireEvent('hide', this, this.date);
22798 setTime : function()
22801 this.setValue(this.time.format(this.format));
22803 this.fireEvent('select', this, this.date);
22808 onMousedown: function(e){
22809 e.stopPropagation();
22810 e.preventDefault();
22813 onIncrementHours: function()
22815 Roo.log('onIncrementHours');
22816 this.time = this.time.add(Date.HOUR, 1);
22821 onDecrementHours: function()
22823 Roo.log('onDecrementHours');
22824 this.time = this.time.add(Date.HOUR, -1);
22828 onIncrementMinutes: function()
22830 Roo.log('onIncrementMinutes');
22831 this.time = this.time.add(Date.MINUTE, 1);
22835 onDecrementMinutes: function()
22837 Roo.log('onDecrementMinutes');
22838 this.time = this.time.add(Date.MINUTE, -1);
22842 onTogglePeriod: function()
22844 Roo.log('onTogglePeriod');
22845 this.time = this.time.add(Date.HOUR, 12);
22853 Roo.apply(Roo.bootstrap.TimeField, {
22857 cls: 'datepicker dropdown-menu',
22861 cls: 'datepicker-time',
22865 cls: 'table-condensed',
22894 cls: 'btn btn-info ok',
22922 * @class Roo.bootstrap.MonthField
22923 * @extends Roo.bootstrap.Input
22924 * Bootstrap MonthField class
22926 * @cfg {String} language default en
22929 * Create a new MonthField
22930 * @param {Object} config The config object
22933 Roo.bootstrap.MonthField = function(config){
22934 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22939 * Fires when this field show.
22940 * @param {Roo.bootstrap.MonthField} this
22941 * @param {Mixed} date The date value
22946 * Fires when this field hide.
22947 * @param {Roo.bootstrap.MonthField} this
22948 * @param {Mixed} date The date value
22953 * Fires when select a date.
22954 * @param {Roo.bootstrap.MonthField} this
22955 * @param {String} oldvalue The old value
22956 * @param {String} newvalue The new value
22962 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
22964 onRender: function(ct, position)
22967 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22969 this.language = this.language || 'en';
22970 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22971 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22973 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22974 this.isInline = false;
22975 this.isInput = true;
22976 this.component = this.el.select('.add-on', true).first() || false;
22977 this.component = (this.component && this.component.length === 0) ? false : this.component;
22978 this.hasInput = this.component && this.inputEL().length;
22980 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22982 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22984 this.picker().on('mousedown', this.onMousedown, this);
22985 this.picker().on('click', this.onClick, this);
22987 this.picker().addClass('datepicker-dropdown');
22989 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22990 v.setStyle('width', '189px');
22997 if(this.isInline) {
23003 setValue: function(v, suppressEvent)
23005 var o = this.getValue();
23007 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
23011 if(suppressEvent !== true){
23012 this.fireEvent('select', this, o, v);
23017 getValue: function()
23022 onClick: function(e)
23024 e.stopPropagation();
23025 e.preventDefault();
23027 var target = e.getTarget();
23029 if(target.nodeName.toLowerCase() === 'i'){
23030 target = Roo.get(target).dom.parentNode;
23033 var nodeName = target.nodeName;
23034 var className = target.className;
23035 var html = target.innerHTML;
23037 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
23041 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
23043 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23049 picker : function()
23051 return this.pickerEl;
23054 fillMonths: function()
23057 var months = this.picker().select('>.datepicker-months td', true).first();
23059 months.dom.innerHTML = '';
23065 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
23068 months.createChild(month);
23077 if(typeof(this.vIndex) == 'undefined' && this.value.length){
23078 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
23081 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
23082 e.removeClass('active');
23084 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
23085 e.addClass('active');
23092 if(this.isInline) {
23096 this.picker().removeClass(['bottom', 'top']);
23098 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23100 * place to the top of element!
23104 this.picker().addClass('top');
23105 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23110 this.picker().addClass('bottom');
23112 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23115 onFocus : function()
23117 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
23121 onBlur : function()
23123 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
23125 var d = this.inputEl().getValue();
23134 this.picker().show();
23135 this.picker().select('>.datepicker-months', true).first().show();
23139 this.fireEvent('show', this, this.date);
23144 if(this.isInline) {
23147 this.picker().hide();
23148 this.fireEvent('hide', this, this.date);
23152 onMousedown: function(e)
23154 e.stopPropagation();
23155 e.preventDefault();
23160 Roo.bootstrap.MonthField.superclass.keyup.call(this);
23164 fireKey: function(e)
23166 if (!this.picker().isVisible()){
23167 if (e.keyCode == 27) {// allow escape to hide and re-show picker
23178 e.preventDefault();
23182 dir = e.keyCode == 37 ? -1 : 1;
23184 this.vIndex = this.vIndex + dir;
23186 if(this.vIndex < 0){
23190 if(this.vIndex > 11){
23194 if(isNaN(this.vIndex)){
23198 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23204 dir = e.keyCode == 38 ? -1 : 1;
23206 this.vIndex = this.vIndex + dir * 4;
23208 if(this.vIndex < 0){
23212 if(this.vIndex > 11){
23216 if(isNaN(this.vIndex)){
23220 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23225 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23226 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23230 e.preventDefault();
23233 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23234 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23250 this.picker().remove();
23255 Roo.apply(Roo.bootstrap.MonthField, {
23274 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23275 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
23280 Roo.apply(Roo.bootstrap.MonthField, {
23284 cls: 'datepicker dropdown-menu roo-dynamic',
23288 cls: 'datepicker-months',
23292 cls: 'table-condensed',
23294 Roo.bootstrap.DateField.content
23314 * @class Roo.bootstrap.CheckBox
23315 * @extends Roo.bootstrap.Input
23316 * Bootstrap CheckBox class
23318 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23319 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
23320 * @cfg {String} boxLabel The text that appears beside the checkbox
23321 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
23322 * @cfg {Boolean} checked initnal the element
23323 * @cfg {Boolean} inline inline the element (default false)
23324 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
23325 * @cfg {String} tooltip label tooltip
23328 * Create a new CheckBox
23329 * @param {Object} config The config object
23332 Roo.bootstrap.CheckBox = function(config){
23333 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23338 * Fires when the element is checked or unchecked.
23339 * @param {Roo.bootstrap.CheckBox} this This input
23340 * @param {Boolean} checked The new checked value
23345 * Fires when the element is click.
23346 * @param {Roo.bootstrap.CheckBox} this This input
23353 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
23355 inputType: 'checkbox',
23364 // checkbox success does not make any sense really..
23369 getAutoCreate : function()
23371 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23377 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23380 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
23386 type : this.inputType,
23387 value : this.inputValue,
23388 cls : 'roo-' + this.inputType, //'form-box',
23389 placeholder : this.placeholder || ''
23393 if(this.inputType != 'radio'){
23397 cls : 'roo-hidden-value',
23398 value : this.checked ? this.inputValue : this.valueOff
23403 if (this.weight) { // Validity check?
23404 cfg.cls += " " + this.inputType + "-" + this.weight;
23407 if (this.disabled) {
23408 input.disabled=true;
23412 input.checked = this.checked;
23417 input.name = this.name;
23419 if(this.inputType != 'radio'){
23420 hidden.name = this.name;
23421 input.name = '_hidden_' + this.name;
23426 input.cls += ' input-' + this.size;
23431 ['xs','sm','md','lg'].map(function(size){
23432 if (settings[size]) {
23433 cfg.cls += ' col-' + size + '-' + settings[size];
23437 var inputblock = input;
23439 if (this.before || this.after) {
23442 cls : 'input-group',
23447 inputblock.cn.push({
23449 cls : 'input-group-addon',
23454 inputblock.cn.push(input);
23456 if(this.inputType != 'radio'){
23457 inputblock.cn.push(hidden);
23461 inputblock.cn.push({
23463 cls : 'input-group-addon',
23469 var boxLabelCfg = false;
23475 //'for': id, // box label is handled by onclick - so no for...
23477 html: this.boxLabel
23480 boxLabelCfg.tooltip = this.tooltip;
23486 if (align ==='left' && this.fieldLabel.length) {
23487 // Roo.log("left and has label");
23492 cls : 'control-label',
23493 html : this.fieldLabel
23504 cfg.cn[1].cn.push(boxLabelCfg);
23507 if(this.labelWidth > 12){
23508 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23511 if(this.labelWidth < 13 && this.labelmd == 0){
23512 this.labelmd = this.labelWidth;
23515 if(this.labellg > 0){
23516 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23517 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23520 if(this.labelmd > 0){
23521 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23522 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23525 if(this.labelsm > 0){
23526 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23527 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23530 if(this.labelxs > 0){
23531 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23532 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23535 } else if ( this.fieldLabel.length) {
23536 // Roo.log(" label");
23540 tag: this.boxLabel ? 'span' : 'label',
23542 cls: 'control-label box-input-label',
23543 //cls : 'input-group-addon',
23544 html : this.fieldLabel
23551 cfg.cn.push(boxLabelCfg);
23556 // Roo.log(" no label && no align");
23557 cfg.cn = [ inputblock ] ;
23559 cfg.cn.push(boxLabelCfg);
23567 if(this.inputType != 'radio'){
23568 cfg.cn.push(hidden);
23576 * return the real input element.
23578 inputEl: function ()
23580 return this.el.select('input.roo-' + this.inputType,true).first();
23582 hiddenEl: function ()
23584 return this.el.select('input.roo-hidden-value',true).first();
23587 labelEl: function()
23589 return this.el.select('label.control-label',true).first();
23591 /* depricated... */
23595 return this.labelEl();
23598 boxLabelEl: function()
23600 return this.el.select('label.box-label',true).first();
23603 initEvents : function()
23605 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23607 this.inputEl().on('click', this.onClick, this);
23609 if (this.boxLabel) {
23610 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
23613 this.startValue = this.getValue();
23616 Roo.bootstrap.CheckBox.register(this);
23620 onClick : function(e)
23622 if(this.fireEvent('click', this, e) !== false){
23623 this.setChecked(!this.checked);
23628 setChecked : function(state,suppressEvent)
23630 this.startValue = this.getValue();
23632 if(this.inputType == 'radio'){
23634 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23635 e.dom.checked = false;
23638 this.inputEl().dom.checked = true;
23640 this.inputEl().dom.value = this.inputValue;
23642 if(suppressEvent !== true){
23643 this.fireEvent('check', this, true);
23651 this.checked = state;
23653 this.inputEl().dom.checked = state;
23656 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23658 if(suppressEvent !== true){
23659 this.fireEvent('check', this, state);
23665 getValue : function()
23667 if(this.inputType == 'radio'){
23668 return this.getGroupValue();
23671 return this.hiddenEl().dom.value;
23675 getGroupValue : function()
23677 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23681 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23684 setValue : function(v,suppressEvent)
23686 if(this.inputType == 'radio'){
23687 this.setGroupValue(v, suppressEvent);
23691 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23696 setGroupValue : function(v, suppressEvent)
23698 this.startValue = this.getValue();
23700 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23701 e.dom.checked = false;
23703 if(e.dom.value == v){
23704 e.dom.checked = true;
23708 if(suppressEvent !== true){
23709 this.fireEvent('check', this, true);
23717 validate : function()
23719 if(this.getVisibilityEl().hasClass('hidden')){
23725 (this.inputType == 'radio' && this.validateRadio()) ||
23726 (this.inputType == 'checkbox' && this.validateCheckbox())
23732 this.markInvalid();
23736 validateRadio : function()
23738 if(this.getVisibilityEl().hasClass('hidden')){
23742 if(this.allowBlank){
23748 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23749 if(!e.dom.checked){
23761 validateCheckbox : function()
23764 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23765 //return (this.getValue() == this.inputValue) ? true : false;
23768 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23776 for(var i in group){
23777 if(group[i].el.isVisible(true)){
23785 for(var i in group){
23790 r = (group[i].getValue() == group[i].inputValue) ? true : false;
23797 * Mark this field as valid
23799 markValid : function()
23803 this.fireEvent('valid', this);
23805 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23808 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23815 if(this.inputType == 'radio'){
23816 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23817 var fg = e.findParent('.form-group', false, true);
23818 if (Roo.bootstrap.version == 3) {
23819 fg.removeClass([_this.invalidClass, _this.validClass]);
23820 fg.addClass(_this.validClass);
23822 fg.removeClass(['is-valid', 'is-invalid']);
23823 fg.addClass('is-valid');
23831 var fg = this.el.findParent('.form-group', false, true);
23832 if (Roo.bootstrap.version == 3) {
23833 fg.removeClass([this.invalidClass, this.validClass]);
23834 fg.addClass(this.validClass);
23836 fg.removeClass(['is-valid', 'is-invalid']);
23837 fg.addClass('is-valid');
23842 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23848 for(var i in group){
23849 var fg = group[i].el.findParent('.form-group', false, true);
23850 if (Roo.bootstrap.version == 3) {
23851 fg.removeClass([this.invalidClass, this.validClass]);
23852 fg.addClass(this.validClass);
23854 fg.removeClass(['is-valid', 'is-invalid']);
23855 fg.addClass('is-valid');
23861 * Mark this field as invalid
23862 * @param {String} msg The validation message
23864 markInvalid : function(msg)
23866 if(this.allowBlank){
23872 this.fireEvent('invalid', this, msg);
23874 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23877 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23881 label.markInvalid();
23884 if(this.inputType == 'radio'){
23886 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23887 var fg = e.findParent('.form-group', false, true);
23888 if (Roo.bootstrap.version == 3) {
23889 fg.removeClass([_this.invalidClass, _this.validClass]);
23890 fg.addClass(_this.invalidClass);
23892 fg.removeClass(['is-invalid', 'is-valid']);
23893 fg.addClass('is-invalid');
23901 var fg = this.el.findParent('.form-group', false, true);
23902 if (Roo.bootstrap.version == 3) {
23903 fg.removeClass([_this.invalidClass, _this.validClass]);
23904 fg.addClass(_this.invalidClass);
23906 fg.removeClass(['is-invalid', 'is-valid']);
23907 fg.addClass('is-invalid');
23912 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23918 for(var i in group){
23919 var fg = group[i].el.findParent('.form-group', false, true);
23920 if (Roo.bootstrap.version == 3) {
23921 fg.removeClass([_this.invalidClass, _this.validClass]);
23922 fg.addClass(_this.invalidClass);
23924 fg.removeClass(['is-invalid', 'is-valid']);
23925 fg.addClass('is-invalid');
23931 clearInvalid : function()
23933 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23935 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23937 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23939 if (label && label.iconEl) {
23940 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23941 label.iconEl.removeClass(['is-invalid', 'is-valid']);
23945 disable : function()
23947 if(this.inputType != 'radio'){
23948 Roo.bootstrap.CheckBox.superclass.disable.call(this);
23955 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23956 _this.getActionEl().addClass(this.disabledClass);
23957 e.dom.disabled = true;
23961 this.disabled = true;
23962 this.fireEvent("disable", this);
23966 enable : function()
23968 if(this.inputType != 'radio'){
23969 Roo.bootstrap.CheckBox.superclass.enable.call(this);
23976 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23977 _this.getActionEl().removeClass(this.disabledClass);
23978 e.dom.disabled = false;
23982 this.disabled = false;
23983 this.fireEvent("enable", this);
23987 setBoxLabel : function(v)
23992 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23998 Roo.apply(Roo.bootstrap.CheckBox, {
24003 * register a CheckBox Group
24004 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
24006 register : function(checkbox)
24008 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
24009 this.groups[checkbox.groupId] = {};
24012 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
24016 this.groups[checkbox.groupId][checkbox.name] = checkbox;
24020 * fetch a CheckBox Group based on the group ID
24021 * @param {string} the group ID
24022 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
24024 get: function(groupId) {
24025 if (typeof(this.groups[groupId]) == 'undefined') {
24029 return this.groups[groupId] ;
24042 * @class Roo.bootstrap.Radio
24043 * @extends Roo.bootstrap.Component
24044 * Bootstrap Radio class
24045 * @cfg {String} boxLabel - the label associated
24046 * @cfg {String} value - the value of radio
24049 * Create a new Radio
24050 * @param {Object} config The config object
24052 Roo.bootstrap.Radio = function(config){
24053 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
24057 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
24063 getAutoCreate : function()
24067 cls : 'form-group radio',
24072 html : this.boxLabel
24080 initEvents : function()
24082 this.parent().register(this);
24084 this.el.on('click', this.onClick, this);
24088 onClick : function(e)
24090 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
24091 this.setChecked(true);
24095 setChecked : function(state, suppressEvent)
24097 this.parent().setValue(this.value, suppressEvent);
24101 setBoxLabel : function(v)
24106 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24121 * @class Roo.bootstrap.SecurePass
24122 * @extends Roo.bootstrap.Input
24123 * Bootstrap SecurePass class
24127 * Create a new SecurePass
24128 * @param {Object} config The config object
24131 Roo.bootstrap.SecurePass = function (config) {
24132 // these go here, so the translation tool can replace them..
24134 PwdEmpty: "Please type a password, and then retype it to confirm.",
24135 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24136 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24137 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24138 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24139 FNInPwd: "Your password can't contain your first name. Please type a different password.",
24140 LNInPwd: "Your password can't contain your last name. Please type a different password.",
24141 TooWeak: "Your password is Too Weak."
24143 this.meterLabel = "Password strength:";
24144 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
24145 this.meterClass = [
24146 "roo-password-meter-tooweak",
24147 "roo-password-meter-weak",
24148 "roo-password-meter-medium",
24149 "roo-password-meter-strong",
24150 "roo-password-meter-grey"
24155 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
24158 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
24160 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
24162 * PwdEmpty: "Please type a password, and then retype it to confirm.",
24163 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24164 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24165 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24166 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24167 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
24168 * LNInPwd: "Your password can't contain your last name. Please type a different password."
24178 * @cfg {String/Object} Label for the strength meter (defaults to
24179 * 'Password strength:')
24184 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
24185 * ['Weak', 'Medium', 'Strong'])
24188 pwdStrengths: false,
24201 initEvents: function ()
24203 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
24205 if (this.el.is('input[type=password]') && Roo.isSafari) {
24206 this.el.on('keydown', this.SafariOnKeyDown, this);
24209 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
24212 onRender: function (ct, position)
24214 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
24215 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
24216 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
24218 this.trigger.createChild({
24223 cls: 'roo-password-meter-grey col-xs-12',
24226 //width: this.meterWidth + 'px'
24230 cls: 'roo-password-meter-text'
24236 if (this.hideTrigger) {
24237 this.trigger.setDisplayed(false);
24239 this.setSize(this.width || '', this.height || '');
24242 onDestroy: function ()
24244 if (this.trigger) {
24245 this.trigger.removeAllListeners();
24246 this.trigger.remove();
24249 this.wrap.remove();
24251 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
24254 checkStrength: function ()
24256 var pwd = this.inputEl().getValue();
24257 if (pwd == this._lastPwd) {
24262 if (this.ClientSideStrongPassword(pwd)) {
24264 } else if (this.ClientSideMediumPassword(pwd)) {
24266 } else if (this.ClientSideWeakPassword(pwd)) {
24272 Roo.log('strength1: ' + strength);
24274 //var pm = this.trigger.child('div/div/div').dom;
24275 var pm = this.trigger.child('div/div');
24276 pm.removeClass(this.meterClass);
24277 pm.addClass(this.meterClass[strength]);
24280 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24282 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24284 this._lastPwd = pwd;
24288 Roo.bootstrap.SecurePass.superclass.reset.call(this);
24290 this._lastPwd = '';
24292 var pm = this.trigger.child('div/div');
24293 pm.removeClass(this.meterClass);
24294 pm.addClass('roo-password-meter-grey');
24297 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24300 this.inputEl().dom.type='password';
24303 validateValue: function (value)
24305 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
24308 if (value.length == 0) {
24309 if (this.allowBlank) {
24310 this.clearInvalid();
24314 this.markInvalid(this.errors.PwdEmpty);
24315 this.errorMsg = this.errors.PwdEmpty;
24323 if (!value.match(/[\x21-\x7e]+/)) {
24324 this.markInvalid(this.errors.PwdBadChar);
24325 this.errorMsg = this.errors.PwdBadChar;
24328 if (value.length < 6) {
24329 this.markInvalid(this.errors.PwdShort);
24330 this.errorMsg = this.errors.PwdShort;
24333 if (value.length > 16) {
24334 this.markInvalid(this.errors.PwdLong);
24335 this.errorMsg = this.errors.PwdLong;
24339 if (this.ClientSideStrongPassword(value)) {
24341 } else if (this.ClientSideMediumPassword(value)) {
24343 } else if (this.ClientSideWeakPassword(value)) {
24350 if (strength < 2) {
24351 //this.markInvalid(this.errors.TooWeak);
24352 this.errorMsg = this.errors.TooWeak;
24357 console.log('strength2: ' + strength);
24359 //var pm = this.trigger.child('div/div/div').dom;
24361 var pm = this.trigger.child('div/div');
24362 pm.removeClass(this.meterClass);
24363 pm.addClass(this.meterClass[strength]);
24365 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24367 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24369 this.errorMsg = '';
24373 CharacterSetChecks: function (type)
24376 this.fResult = false;
24379 isctype: function (character, type)
24382 case this.kCapitalLetter:
24383 if (character >= 'A' && character <= 'Z') {
24388 case this.kSmallLetter:
24389 if (character >= 'a' && character <= 'z') {
24395 if (character >= '0' && character <= '9') {
24400 case this.kPunctuation:
24401 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24412 IsLongEnough: function (pwd, size)
24414 return !(pwd == null || isNaN(size) || pwd.length < size);
24417 SpansEnoughCharacterSets: function (word, nb)
24419 if (!this.IsLongEnough(word, nb))
24424 var characterSetChecks = new Array(
24425 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24426 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24429 for (var index = 0; index < word.length; ++index) {
24430 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24431 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24432 characterSetChecks[nCharSet].fResult = true;
24439 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24440 if (characterSetChecks[nCharSet].fResult) {
24445 if (nCharSets < nb) {
24451 ClientSideStrongPassword: function (pwd)
24453 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24456 ClientSideMediumPassword: function (pwd)
24458 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24461 ClientSideWeakPassword: function (pwd)
24463 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24466 })//<script type="text/javascript">
24469 * Based Ext JS Library 1.1.1
24470 * Copyright(c) 2006-2007, Ext JS, LLC.
24476 * @class Roo.HtmlEditorCore
24477 * @extends Roo.Component
24478 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24480 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24483 Roo.HtmlEditorCore = function(config){
24486 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24491 * @event initialize
24492 * Fires when the editor is fully initialized (including the iframe)
24493 * @param {Roo.HtmlEditorCore} this
24498 * Fires when the editor is first receives the focus. Any insertion must wait
24499 * until after this event.
24500 * @param {Roo.HtmlEditorCore} this
24504 * @event beforesync
24505 * Fires before the textarea is updated with content from the editor iframe. Return false
24506 * to cancel the sync.
24507 * @param {Roo.HtmlEditorCore} this
24508 * @param {String} html
24512 * @event beforepush
24513 * Fires before the iframe editor is updated with content from the textarea. Return false
24514 * to cancel the push.
24515 * @param {Roo.HtmlEditorCore} this
24516 * @param {String} html
24521 * Fires when the textarea is updated with content from the editor iframe.
24522 * @param {Roo.HtmlEditorCore} this
24523 * @param {String} html
24528 * Fires when the iframe editor is updated with content from the textarea.
24529 * @param {Roo.HtmlEditorCore} this
24530 * @param {String} html
24535 * @event editorevent
24536 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24537 * @param {Roo.HtmlEditorCore} this
24543 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24545 // defaults : white / black...
24546 this.applyBlacklists();
24553 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
24557 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
24563 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
24568 * @cfg {Number} height (in pixels)
24572 * @cfg {Number} width (in pixels)
24577 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24580 stylesheets: false,
24585 // private properties
24586 validationEvent : false,
24588 initialized : false,
24590 sourceEditMode : false,
24591 onFocus : Roo.emptyFn,
24593 hideMode:'offsets',
24597 // blacklist + whitelisted elements..
24604 * Protected method that will not generally be called directly. It
24605 * is called when the editor initializes the iframe with HTML contents. Override this method if you
24606 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24608 getDocMarkup : function(){
24612 // inherit styels from page...??
24613 if (this.stylesheets === false) {
24615 Roo.get(document.head).select('style').each(function(node) {
24616 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24619 Roo.get(document.head).select('link').each(function(node) {
24620 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24623 } else if (!this.stylesheets.length) {
24625 st = '<style type="text/css">' +
24626 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24629 for (var i in this.stylesheets) {
24630 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24635 st += '<style type="text/css">' +
24636 'IMG { cursor: pointer } ' +
24639 var cls = 'roo-htmleditor-body';
24641 if(this.bodyCls.length){
24642 cls += ' ' + this.bodyCls;
24645 return '<html><head>' + st +
24646 //<style type="text/css">' +
24647 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24649 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
24653 onRender : function(ct, position)
24656 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24657 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24660 this.el.dom.style.border = '0 none';
24661 this.el.dom.setAttribute('tabIndex', -1);
24662 this.el.addClass('x-hidden hide');
24666 if(Roo.isIE){ // fix IE 1px bogus margin
24667 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24671 this.frameId = Roo.id();
24675 var iframe = this.owner.wrap.createChild({
24677 cls: 'form-control', // bootstrap..
24679 name: this.frameId,
24680 frameBorder : 'no',
24681 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
24686 this.iframe = iframe.dom;
24688 this.assignDocWin();
24690 this.doc.designMode = 'on';
24693 this.doc.write(this.getDocMarkup());
24697 var task = { // must defer to wait for browser to be ready
24699 //console.log("run task?" + this.doc.readyState);
24700 this.assignDocWin();
24701 if(this.doc.body || this.doc.readyState == 'complete'){
24703 this.doc.designMode="on";
24707 Roo.TaskMgr.stop(task);
24708 this.initEditor.defer(10, this);
24715 Roo.TaskMgr.start(task);
24720 onResize : function(w, h)
24722 Roo.log('resize: ' +w + ',' + h );
24723 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24727 if(typeof w == 'number'){
24729 this.iframe.style.width = w + 'px';
24731 if(typeof h == 'number'){
24733 this.iframe.style.height = h + 'px';
24735 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24742 * Toggles the editor between standard and source edit mode.
24743 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24745 toggleSourceEdit : function(sourceEditMode){
24747 this.sourceEditMode = sourceEditMode === true;
24749 if(this.sourceEditMode){
24751 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
24754 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24755 //this.iframe.className = '';
24758 //this.setSize(this.owner.wrap.getSize());
24759 //this.fireEvent('editmodechange', this, this.sourceEditMode);
24766 * Protected method that will not generally be called directly. If you need/want
24767 * custom HTML cleanup, this is the method you should override.
24768 * @param {String} html The HTML to be cleaned
24769 * return {String} The cleaned HTML
24771 cleanHtml : function(html){
24772 html = String(html);
24773 if(html.length > 5){
24774 if(Roo.isSafari){ // strip safari nonsense
24775 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24778 if(html == ' '){
24785 * HTML Editor -> Textarea
24786 * Protected method that will not generally be called directly. Syncs the contents
24787 * of the editor iframe with the textarea.
24789 syncValue : function(){
24790 if(this.initialized){
24791 var bd = (this.doc.body || this.doc.documentElement);
24792 //this.cleanUpPaste(); -- this is done else where and causes havoc..
24793 var html = bd.innerHTML;
24795 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24796 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24798 html = '<div style="'+m[0]+'">' + html + '</div>';
24801 html = this.cleanHtml(html);
24802 // fix up the special chars.. normaly like back quotes in word...
24803 // however we do not want to do this with chinese..
24804 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24806 var cc = match.charCodeAt();
24808 // Get the character value, handling surrogate pairs
24809 if (match.length == 2) {
24810 // It's a surrogate pair, calculate the Unicode code point
24811 var high = match.charCodeAt(0) - 0xD800;
24812 var low = match.charCodeAt(1) - 0xDC00;
24813 cc = (high * 0x400) + low + 0x10000;
24815 (cc >= 0x4E00 && cc < 0xA000 ) ||
24816 (cc >= 0x3400 && cc < 0x4E00 ) ||
24817 (cc >= 0xf900 && cc < 0xfb00 )
24822 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24823 return "&#" + cc + ";";
24830 if(this.owner.fireEvent('beforesync', this, html) !== false){
24831 this.el.dom.value = html;
24832 this.owner.fireEvent('sync', this, html);
24838 * Protected method that will not generally be called directly. Pushes the value of the textarea
24839 * into the iframe editor.
24841 pushValue : function(){
24842 if(this.initialized){
24843 var v = this.el.dom.value.trim();
24845 // if(v.length < 1){
24849 if(this.owner.fireEvent('beforepush', this, v) !== false){
24850 var d = (this.doc.body || this.doc.documentElement);
24852 this.cleanUpPaste();
24853 this.el.dom.value = d.innerHTML;
24854 this.owner.fireEvent('push', this, v);
24860 deferFocus : function(){
24861 this.focus.defer(10, this);
24865 focus : function(){
24866 if(this.win && !this.sourceEditMode){
24873 assignDocWin: function()
24875 var iframe = this.iframe;
24878 this.doc = iframe.contentWindow.document;
24879 this.win = iframe.contentWindow;
24881 // if (!Roo.get(this.frameId)) {
24884 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24885 // this.win = Roo.get(this.frameId).dom.contentWindow;
24887 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24891 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24892 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24897 initEditor : function(){
24898 //console.log("INIT EDITOR");
24899 this.assignDocWin();
24903 this.doc.designMode="on";
24905 this.doc.write(this.getDocMarkup());
24908 var dbody = (this.doc.body || this.doc.documentElement);
24909 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24910 // this copies styles from the containing element into thsi one..
24911 // not sure why we need all of this..
24912 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24914 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24915 //ss['background-attachment'] = 'fixed'; // w3c
24916 dbody.bgProperties = 'fixed'; // ie
24917 //Roo.DomHelper.applyStyles(dbody, ss);
24918 Roo.EventManager.on(this.doc, {
24919 //'mousedown': this.onEditorEvent,
24920 'mouseup': this.onEditorEvent,
24921 'dblclick': this.onEditorEvent,
24922 'click': this.onEditorEvent,
24923 'keyup': this.onEditorEvent,
24928 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24930 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24931 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24933 this.initialized = true;
24935 this.owner.fireEvent('initialize', this);
24940 onDestroy : function(){
24946 //for (var i =0; i < this.toolbars.length;i++) {
24947 // // fixme - ask toolbars for heights?
24948 // this.toolbars[i].onDestroy();
24951 //this.wrap.dom.innerHTML = '';
24952 //this.wrap.remove();
24957 onFirstFocus : function(){
24959 this.assignDocWin();
24962 this.activated = true;
24965 if(Roo.isGecko){ // prevent silly gecko errors
24967 var s = this.win.getSelection();
24968 if(!s.focusNode || s.focusNode.nodeType != 3){
24969 var r = s.getRangeAt(0);
24970 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24975 this.execCmd('useCSS', true);
24976 this.execCmd('styleWithCSS', false);
24979 this.owner.fireEvent('activate', this);
24983 adjustFont: function(btn){
24984 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24985 //if(Roo.isSafari){ // safari
24988 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24989 if(Roo.isSafari){ // safari
24990 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24991 v = (v < 10) ? 10 : v;
24992 v = (v > 48) ? 48 : v;
24993 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24998 v = Math.max(1, v+adjust);
25000 this.execCmd('FontSize', v );
25003 onEditorEvent : function(e)
25005 this.owner.fireEvent('editorevent', this, e);
25006 // this.updateToolbar();
25007 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25010 insertTag : function(tg)
25012 // could be a bit smarter... -> wrap the current selected tRoo..
25013 if (tg.toLowerCase() == 'span' ||
25014 tg.toLowerCase() == 'code' ||
25015 tg.toLowerCase() == 'sup' ||
25016 tg.toLowerCase() == 'sub'
25019 range = this.createRange(this.getSelection());
25020 var wrappingNode = this.doc.createElement(tg.toLowerCase());
25021 wrappingNode.appendChild(range.extractContents());
25022 range.insertNode(wrappingNode);
25029 this.execCmd("formatblock", tg);
25033 insertText : function(txt)
25037 var range = this.createRange();
25038 range.deleteContents();
25039 //alert(Sender.getAttribute('label'));
25041 range.insertNode(this.doc.createTextNode(txt));
25047 * Executes a Midas editor command on the editor document and performs necessary focus and
25048 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25049 * @param {String} cmd The Midas command
25050 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25052 relayCmd : function(cmd, value){
25054 this.execCmd(cmd, value);
25055 this.owner.fireEvent('editorevent', this);
25056 //this.updateToolbar();
25057 this.owner.deferFocus();
25061 * Executes a Midas editor command directly on the editor document.
25062 * For visual commands, you should use {@link #relayCmd} instead.
25063 * <b>This should only be called after the editor is initialized.</b>
25064 * @param {String} cmd The Midas command
25065 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25067 execCmd : function(cmd, value){
25068 this.doc.execCommand(cmd, false, value === undefined ? null : value);
25075 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25077 * @param {String} text | dom node..
25079 insertAtCursor : function(text)
25082 if(!this.activated){
25088 var r = this.doc.selection.createRange();
25099 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25103 // from jquery ui (MIT licenced)
25105 var win = this.win;
25107 if (win.getSelection && win.getSelection().getRangeAt) {
25108 range = win.getSelection().getRangeAt(0);
25109 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25110 range.insertNode(node);
25111 } else if (win.document.selection && win.document.selection.createRange) {
25112 // no firefox support
25113 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25114 win.document.selection.createRange().pasteHTML(txt);
25116 // no firefox support
25117 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25118 this.execCmd('InsertHTML', txt);
25127 mozKeyPress : function(e){
25129 var c = e.getCharCode(), cmd;
25132 c = String.fromCharCode(c).toLowerCase();
25146 this.cleanUpPaste.defer(100, this);
25154 e.preventDefault();
25162 fixKeys : function(){ // load time branching for fastest keydown performance
25164 return function(e){
25165 var k = e.getKey(), r;
25168 r = this.doc.selection.createRange();
25171 r.pasteHTML('    ');
25178 r = this.doc.selection.createRange();
25180 var target = r.parentElement();
25181 if(!target || target.tagName.toLowerCase() != 'li'){
25183 r.pasteHTML('<br />');
25189 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25190 this.cleanUpPaste.defer(100, this);
25196 }else if(Roo.isOpera){
25197 return function(e){
25198 var k = e.getKey();
25202 this.execCmd('InsertHTML','    ');
25205 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25206 this.cleanUpPaste.defer(100, this);
25211 }else if(Roo.isSafari){
25212 return function(e){
25213 var k = e.getKey();
25217 this.execCmd('InsertText','\t');
25221 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25222 this.cleanUpPaste.defer(100, this);
25230 getAllAncestors: function()
25232 var p = this.getSelectedNode();
25235 a.push(p); // push blank onto stack..
25236 p = this.getParentElement();
25240 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25244 a.push(this.doc.body);
25248 lastSelNode : false,
25251 getSelection : function()
25253 this.assignDocWin();
25254 return Roo.isIE ? this.doc.selection : this.win.getSelection();
25257 getSelectedNode: function()
25259 // this may only work on Gecko!!!
25261 // should we cache this!!!!
25266 var range = this.createRange(this.getSelection()).cloneRange();
25269 var parent = range.parentElement();
25271 var testRange = range.duplicate();
25272 testRange.moveToElementText(parent);
25273 if (testRange.inRange(range)) {
25276 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25279 parent = parent.parentElement;
25284 // is ancestor a text element.
25285 var ac = range.commonAncestorContainer;
25286 if (ac.nodeType == 3) {
25287 ac = ac.parentNode;
25290 var ar = ac.childNodes;
25293 var other_nodes = [];
25294 var has_other_nodes = false;
25295 for (var i=0;i<ar.length;i++) {
25296 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
25299 // fullly contained node.
25301 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25306 // probably selected..
25307 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25308 other_nodes.push(ar[i]);
25312 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
25317 has_other_nodes = true;
25319 if (!nodes.length && other_nodes.length) {
25320 nodes= other_nodes;
25322 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25328 createRange: function(sel)
25330 // this has strange effects when using with
25331 // top toolbar - not sure if it's a great idea.
25332 //this.editor.contentWindow.focus();
25333 if (typeof sel != "undefined") {
25335 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25337 return this.doc.createRange();
25340 return this.doc.createRange();
25343 getParentElement: function()
25346 this.assignDocWin();
25347 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25349 var range = this.createRange(sel);
25352 var p = range.commonAncestorContainer;
25353 while (p.nodeType == 3) { // text node
25364 * Range intersection.. the hard stuff...
25368 * [ -- selected range --- ]
25372 * if end is before start or hits it. fail.
25373 * if start is after end or hits it fail.
25375 * if either hits (but other is outside. - then it's not
25381 // @see http://www.thismuchiknow.co.uk/?p=64.
25382 rangeIntersectsNode : function(range, node)
25384 var nodeRange = node.ownerDocument.createRange();
25386 nodeRange.selectNode(node);
25388 nodeRange.selectNodeContents(node);
25391 var rangeStartRange = range.cloneRange();
25392 rangeStartRange.collapse(true);
25394 var rangeEndRange = range.cloneRange();
25395 rangeEndRange.collapse(false);
25397 var nodeStartRange = nodeRange.cloneRange();
25398 nodeStartRange.collapse(true);
25400 var nodeEndRange = nodeRange.cloneRange();
25401 nodeEndRange.collapse(false);
25403 return rangeStartRange.compareBoundaryPoints(
25404 Range.START_TO_START, nodeEndRange) == -1 &&
25405 rangeEndRange.compareBoundaryPoints(
25406 Range.START_TO_START, nodeStartRange) == 1;
25410 rangeCompareNode : function(range, node)
25412 var nodeRange = node.ownerDocument.createRange();
25414 nodeRange.selectNode(node);
25416 nodeRange.selectNodeContents(node);
25420 range.collapse(true);
25422 nodeRange.collapse(true);
25424 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25425 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
25427 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25429 var nodeIsBefore = ss == 1;
25430 var nodeIsAfter = ee == -1;
25432 if (nodeIsBefore && nodeIsAfter) {
25435 if (!nodeIsBefore && nodeIsAfter) {
25436 return 1; //right trailed.
25439 if (nodeIsBefore && !nodeIsAfter) {
25440 return 2; // left trailed.
25446 // private? - in a new class?
25447 cleanUpPaste : function()
25449 // cleans up the whole document..
25450 Roo.log('cleanuppaste');
25452 this.cleanUpChildren(this.doc.body);
25453 var clean = this.cleanWordChars(this.doc.body.innerHTML);
25454 if (clean != this.doc.body.innerHTML) {
25455 this.doc.body.innerHTML = clean;
25460 cleanWordChars : function(input) {// change the chars to hex code
25461 var he = Roo.HtmlEditorCore;
25463 var output = input;
25464 Roo.each(he.swapCodes, function(sw) {
25465 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25467 output = output.replace(swapper, sw[1]);
25474 cleanUpChildren : function (n)
25476 if (!n.childNodes.length) {
25479 for (var i = n.childNodes.length-1; i > -1 ; i--) {
25480 this.cleanUpChild(n.childNodes[i]);
25487 cleanUpChild : function (node)
25490 //console.log(node);
25491 if (node.nodeName == "#text") {
25492 // clean up silly Windows -- stuff?
25495 if (node.nodeName == "#comment") {
25496 node.parentNode.removeChild(node);
25497 // clean up silly Windows -- stuff?
25500 var lcname = node.tagName.toLowerCase();
25501 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25502 // whitelist of tags..
25504 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25506 node.parentNode.removeChild(node);
25511 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25513 // spans with no attributes - just remove them..
25514 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
25515 remove_keep_children = true;
25518 // remove <a name=....> as rendering on yahoo mailer is borked with this.
25519 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25521 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25522 // remove_keep_children = true;
25525 if (remove_keep_children) {
25526 this.cleanUpChildren(node);
25527 // inserts everything just before this node...
25528 while (node.childNodes.length) {
25529 var cn = node.childNodes[0];
25530 node.removeChild(cn);
25531 node.parentNode.insertBefore(cn, node);
25533 node.parentNode.removeChild(node);
25537 if (!node.attributes || !node.attributes.length) {
25542 this.cleanUpChildren(node);
25546 function cleanAttr(n,v)
25549 if (v.match(/^\./) || v.match(/^\//)) {
25552 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25555 if (v.match(/^#/)) {
25558 if (v.match(/^\{/)) { // allow template editing.
25561 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25562 node.removeAttribute(n);
25566 var cwhite = this.cwhite;
25567 var cblack = this.cblack;
25569 function cleanStyle(n,v)
25571 if (v.match(/expression/)) { //XSS?? should we even bother..
25572 node.removeAttribute(n);
25576 var parts = v.split(/;/);
25579 Roo.each(parts, function(p) {
25580 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25584 var l = p.split(':').shift().replace(/\s+/g,'');
25585 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25587 if ( cwhite.length && cblack.indexOf(l) > -1) {
25588 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25589 //node.removeAttribute(n);
25593 // only allow 'c whitelisted system attributes'
25594 if ( cwhite.length && cwhite.indexOf(l) < 0) {
25595 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25596 //node.removeAttribute(n);
25606 if (clean.length) {
25607 node.setAttribute(n, clean.join(';'));
25609 node.removeAttribute(n);
25615 for (var i = node.attributes.length-1; i > -1 ; i--) {
25616 var a = node.attributes[i];
25619 if (a.name.toLowerCase().substr(0,2)=='on') {
25620 node.removeAttribute(a.name);
25623 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25624 node.removeAttribute(a.name);
25627 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25628 cleanAttr(a.name,a.value); // fixme..
25631 if (a.name == 'style') {
25632 cleanStyle(a.name,a.value);
25635 /// clean up MS crap..
25636 // tecnically this should be a list of valid class'es..
25639 if (a.name == 'class') {
25640 if (a.value.match(/^Mso/)) {
25641 node.removeAttribute('class');
25644 if (a.value.match(/^body$/)) {
25645 node.removeAttribute('class');
25656 this.cleanUpChildren(node);
25662 * Clean up MS wordisms...
25664 cleanWord : function(node)
25667 this.cleanWord(this.doc.body);
25672 node.nodeName == 'SPAN' &&
25673 !node.hasAttributes() &&
25674 node.childNodes.length == 1 &&
25675 node.firstChild.nodeName == "#text"
25677 var textNode = node.firstChild;
25678 node.removeChild(textNode);
25679 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25680 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25682 node.parentNode.insertBefore(textNode, node);
25683 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25684 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25686 node.parentNode.removeChild(node);
25689 if (node.nodeName == "#text") {
25690 // clean up silly Windows -- stuff?
25693 if (node.nodeName == "#comment") {
25694 node.parentNode.removeChild(node);
25695 // clean up silly Windows -- stuff?
25699 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25700 node.parentNode.removeChild(node);
25703 //Roo.log(node.tagName);
25704 // remove - but keep children..
25705 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25706 //Roo.log('-- removed');
25707 while (node.childNodes.length) {
25708 var cn = node.childNodes[0];
25709 node.removeChild(cn);
25710 node.parentNode.insertBefore(cn, node);
25711 // move node to parent - and clean it..
25712 this.cleanWord(cn);
25714 node.parentNode.removeChild(node);
25715 /// no need to iterate chidlren = it's got none..
25716 //this.iterateChildren(node, this.cleanWord);
25720 if (node.className.length) {
25722 var cn = node.className.split(/\W+/);
25724 Roo.each(cn, function(cls) {
25725 if (cls.match(/Mso[a-zA-Z]+/)) {
25730 node.className = cna.length ? cna.join(' ') : '';
25732 node.removeAttribute("class");
25736 if (node.hasAttribute("lang")) {
25737 node.removeAttribute("lang");
25740 if (node.hasAttribute("style")) {
25742 var styles = node.getAttribute("style").split(";");
25744 Roo.each(styles, function(s) {
25745 if (!s.match(/:/)) {
25748 var kv = s.split(":");
25749 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25752 // what ever is left... we allow.
25755 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25756 if (!nstyle.length) {
25757 node.removeAttribute('style');
25760 this.iterateChildren(node, this.cleanWord);
25766 * iterateChildren of a Node, calling fn each time, using this as the scole..
25767 * @param {DomNode} node node to iterate children of.
25768 * @param {Function} fn method of this class to call on each item.
25770 iterateChildren : function(node, fn)
25772 if (!node.childNodes.length) {
25775 for (var i = node.childNodes.length-1; i > -1 ; i--) {
25776 fn.call(this, node.childNodes[i])
25782 * cleanTableWidths.
25784 * Quite often pasting from word etc.. results in tables with column and widths.
25785 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25788 cleanTableWidths : function(node)
25793 this.cleanTableWidths(this.doc.body);
25798 if (node.nodeName == "#text" || node.nodeName == "#comment") {
25801 Roo.log(node.tagName);
25802 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25803 this.iterateChildren(node, this.cleanTableWidths);
25806 if (node.hasAttribute('width')) {
25807 node.removeAttribute('width');
25811 if (node.hasAttribute("style")) {
25814 var styles = node.getAttribute("style").split(";");
25816 Roo.each(styles, function(s) {
25817 if (!s.match(/:/)) {
25820 var kv = s.split(":");
25821 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25824 // what ever is left... we allow.
25827 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25828 if (!nstyle.length) {
25829 node.removeAttribute('style');
25833 this.iterateChildren(node, this.cleanTableWidths);
25841 domToHTML : function(currentElement, depth, nopadtext) {
25843 depth = depth || 0;
25844 nopadtext = nopadtext || false;
25846 if (!currentElement) {
25847 return this.domToHTML(this.doc.body);
25850 //Roo.log(currentElement);
25852 var allText = false;
25853 var nodeName = currentElement.nodeName;
25854 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25856 if (nodeName == '#text') {
25858 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25863 if (nodeName != 'BODY') {
25866 // Prints the node tagName, such as <A>, <IMG>, etc
25869 for(i = 0; i < currentElement.attributes.length;i++) {
25871 var aname = currentElement.attributes.item(i).name;
25872 if (!currentElement.attributes.item(i).value.length) {
25875 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25878 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25887 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25890 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25895 // Traverse the tree
25897 var currentElementChild = currentElement.childNodes.item(i);
25898 var allText = true;
25899 var innerHTML = '';
25901 while (currentElementChild) {
25902 // Formatting code (indent the tree so it looks nice on the screen)
25903 var nopad = nopadtext;
25904 if (lastnode == 'SPAN') {
25908 if (currentElementChild.nodeName == '#text') {
25909 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25910 toadd = nopadtext ? toadd : toadd.trim();
25911 if (!nopad && toadd.length > 80) {
25912 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
25914 innerHTML += toadd;
25917 currentElementChild = currentElement.childNodes.item(i);
25923 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
25925 // Recursively traverse the tree structure of the child node
25926 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
25927 lastnode = currentElementChild.nodeName;
25929 currentElementChild=currentElement.childNodes.item(i);
25935 // The remaining code is mostly for formatting the tree
25936 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
25941 ret+= "</"+tagName+">";
25947 applyBlacklists : function()
25949 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
25950 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
25954 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25955 if (b.indexOf(tag) > -1) {
25958 this.white.push(tag);
25962 Roo.each(w, function(tag) {
25963 if (b.indexOf(tag) > -1) {
25966 if (this.white.indexOf(tag) > -1) {
25969 this.white.push(tag);
25974 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25975 if (w.indexOf(tag) > -1) {
25978 this.black.push(tag);
25982 Roo.each(b, function(tag) {
25983 if (w.indexOf(tag) > -1) {
25986 if (this.black.indexOf(tag) > -1) {
25989 this.black.push(tag);
25994 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
25995 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
25999 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
26000 if (b.indexOf(tag) > -1) {
26003 this.cwhite.push(tag);
26007 Roo.each(w, function(tag) {
26008 if (b.indexOf(tag) > -1) {
26011 if (this.cwhite.indexOf(tag) > -1) {
26014 this.cwhite.push(tag);
26019 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
26020 if (w.indexOf(tag) > -1) {
26023 this.cblack.push(tag);
26027 Roo.each(b, function(tag) {
26028 if (w.indexOf(tag) > -1) {
26031 if (this.cblack.indexOf(tag) > -1) {
26034 this.cblack.push(tag);
26039 setStylesheets : function(stylesheets)
26041 if(typeof(stylesheets) == 'string'){
26042 Roo.get(this.iframe.contentDocument.head).createChild({
26044 rel : 'stylesheet',
26053 Roo.each(stylesheets, function(s) {
26058 Roo.get(_this.iframe.contentDocument.head).createChild({
26060 rel : 'stylesheet',
26069 removeStylesheets : function()
26073 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
26078 setStyle : function(style)
26080 Roo.get(this.iframe.contentDocument.head).createChild({
26089 // hide stuff that is not compatible
26103 * @event specialkey
26107 * @cfg {String} fieldClass @hide
26110 * @cfg {String} focusClass @hide
26113 * @cfg {String} autoCreate @hide
26116 * @cfg {String} inputType @hide
26119 * @cfg {String} invalidClass @hide
26122 * @cfg {String} invalidText @hide
26125 * @cfg {String} msgFx @hide
26128 * @cfg {String} validateOnBlur @hide
26132 Roo.HtmlEditorCore.white = [
26133 'area', 'br', 'img', 'input', 'hr', 'wbr',
26135 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
26136 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
26137 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
26138 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
26139 'table', 'ul', 'xmp',
26141 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
26144 'dir', 'menu', 'ol', 'ul', 'dl',
26150 Roo.HtmlEditorCore.black = [
26151 // 'embed', 'object', // enable - backend responsiblity to clean thiese
26153 'base', 'basefont', 'bgsound', 'blink', 'body',
26154 'frame', 'frameset', 'head', 'html', 'ilayer',
26155 'iframe', 'layer', 'link', 'meta', 'object',
26156 'script', 'style' ,'title', 'xml' // clean later..
26158 Roo.HtmlEditorCore.clean = [
26159 'script', 'style', 'title', 'xml'
26161 Roo.HtmlEditorCore.remove = [
26166 Roo.HtmlEditorCore.ablack = [
26170 Roo.HtmlEditorCore.aclean = [
26171 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
26175 Roo.HtmlEditorCore.pwhite= [
26176 'http', 'https', 'mailto'
26179 // white listed style attributes.
26180 Roo.HtmlEditorCore.cwhite= [
26181 // 'text-align', /// default is to allow most things..
26187 // black listed style attributes.
26188 Roo.HtmlEditorCore.cblack= [
26189 // 'font-size' -- this can be set by the project
26193 Roo.HtmlEditorCore.swapCodes =[
26194 [ 8211, "–" ],
26195 [ 8212, "—" ],
26212 * @class Roo.bootstrap.HtmlEditor
26213 * @extends Roo.bootstrap.TextArea
26214 * Bootstrap HtmlEditor class
26217 * Create a new HtmlEditor
26218 * @param {Object} config The config object
26221 Roo.bootstrap.HtmlEditor = function(config){
26222 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
26223 if (!this.toolbars) {
26224 this.toolbars = [];
26227 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26230 * @event initialize
26231 * Fires when the editor is fully initialized (including the iframe)
26232 * @param {HtmlEditor} this
26237 * Fires when the editor is first receives the focus. Any insertion must wait
26238 * until after this event.
26239 * @param {HtmlEditor} this
26243 * @event beforesync
26244 * Fires before the textarea is updated with content from the editor iframe. Return false
26245 * to cancel the sync.
26246 * @param {HtmlEditor} this
26247 * @param {String} html
26251 * @event beforepush
26252 * Fires before the iframe editor is updated with content from the textarea. Return false
26253 * to cancel the push.
26254 * @param {HtmlEditor} this
26255 * @param {String} html
26260 * Fires when the textarea is updated with content from the editor iframe.
26261 * @param {HtmlEditor} this
26262 * @param {String} html
26267 * Fires when the iframe editor is updated with content from the textarea.
26268 * @param {HtmlEditor} this
26269 * @param {String} html
26273 * @event editmodechange
26274 * Fires when the editor switches edit modes
26275 * @param {HtmlEditor} this
26276 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26278 editmodechange: true,
26280 * @event editorevent
26281 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26282 * @param {HtmlEditor} this
26286 * @event firstfocus
26287 * Fires when on first focus - needed by toolbars..
26288 * @param {HtmlEditor} this
26293 * Auto save the htmlEditor value as a file into Events
26294 * @param {HtmlEditor} this
26298 * @event savedpreview
26299 * preview the saved version of htmlEditor
26300 * @param {HtmlEditor} this
26307 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
26311 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26316 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
26321 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
26326 * @cfg {Number} height (in pixels)
26330 * @cfg {Number} width (in pixels)
26335 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26338 stylesheets: false,
26343 // private properties
26344 validationEvent : false,
26346 initialized : false,
26349 onFocus : Roo.emptyFn,
26351 hideMode:'offsets',
26353 tbContainer : false,
26357 toolbarContainer :function() {
26358 return this.wrap.select('.x-html-editor-tb',true).first();
26362 * Protected method that will not generally be called directly. It
26363 * is called when the editor creates its toolbar. Override this method if you need to
26364 * add custom toolbar buttons.
26365 * @param {HtmlEditor} editor
26367 createToolbar : function(){
26368 Roo.log('renewing');
26369 Roo.log("create toolbars");
26371 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26372 this.toolbars[0].render(this.toolbarContainer());
26376 // if (!editor.toolbars || !editor.toolbars.length) {
26377 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26380 // for (var i =0 ; i < editor.toolbars.length;i++) {
26381 // editor.toolbars[i] = Roo.factory(
26382 // typeof(editor.toolbars[i]) == 'string' ?
26383 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
26384 // Roo.bootstrap.HtmlEditor);
26385 // editor.toolbars[i].init(editor);
26391 onRender : function(ct, position)
26393 // Roo.log("Call onRender: " + this.xtype);
26395 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26397 this.wrap = this.inputEl().wrap({
26398 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26401 this.editorcore.onRender(ct, position);
26403 if (this.resizable) {
26404 this.resizeEl = new Roo.Resizable(this.wrap, {
26408 minHeight : this.height,
26409 height: this.height,
26410 handles : this.resizable,
26413 resize : function(r, w, h) {
26414 _t.onResize(w,h); // -something
26420 this.createToolbar(this);
26423 if(!this.width && this.resizable){
26424 this.setSize(this.wrap.getSize());
26426 if (this.resizeEl) {
26427 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26428 // should trigger onReize..
26434 onResize : function(w, h)
26436 Roo.log('resize: ' +w + ',' + h );
26437 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26441 if(this.inputEl() ){
26442 if(typeof w == 'number'){
26443 var aw = w - this.wrap.getFrameWidth('lr');
26444 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26447 if(typeof h == 'number'){
26448 var tbh = -11; // fixme it needs to tool bar size!
26449 for (var i =0; i < this.toolbars.length;i++) {
26450 // fixme - ask toolbars for heights?
26451 tbh += this.toolbars[i].el.getHeight();
26452 //if (this.toolbars[i].footer) {
26453 // tbh += this.toolbars[i].footer.el.getHeight();
26461 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26462 ah -= 5; // knock a few pixes off for look..
26463 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26467 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26468 this.editorcore.onResize(ew,eh);
26473 * Toggles the editor between standard and source edit mode.
26474 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26476 toggleSourceEdit : function(sourceEditMode)
26478 this.editorcore.toggleSourceEdit(sourceEditMode);
26480 if(this.editorcore.sourceEditMode){
26481 Roo.log('editor - showing textarea');
26484 // Roo.log(this.syncValue());
26486 this.inputEl().removeClass(['hide', 'x-hidden']);
26487 this.inputEl().dom.removeAttribute('tabIndex');
26488 this.inputEl().focus();
26490 Roo.log('editor - hiding textarea');
26492 // Roo.log(this.pushValue());
26495 this.inputEl().addClass(['hide', 'x-hidden']);
26496 this.inputEl().dom.setAttribute('tabIndex', -1);
26497 //this.deferFocus();
26500 if(this.resizable){
26501 this.setSize(this.wrap.getSize());
26504 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26507 // private (for BoxComponent)
26508 adjustSize : Roo.BoxComponent.prototype.adjustSize,
26510 // private (for BoxComponent)
26511 getResizeEl : function(){
26515 // private (for BoxComponent)
26516 getPositionEl : function(){
26521 initEvents : function(){
26522 this.originalValue = this.getValue();
26526 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26529 // markInvalid : Roo.emptyFn,
26531 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26534 // clearInvalid : Roo.emptyFn,
26536 setValue : function(v){
26537 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26538 this.editorcore.pushValue();
26543 deferFocus : function(){
26544 this.focus.defer(10, this);
26548 focus : function(){
26549 this.editorcore.focus();
26555 onDestroy : function(){
26561 for (var i =0; i < this.toolbars.length;i++) {
26562 // fixme - ask toolbars for heights?
26563 this.toolbars[i].onDestroy();
26566 this.wrap.dom.innerHTML = '';
26567 this.wrap.remove();
26572 onFirstFocus : function(){
26573 //Roo.log("onFirstFocus");
26574 this.editorcore.onFirstFocus();
26575 for (var i =0; i < this.toolbars.length;i++) {
26576 this.toolbars[i].onFirstFocus();
26582 syncValue : function()
26584 this.editorcore.syncValue();
26587 pushValue : function()
26589 this.editorcore.pushValue();
26593 // hide stuff that is not compatible
26607 * @event specialkey
26611 * @cfg {String} fieldClass @hide
26614 * @cfg {String} focusClass @hide
26617 * @cfg {String} autoCreate @hide
26620 * @cfg {String} inputType @hide
26624 * @cfg {String} invalidText @hide
26627 * @cfg {String} msgFx @hide
26630 * @cfg {String} validateOnBlur @hide
26639 Roo.namespace('Roo.bootstrap.htmleditor');
26641 * @class Roo.bootstrap.HtmlEditorToolbar1
26647 new Roo.bootstrap.HtmlEditor({
26650 new Roo.bootstrap.HtmlEditorToolbar1({
26651 disable : { fonts: 1 , format: 1, ..., ... , ...],
26657 * @cfg {Object} disable List of elements to disable..
26658 * @cfg {Array} btns List of additional buttons.
26662 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26665 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26668 Roo.apply(this, config);
26670 // default disabled, based on 'good practice'..
26671 this.disable = this.disable || {};
26672 Roo.applyIf(this.disable, {
26675 specialElements : true
26677 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26679 this.editor = config.editor;
26680 this.editorcore = config.editor.editorcore;
26682 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26684 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26685 // dont call parent... till later.
26687 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
26692 editorcore : false,
26697 "h1","h2","h3","h4","h5","h6",
26699 "abbr", "acronym", "address", "cite", "samp", "var",
26703 onRender : function(ct, position)
26705 // Roo.log("Call onRender: " + this.xtype);
26707 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26709 this.el.dom.style.marginBottom = '0';
26711 var editorcore = this.editorcore;
26712 var editor= this.editor;
26715 var btn = function(id,cmd , toggle, handler, html){
26717 var event = toggle ? 'toggle' : 'click';
26722 xns: Roo.bootstrap,
26726 enableToggle:toggle !== false,
26728 pressed : toggle ? false : null,
26731 a.listeners[toggle ? 'toggle' : 'click'] = function() {
26732 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
26738 // var cb_box = function...
26743 xns: Roo.bootstrap,
26748 xns: Roo.bootstrap,
26752 Roo.each(this.formats, function(f) {
26753 style.menu.items.push({
26755 xns: Roo.bootstrap,
26756 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26761 editorcore.insertTag(this.tagname);
26768 children.push(style);
26770 btn('bold',false,true);
26771 btn('italic',false,true);
26772 btn('align-left', 'justifyleft',true);
26773 btn('align-center', 'justifycenter',true);
26774 btn('align-right' , 'justifyright',true);
26775 btn('link', false, false, function(btn) {
26776 //Roo.log("create link?");
26777 var url = prompt(this.createLinkText, this.defaultLinkValue);
26778 if(url && url != 'http:/'+'/'){
26779 this.editorcore.relayCmd('createlink', url);
26782 btn('list','insertunorderedlist',true);
26783 btn('pencil', false,true, function(btn){
26785 this.toggleSourceEdit(btn.pressed);
26788 if (this.editor.btns.length > 0) {
26789 for (var i = 0; i<this.editor.btns.length; i++) {
26790 children.push(this.editor.btns[i]);
26798 xns: Roo.bootstrap,
26803 xns: Roo.bootstrap,
26808 cog.menu.items.push({
26810 xns: Roo.bootstrap,
26811 html : Clean styles,
26816 editorcore.insertTag(this.tagname);
26825 this.xtype = 'NavSimplebar';
26827 for(var i=0;i< children.length;i++) {
26829 this.buttons.add(this.addxtypeChild(children[i]));
26833 editor.on('editorevent', this.updateToolbar, this);
26835 onBtnClick : function(id)
26837 this.editorcore.relayCmd(id);
26838 this.editorcore.focus();
26842 * Protected method that will not generally be called directly. It triggers
26843 * a toolbar update by reading the markup state of the current selection in the editor.
26845 updateToolbar: function(){
26847 if(!this.editorcore.activated){
26848 this.editor.onFirstFocus(); // is this neeed?
26852 var btns = this.buttons;
26853 var doc = this.editorcore.doc;
26854 btns.get('bold').setActive(doc.queryCommandState('bold'));
26855 btns.get('italic').setActive(doc.queryCommandState('italic'));
26856 //btns.get('underline').setActive(doc.queryCommandState('underline'));
26858 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26859 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26860 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26862 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26863 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26866 var ans = this.editorcore.getAllAncestors();
26867 if (this.formatCombo) {
26870 var store = this.formatCombo.store;
26871 this.formatCombo.setValue("");
26872 for (var i =0; i < ans.length;i++) {
26873 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26875 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26883 // hides menus... - so this cant be on a menu...
26884 Roo.bootstrap.MenuMgr.hideAll();
26886 Roo.bootstrap.MenuMgr.hideAll();
26887 //this.editorsyncValue();
26889 onFirstFocus: function() {
26890 this.buttons.each(function(item){
26894 toggleSourceEdit : function(sourceEditMode){
26897 if(sourceEditMode){
26898 Roo.log("disabling buttons");
26899 this.buttons.each( function(item){
26900 if(item.cmd != 'pencil'){
26906 Roo.log("enabling buttons");
26907 if(this.editorcore.initialized){
26908 this.buttons.each( function(item){
26914 Roo.log("calling toggole on editor");
26915 // tell the editor that it's been pressed..
26916 this.editor.toggleSourceEdit(sourceEditMode);
26930 * @class Roo.bootstrap.Markdown
26931 * @extends Roo.bootstrap.TextArea
26932 * Bootstrap Showdown editable area
26933 * @cfg {string} content
26936 * Create a new Showdown
26939 Roo.bootstrap.Markdown = function(config){
26940 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26944 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
26948 initEvents : function()
26951 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26952 this.markdownEl = this.el.createChild({
26953 cls : 'roo-markdown-area'
26955 this.inputEl().addClass('d-none');
26956 if (this.getValue() == '') {
26957 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26960 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26962 this.markdownEl.on('click', this.toggleTextEdit, this);
26963 this.on('blur', this.toggleTextEdit, this);
26964 this.on('specialkey', this.resizeTextArea, this);
26967 toggleTextEdit : function()
26969 var sh = this.markdownEl.getHeight();
26970 this.inputEl().addClass('d-none');
26971 this.markdownEl.addClass('d-none');
26972 if (!this.editing) {
26974 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26975 this.inputEl().removeClass('d-none');
26976 this.inputEl().focus();
26977 this.editing = true;
26980 // show showdown...
26981 this.updateMarkdown();
26982 this.markdownEl.removeClass('d-none');
26983 this.editing = false;
26986 updateMarkdown : function()
26988 if (this.getValue() == '') {
26989 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26993 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26996 resizeTextArea: function () {
26999 Roo.log([sh, this.getValue().split("\n").length * 30]);
27000 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
27002 setValue : function(val)
27004 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
27005 if (!this.editing) {
27006 this.updateMarkdown();
27012 if (!this.editing) {
27013 this.toggleTextEdit();
27021 * @class Roo.bootstrap.Table.AbstractSelectionModel
27022 * @extends Roo.util.Observable
27023 * Abstract base class for grid SelectionModels. It provides the interface that should be
27024 * implemented by descendant classes. This class should not be directly instantiated.
27027 Roo.bootstrap.Table.AbstractSelectionModel = function(){
27028 this.locked = false;
27029 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
27033 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
27034 /** @ignore Called by the grid automatically. Do not call directly. */
27035 init : function(grid){
27041 * Locks the selections.
27044 this.locked = true;
27048 * Unlocks the selections.
27050 unlock : function(){
27051 this.locked = false;
27055 * Returns true if the selections are locked.
27056 * @return {Boolean}
27058 isLocked : function(){
27059 return this.locked;
27063 initEvents : function ()
27069 * @extends Roo.bootstrap.Table.AbstractSelectionModel
27070 * @class Roo.bootstrap.Table.RowSelectionModel
27071 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
27072 * It supports multiple selections and keyboard selection/navigation.
27074 * @param {Object} config
27077 Roo.bootstrap.Table.RowSelectionModel = function(config){
27078 Roo.apply(this, config);
27079 this.selections = new Roo.util.MixedCollection(false, function(o){
27084 this.lastActive = false;
27088 * @event selectionchange
27089 * Fires when the selection changes
27090 * @param {SelectionModel} this
27092 "selectionchange" : true,
27094 * @event afterselectionchange
27095 * Fires after the selection changes (eg. by key press or clicking)
27096 * @param {SelectionModel} this
27098 "afterselectionchange" : true,
27100 * @event beforerowselect
27101 * Fires when a row is selected being selected, return false to cancel.
27102 * @param {SelectionModel} this
27103 * @param {Number} rowIndex The selected index
27104 * @param {Boolean} keepExisting False if other selections will be cleared
27106 "beforerowselect" : true,
27109 * Fires when a row is selected.
27110 * @param {SelectionModel} this
27111 * @param {Number} rowIndex The selected index
27112 * @param {Roo.data.Record} r The record
27114 "rowselect" : true,
27116 * @event rowdeselect
27117 * Fires when a row is deselected.
27118 * @param {SelectionModel} this
27119 * @param {Number} rowIndex The selected index
27121 "rowdeselect" : true
27123 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
27124 this.locked = false;
27127 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
27129 * @cfg {Boolean} singleSelect
27130 * True to allow selection of only one row at a time (defaults to false)
27132 singleSelect : false,
27135 initEvents : function()
27138 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
27139 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
27140 //}else{ // allow click to work like normal
27141 // this.grid.on("rowclick", this.handleDragableRowClick, this);
27143 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
27144 this.grid.on("rowclick", this.handleMouseDown, this);
27146 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
27147 "up" : function(e){
27149 this.selectPrevious(e.shiftKey);
27150 }else if(this.last !== false && this.lastActive !== false){
27151 var last = this.last;
27152 this.selectRange(this.last, this.lastActive-1);
27153 this.grid.getView().focusRow(this.lastActive);
27154 if(last !== false){
27158 this.selectFirstRow();
27160 this.fireEvent("afterselectionchange", this);
27162 "down" : function(e){
27164 this.selectNext(e.shiftKey);
27165 }else if(this.last !== false && this.lastActive !== false){
27166 var last = this.last;
27167 this.selectRange(this.last, this.lastActive+1);
27168 this.grid.getView().focusRow(this.lastActive);
27169 if(last !== false){
27173 this.selectFirstRow();
27175 this.fireEvent("afterselectionchange", this);
27179 this.grid.store.on('load', function(){
27180 this.selections.clear();
27183 var view = this.grid.view;
27184 view.on("refresh", this.onRefresh, this);
27185 view.on("rowupdated", this.onRowUpdated, this);
27186 view.on("rowremoved", this.onRemove, this);
27191 onRefresh : function()
27193 var ds = this.grid.store, i, v = this.grid.view;
27194 var s = this.selections;
27195 s.each(function(r){
27196 if((i = ds.indexOfId(r.id)) != -1){
27205 onRemove : function(v, index, r){
27206 this.selections.remove(r);
27210 onRowUpdated : function(v, index, r){
27211 if(this.isSelected(r)){
27212 v.onRowSelect(index);
27218 * @param {Array} records The records to select
27219 * @param {Boolean} keepExisting (optional) True to keep existing selections
27221 selectRecords : function(records, keepExisting)
27224 this.clearSelections();
27226 var ds = this.grid.store;
27227 for(var i = 0, len = records.length; i < len; i++){
27228 this.selectRow(ds.indexOf(records[i]), true);
27233 * Gets the number of selected rows.
27236 getCount : function(){
27237 return this.selections.length;
27241 * Selects the first row in the grid.
27243 selectFirstRow : function(){
27248 * Select the last row.
27249 * @param {Boolean} keepExisting (optional) True to keep existing selections
27251 selectLastRow : function(keepExisting){
27252 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
27253 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
27257 * Selects the row immediately following the last selected row.
27258 * @param {Boolean} keepExisting (optional) True to keep existing selections
27260 selectNext : function(keepExisting)
27262 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
27263 this.selectRow(this.last+1, keepExisting);
27264 this.grid.getView().focusRow(this.last);
27269 * Selects the row that precedes the last selected row.
27270 * @param {Boolean} keepExisting (optional) True to keep existing selections
27272 selectPrevious : function(keepExisting){
27274 this.selectRow(this.last-1, keepExisting);
27275 this.grid.getView().focusRow(this.last);
27280 * Returns the selected records
27281 * @return {Array} Array of selected records
27283 getSelections : function(){
27284 return [].concat(this.selections.items);
27288 * Returns the first selected record.
27291 getSelected : function(){
27292 return this.selections.itemAt(0);
27297 * Clears all selections.
27299 clearSelections : function(fast)
27305 var ds = this.grid.store;
27306 var s = this.selections;
27307 s.each(function(r){
27308 this.deselectRow(ds.indexOfId(r.id));
27312 this.selections.clear();
27319 * Selects all rows.
27321 selectAll : function(){
27325 this.selections.clear();
27326 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
27327 this.selectRow(i, true);
27332 * Returns True if there is a selection.
27333 * @return {Boolean}
27335 hasSelection : function(){
27336 return this.selections.length > 0;
27340 * Returns True if the specified row is selected.
27341 * @param {Number/Record} record The record or index of the record to check
27342 * @return {Boolean}
27344 isSelected : function(index){
27345 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
27346 return (r && this.selections.key(r.id) ? true : false);
27350 * Returns True if the specified record id is selected.
27351 * @param {String} id The id of record to check
27352 * @return {Boolean}
27354 isIdSelected : function(id){
27355 return (this.selections.key(id) ? true : false);
27360 handleMouseDBClick : function(e, t){
27364 handleMouseDown : function(e, t)
27366 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27367 if(this.isLocked() || rowIndex < 0 ){
27370 if(e.shiftKey && this.last !== false){
27371 var last = this.last;
27372 this.selectRange(last, rowIndex, e.ctrlKey);
27373 this.last = last; // reset the last
27377 var isSelected = this.isSelected(rowIndex);
27378 //Roo.log("select row:" + rowIndex);
27380 this.deselectRow(rowIndex);
27382 this.selectRow(rowIndex, true);
27386 if(e.button !== 0 && isSelected){
27387 alert('rowIndex 2: ' + rowIndex);
27388 view.focusRow(rowIndex);
27389 }else if(e.ctrlKey && isSelected){
27390 this.deselectRow(rowIndex);
27391 }else if(!isSelected){
27392 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27393 view.focusRow(rowIndex);
27397 this.fireEvent("afterselectionchange", this);
27400 handleDragableRowClick : function(grid, rowIndex, e)
27402 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27403 this.selectRow(rowIndex, false);
27404 grid.view.focusRow(rowIndex);
27405 this.fireEvent("afterselectionchange", this);
27410 * Selects multiple rows.
27411 * @param {Array} rows Array of the indexes of the row to select
27412 * @param {Boolean} keepExisting (optional) True to keep existing selections
27414 selectRows : function(rows, keepExisting){
27416 this.clearSelections();
27418 for(var i = 0, len = rows.length; i < len; i++){
27419 this.selectRow(rows[i], true);
27424 * Selects a range of rows. All rows in between startRow and endRow are also selected.
27425 * @param {Number} startRow The index of the first row in the range
27426 * @param {Number} endRow The index of the last row in the range
27427 * @param {Boolean} keepExisting (optional) True to retain existing selections
27429 selectRange : function(startRow, endRow, keepExisting){
27434 this.clearSelections();
27436 if(startRow <= endRow){
27437 for(var i = startRow; i <= endRow; i++){
27438 this.selectRow(i, true);
27441 for(var i = startRow; i >= endRow; i--){
27442 this.selectRow(i, true);
27448 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27449 * @param {Number} startRow The index of the first row in the range
27450 * @param {Number} endRow The index of the last row in the range
27452 deselectRange : function(startRow, endRow, preventViewNotify){
27456 for(var i = startRow; i <= endRow; i++){
27457 this.deselectRow(i, preventViewNotify);
27463 * @param {Number} row The index of the row to select
27464 * @param {Boolean} keepExisting (optional) True to keep existing selections
27466 selectRow : function(index, keepExisting, preventViewNotify)
27468 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27471 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27472 if(!keepExisting || this.singleSelect){
27473 this.clearSelections();
27476 var r = this.grid.store.getAt(index);
27477 //console.log('selectRow - record id :' + r.id);
27479 this.selections.add(r);
27480 this.last = this.lastActive = index;
27481 if(!preventViewNotify){
27482 var proxy = new Roo.Element(
27483 this.grid.getRowDom(index)
27485 proxy.addClass('bg-info info');
27487 this.fireEvent("rowselect", this, index, r);
27488 this.fireEvent("selectionchange", this);
27494 * @param {Number} row The index of the row to deselect
27496 deselectRow : function(index, preventViewNotify)
27501 if(this.last == index){
27504 if(this.lastActive == index){
27505 this.lastActive = false;
27508 var r = this.grid.store.getAt(index);
27513 this.selections.remove(r);
27514 //.console.log('deselectRow - record id :' + r.id);
27515 if(!preventViewNotify){
27517 var proxy = new Roo.Element(
27518 this.grid.getRowDom(index)
27520 proxy.removeClass('bg-info info');
27522 this.fireEvent("rowdeselect", this, index);
27523 this.fireEvent("selectionchange", this);
27527 restoreLast : function(){
27529 this.last = this._last;
27534 acceptsNav : function(row, col, cm){
27535 return !cm.isHidden(col) && cm.isCellEditable(col, row);
27539 onEditorKey : function(field, e){
27540 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27545 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27547 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27549 }else if(k == e.ENTER && !e.ctrlKey){
27553 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27555 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27557 }else if(k == e.ESC){
27561 g.startEditing(newCell[0], newCell[1]);
27567 * Ext JS Library 1.1.1
27568 * Copyright(c) 2006-2007, Ext JS, LLC.
27570 * Originally Released Under LGPL - original licence link has changed is not relivant.
27573 * <script type="text/javascript">
27577 * @class Roo.bootstrap.PagingToolbar
27578 * @extends Roo.bootstrap.NavSimplebar
27579 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27581 * Create a new PagingToolbar
27582 * @param {Object} config The config object
27583 * @param {Roo.data.Store} store
27585 Roo.bootstrap.PagingToolbar = function(config)
27587 // old args format still supported... - xtype is prefered..
27588 // created from xtype...
27590 this.ds = config.dataSource;
27592 if (config.store && !this.ds) {
27593 this.store= Roo.factory(config.store, Roo.data);
27594 this.ds = this.store;
27595 this.ds.xmodule = this.xmodule || false;
27598 this.toolbarItems = [];
27599 if (config.items) {
27600 this.toolbarItems = config.items;
27603 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27608 this.bind(this.ds);
27611 if (Roo.bootstrap.version == 4) {
27612 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27614 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27619 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27621 * @cfg {Roo.data.Store} dataSource
27622 * The underlying data store providing the paged data
27625 * @cfg {String/HTMLElement/Element} container
27626 * container The id or element that will contain the toolbar
27629 * @cfg {Boolean} displayInfo
27630 * True to display the displayMsg (defaults to false)
27633 * @cfg {Number} pageSize
27634 * The number of records to display per page (defaults to 20)
27638 * @cfg {String} displayMsg
27639 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27641 displayMsg : 'Displaying {0} - {1} of {2}',
27643 * @cfg {String} emptyMsg
27644 * The message to display when no records are found (defaults to "No data to display")
27646 emptyMsg : 'No data to display',
27648 * Customizable piece of the default paging text (defaults to "Page")
27651 beforePageText : "Page",
27653 * Customizable piece of the default paging text (defaults to "of %0")
27656 afterPageText : "of {0}",
27658 * Customizable piece of the default paging text (defaults to "First Page")
27661 firstText : "First Page",
27663 * Customizable piece of the default paging text (defaults to "Previous Page")
27666 prevText : "Previous Page",
27668 * Customizable piece of the default paging text (defaults to "Next Page")
27671 nextText : "Next Page",
27673 * Customizable piece of the default paging text (defaults to "Last Page")
27676 lastText : "Last Page",
27678 * Customizable piece of the default paging text (defaults to "Refresh")
27681 refreshText : "Refresh",
27685 onRender : function(ct, position)
27687 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27688 this.navgroup.parentId = this.id;
27689 this.navgroup.onRender(this.el, null);
27690 // add the buttons to the navgroup
27692 if(this.displayInfo){
27693 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27694 this.displayEl = this.el.select('.x-paging-info', true).first();
27695 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27696 // this.displayEl = navel.el.select('span',true).first();
27702 Roo.each(_this.buttons, function(e){ // this might need to use render????
27703 Roo.factory(e).render(_this.el);
27707 Roo.each(_this.toolbarItems, function(e) {
27708 _this.navgroup.addItem(e);
27712 this.first = this.navgroup.addItem({
27713 tooltip: this.firstText,
27714 cls: "prev btn-outline-secondary",
27715 html : ' <i class="fa fa-step-backward"></i>',
27717 preventDefault: true,
27718 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27721 this.prev = this.navgroup.addItem({
27722 tooltip: this.prevText,
27723 cls: "prev btn-outline-secondary",
27724 html : ' <i class="fa fa-backward"></i>',
27726 preventDefault: true,
27727 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
27729 //this.addSeparator();
27732 var field = this.navgroup.addItem( {
27734 cls : 'x-paging-position btn-outline-secondary',
27736 html : this.beforePageText +
27737 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27738 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
27741 this.field = field.el.select('input', true).first();
27742 this.field.on("keydown", this.onPagingKeydown, this);
27743 this.field.on("focus", function(){this.dom.select();});
27746 this.afterTextEl = field.el.select('.x-paging-after',true).first();
27747 //this.field.setHeight(18);
27748 //this.addSeparator();
27749 this.next = this.navgroup.addItem({
27750 tooltip: this.nextText,
27751 cls: "next btn-outline-secondary",
27752 html : ' <i class="fa fa-forward"></i>',
27754 preventDefault: true,
27755 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
27757 this.last = this.navgroup.addItem({
27758 tooltip: this.lastText,
27759 html : ' <i class="fa fa-step-forward"></i>',
27760 cls: "next btn-outline-secondary",
27762 preventDefault: true,
27763 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
27765 //this.addSeparator();
27766 this.loading = this.navgroup.addItem({
27767 tooltip: this.refreshText,
27768 cls: "btn-outline-secondary",
27769 html : ' <i class="fa fa-refresh"></i>',
27770 preventDefault: true,
27771 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27777 updateInfo : function(){
27778 if(this.displayEl){
27779 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27780 var msg = count == 0 ?
27784 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
27786 this.displayEl.update(msg);
27791 onLoad : function(ds, r, o)
27793 this.cursor = o.params && o.params.start ? o.params.start : 0;
27795 var d = this.getPageData(),
27800 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27801 this.field.dom.value = ap;
27802 this.first.setDisabled(ap == 1);
27803 this.prev.setDisabled(ap == 1);
27804 this.next.setDisabled(ap == ps);
27805 this.last.setDisabled(ap == ps);
27806 this.loading.enable();
27811 getPageData : function(){
27812 var total = this.ds.getTotalCount();
27815 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27816 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27821 onLoadError : function(){
27822 this.loading.enable();
27826 onPagingKeydown : function(e){
27827 var k = e.getKey();
27828 var d = this.getPageData();
27830 var v = this.field.dom.value, pageNum;
27831 if(!v || isNaN(pageNum = parseInt(v, 10))){
27832 this.field.dom.value = d.activePage;
27835 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27836 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27839 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))
27841 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27842 this.field.dom.value = pageNum;
27843 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27846 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27848 var v = this.field.dom.value, pageNum;
27849 var increment = (e.shiftKey) ? 10 : 1;
27850 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27853 if(!v || isNaN(pageNum = parseInt(v, 10))) {
27854 this.field.dom.value = d.activePage;
27857 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27859 this.field.dom.value = parseInt(v, 10) + increment;
27860 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27861 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27868 beforeLoad : function(){
27870 this.loading.disable();
27875 onClick : function(which){
27884 ds.load({params:{start: 0, limit: this.pageSize}});
27887 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27890 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27893 var total = ds.getTotalCount();
27894 var extra = total % this.pageSize;
27895 var lastStart = extra ? (total - extra) : total-this.pageSize;
27896 ds.load({params:{start: lastStart, limit: this.pageSize}});
27899 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27905 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27906 * @param {Roo.data.Store} store The data store to unbind
27908 unbind : function(ds){
27909 ds.un("beforeload", this.beforeLoad, this);
27910 ds.un("load", this.onLoad, this);
27911 ds.un("loadexception", this.onLoadError, this);
27912 ds.un("remove", this.updateInfo, this);
27913 ds.un("add", this.updateInfo, this);
27914 this.ds = undefined;
27918 * Binds the paging toolbar to the specified {@link Roo.data.Store}
27919 * @param {Roo.data.Store} store The data store to bind
27921 bind : function(ds){
27922 ds.on("beforeload", this.beforeLoad, this);
27923 ds.on("load", this.onLoad, this);
27924 ds.on("loadexception", this.onLoadError, this);
27925 ds.on("remove", this.updateInfo, this);
27926 ds.on("add", this.updateInfo, this);
27937 * @class Roo.bootstrap.MessageBar
27938 * @extends Roo.bootstrap.Component
27939 * Bootstrap MessageBar class
27940 * @cfg {String} html contents of the MessageBar
27941 * @cfg {String} weight (info | success | warning | danger) default info
27942 * @cfg {String} beforeClass insert the bar before the given class
27943 * @cfg {Boolean} closable (true | false) default false
27944 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27947 * Create a new Element
27948 * @param {Object} config The config object
27951 Roo.bootstrap.MessageBar = function(config){
27952 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27955 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
27961 beforeClass: 'bootstrap-sticky-wrap',
27963 getAutoCreate : function(){
27967 cls: 'alert alert-dismissable alert-' + this.weight,
27972 html: this.html || ''
27978 cfg.cls += ' alert-messages-fixed';
27992 onRender : function(ct, position)
27994 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27997 var cfg = Roo.apply({}, this.getAutoCreate());
28001 cfg.cls += ' ' + this.cls;
28004 cfg.style = this.style;
28006 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28008 this.el.setVisibilityMode(Roo.Element.DISPLAY);
28011 this.el.select('>button.close').on('click', this.hide, this);
28017 if (!this.rendered) {
28023 this.fireEvent('show', this);
28029 if (!this.rendered) {
28035 this.fireEvent('hide', this);
28038 update : function()
28040 // var e = this.el.dom.firstChild;
28042 // if(this.closable){
28043 // e = e.nextSibling;
28046 // e.data = this.html || '';
28048 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28064 * @class Roo.bootstrap.Graph
28065 * @extends Roo.bootstrap.Component
28066 * Bootstrap Graph class
28070 @cfg {String} graphtype bar | vbar | pie
28071 @cfg {number} g_x coodinator | centre x (pie)
28072 @cfg {number} g_y coodinator | centre y (pie)
28073 @cfg {number} g_r radius (pie)
28074 @cfg {number} g_height height of the chart (respected by all elements in the set)
28075 @cfg {number} g_width width of the chart (respected by all elements in the set)
28076 @cfg {Object} title The title of the chart
28079 -opts (object) options for the chart
28081 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28082 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28084 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.
28085 o stacked (boolean) whether or not to tread values as in a stacked bar chart
28087 o stretch (boolean)
28089 -opts (object) options for the pie
28092 o startAngle (number)
28093 o endAngle (number)
28097 * Create a new Input
28098 * @param {Object} config The config object
28101 Roo.bootstrap.Graph = function(config){
28102 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28108 * The img click event for the img.
28109 * @param {Roo.EventObject} e
28115 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
28126 //g_colors: this.colors,
28133 getAutoCreate : function(){
28144 onRender : function(ct,position){
28147 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28149 if (typeof(Raphael) == 'undefined') {
28150 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28154 this.raphael = Raphael(this.el.dom);
28156 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28157 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28158 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28159 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28161 r.text(160, 10, "Single Series Chart").attr(txtattr);
28162 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28163 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28164 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28166 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28167 r.barchart(330, 10, 300, 220, data1);
28168 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28169 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28172 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28173 // r.barchart(30, 30, 560, 250, xdata, {
28174 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28175 // axis : "0 0 1 1",
28176 // axisxlabels : xdata
28177 // //yvalues : cols,
28180 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28182 // this.load(null,xdata,{
28183 // axis : "0 0 1 1",
28184 // axisxlabels : xdata
28189 load : function(graphtype,xdata,opts)
28191 this.raphael.clear();
28193 graphtype = this.graphtype;
28198 var r = this.raphael,
28199 fin = function () {
28200 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28202 fout = function () {
28203 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28205 pfin = function() {
28206 this.sector.stop();
28207 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28210 this.label[0].stop();
28211 this.label[0].attr({ r: 7.5 });
28212 this.label[1].attr({ "font-weight": 800 });
28215 pfout = function() {
28216 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28219 this.label[0].animate({ r: 5 }, 500, "bounce");
28220 this.label[1].attr({ "font-weight": 400 });
28226 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28229 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28232 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
28233 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28235 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28242 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28247 setTitle: function(o)
28252 initEvents: function() {
28255 this.el.on('click', this.onClick, this);
28259 onClick : function(e)
28261 Roo.log('img onclick');
28262 this.fireEvent('click', this, e);
28274 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28277 * @class Roo.bootstrap.dash.NumberBox
28278 * @extends Roo.bootstrap.Component
28279 * Bootstrap NumberBox class
28280 * @cfg {String} headline Box headline
28281 * @cfg {String} content Box content
28282 * @cfg {String} icon Box icon
28283 * @cfg {String} footer Footer text
28284 * @cfg {String} fhref Footer href
28287 * Create a new NumberBox
28288 * @param {Object} config The config object
28292 Roo.bootstrap.dash.NumberBox = function(config){
28293 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28297 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
28306 getAutoCreate : function(){
28310 cls : 'small-box ',
28318 cls : 'roo-headline',
28319 html : this.headline
28323 cls : 'roo-content',
28324 html : this.content
28338 cls : 'ion ' + this.icon
28347 cls : 'small-box-footer',
28348 href : this.fhref || '#',
28352 cfg.cn.push(footer);
28359 onRender : function(ct,position){
28360 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28367 setHeadline: function (value)
28369 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28372 setFooter: function (value, href)
28374 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28377 this.el.select('a.small-box-footer',true).first().attr('href', href);
28382 setContent: function (value)
28384 this.el.select('.roo-content',true).first().dom.innerHTML = value;
28387 initEvents: function()
28401 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28404 * @class Roo.bootstrap.dash.TabBox
28405 * @extends Roo.bootstrap.Component
28406 * Bootstrap TabBox class
28407 * @cfg {String} title Title of the TabBox
28408 * @cfg {String} icon Icon of the TabBox
28409 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28410 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28413 * Create a new TabBox
28414 * @param {Object} config The config object
28418 Roo.bootstrap.dash.TabBox = function(config){
28419 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28424 * When a pane is added
28425 * @param {Roo.bootstrap.dash.TabPane} pane
28429 * @event activatepane
28430 * When a pane is activated
28431 * @param {Roo.bootstrap.dash.TabPane} pane
28433 "activatepane" : true
28441 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28446 tabScrollable : false,
28448 getChildContainer : function()
28450 return this.el.select('.tab-content', true).first();
28453 getAutoCreate : function(){
28457 cls: 'pull-left header',
28465 cls: 'fa ' + this.icon
28471 cls: 'nav nav-tabs pull-right',
28477 if(this.tabScrollable){
28484 cls: 'nav nav-tabs pull-right',
28495 cls: 'nav-tabs-custom',
28500 cls: 'tab-content no-padding',
28508 initEvents : function()
28510 //Roo.log('add add pane handler');
28511 this.on('addpane', this.onAddPane, this);
28514 * Updates the box title
28515 * @param {String} html to set the title to.
28517 setTitle : function(value)
28519 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28521 onAddPane : function(pane)
28523 this.panes.push(pane);
28524 //Roo.log('addpane');
28526 // tabs are rendere left to right..
28527 if(!this.showtabs){
28531 var ctr = this.el.select('.nav-tabs', true).first();
28534 var existing = ctr.select('.nav-tab',true);
28535 var qty = existing.getCount();;
28538 var tab = ctr.createChild({
28540 cls : 'nav-tab' + (qty ? '' : ' active'),
28548 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28551 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28553 pane.el.addClass('active');
28558 onTabClick : function(ev,un,ob,pane)
28560 //Roo.log('tab - prev default');
28561 ev.preventDefault();
28564 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28565 pane.tab.addClass('active');
28566 //Roo.log(pane.title);
28567 this.getChildContainer().select('.tab-pane',true).removeClass('active');
28568 // technically we should have a deactivate event.. but maybe add later.
28569 // and it should not de-activate the selected tab...
28570 this.fireEvent('activatepane', pane);
28571 pane.el.addClass('active');
28572 pane.fireEvent('activate');
28577 getActivePane : function()
28580 Roo.each(this.panes, function(p) {
28581 if(p.el.hasClass('active')){
28602 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28604 * @class Roo.bootstrap.TabPane
28605 * @extends Roo.bootstrap.Component
28606 * Bootstrap TabPane class
28607 * @cfg {Boolean} active (false | true) Default false
28608 * @cfg {String} title title of panel
28612 * Create a new TabPane
28613 * @param {Object} config The config object
28616 Roo.bootstrap.dash.TabPane = function(config){
28617 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28623 * When a pane is activated
28624 * @param {Roo.bootstrap.dash.TabPane} pane
28631 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
28636 // the tabBox that this is attached to.
28639 getAutoCreate : function()
28647 cfg.cls += ' active';
28652 initEvents : function()
28654 //Roo.log('trigger add pane handler');
28655 this.parent().fireEvent('addpane', this)
28659 * Updates the tab title
28660 * @param {String} html to set the title to.
28662 setTitle: function(str)
28668 this.tab.select('a', true).first().dom.innerHTML = str;
28685 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28688 * @class Roo.bootstrap.menu.Menu
28689 * @extends Roo.bootstrap.Component
28690 * Bootstrap Menu class - container for Menu
28691 * @cfg {String} html Text of the menu
28692 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28693 * @cfg {String} icon Font awesome icon
28694 * @cfg {String} pos Menu align to (top | bottom) default bottom
28698 * Create a new Menu
28699 * @param {Object} config The config object
28703 Roo.bootstrap.menu.Menu = function(config){
28704 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28708 * @event beforeshow
28709 * Fires before this menu is displayed
28710 * @param {Roo.bootstrap.menu.Menu} this
28714 * @event beforehide
28715 * Fires before this menu is hidden
28716 * @param {Roo.bootstrap.menu.Menu} this
28721 * Fires after this menu is displayed
28722 * @param {Roo.bootstrap.menu.Menu} this
28727 * Fires after this menu is hidden
28728 * @param {Roo.bootstrap.menu.Menu} this
28733 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28734 * @param {Roo.bootstrap.menu.Menu} this
28735 * @param {Roo.EventObject} e
28742 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
28746 weight : 'default',
28751 getChildContainer : function() {
28752 if(this.isSubMenu){
28756 return this.el.select('ul.dropdown-menu', true).first();
28759 getAutoCreate : function()
28764 cls : 'roo-menu-text',
28772 cls : 'fa ' + this.icon
28783 cls : 'dropdown-button btn btn-' + this.weight,
28788 cls : 'dropdown-toggle btn btn-' + this.weight,
28798 cls : 'dropdown-menu'
28804 if(this.pos == 'top'){
28805 cfg.cls += ' dropup';
28808 if(this.isSubMenu){
28811 cls : 'dropdown-menu'
28818 onRender : function(ct, position)
28820 this.isSubMenu = ct.hasClass('dropdown-submenu');
28822 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28825 initEvents : function()
28827 if(this.isSubMenu){
28831 this.hidden = true;
28833 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28834 this.triggerEl.on('click', this.onTriggerPress, this);
28836 this.buttonEl = this.el.select('button.dropdown-button', true).first();
28837 this.buttonEl.on('click', this.onClick, this);
28843 if(this.isSubMenu){
28847 return this.el.select('ul.dropdown-menu', true).first();
28850 onClick : function(e)
28852 this.fireEvent("click", this, e);
28855 onTriggerPress : function(e)
28857 if (this.isVisible()) {
28864 isVisible : function(){
28865 return !this.hidden;
28870 this.fireEvent("beforeshow", this);
28872 this.hidden = false;
28873 this.el.addClass('open');
28875 Roo.get(document).on("mouseup", this.onMouseUp, this);
28877 this.fireEvent("show", this);
28884 this.fireEvent("beforehide", this);
28886 this.hidden = true;
28887 this.el.removeClass('open');
28889 Roo.get(document).un("mouseup", this.onMouseUp);
28891 this.fireEvent("hide", this);
28894 onMouseUp : function()
28908 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28911 * @class Roo.bootstrap.menu.Item
28912 * @extends Roo.bootstrap.Component
28913 * Bootstrap MenuItem class
28914 * @cfg {Boolean} submenu (true | false) default false
28915 * @cfg {String} html text of the item
28916 * @cfg {String} href the link
28917 * @cfg {Boolean} disable (true | false) default false
28918 * @cfg {Boolean} preventDefault (true | false) default true
28919 * @cfg {String} icon Font awesome icon
28920 * @cfg {String} pos Submenu align to (left | right) default right
28924 * Create a new Item
28925 * @param {Object} config The config object
28929 Roo.bootstrap.menu.Item = function(config){
28930 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28934 * Fires when the mouse is hovering over this menu
28935 * @param {Roo.bootstrap.menu.Item} this
28936 * @param {Roo.EventObject} e
28941 * Fires when the mouse exits this menu
28942 * @param {Roo.bootstrap.menu.Item} this
28943 * @param {Roo.EventObject} e
28949 * The raw click event for the entire grid.
28950 * @param {Roo.EventObject} e
28956 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
28961 preventDefault: true,
28966 getAutoCreate : function()
28971 cls : 'roo-menu-item-text',
28979 cls : 'fa ' + this.icon
28988 href : this.href || '#',
28995 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28999 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
29001 if(this.pos == 'left'){
29002 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
29009 initEvents : function()
29011 this.el.on('mouseover', this.onMouseOver, this);
29012 this.el.on('mouseout', this.onMouseOut, this);
29014 this.el.select('a', true).first().on('click', this.onClick, this);
29018 onClick : function(e)
29020 if(this.preventDefault){
29021 e.preventDefault();
29024 this.fireEvent("click", this, e);
29027 onMouseOver : function(e)
29029 if(this.submenu && this.pos == 'left'){
29030 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29033 this.fireEvent("mouseover", this, e);
29036 onMouseOut : function(e)
29038 this.fireEvent("mouseout", this, e);
29050 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29053 * @class Roo.bootstrap.menu.Separator
29054 * @extends Roo.bootstrap.Component
29055 * Bootstrap Separator class
29058 * Create a new Separator
29059 * @param {Object} config The config object
29063 Roo.bootstrap.menu.Separator = function(config){
29064 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29067 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
29069 getAutoCreate : function(){
29072 cls: 'dropdown-divider divider'
29090 * @class Roo.bootstrap.Tooltip
29091 * Bootstrap Tooltip class
29092 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29093 * to determine which dom element triggers the tooltip.
29095 * It needs to add support for additional attributes like tooltip-position
29098 * Create a new Toolti
29099 * @param {Object} config The config object
29102 Roo.bootstrap.Tooltip = function(config){
29103 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29105 this.alignment = Roo.bootstrap.Tooltip.alignment;
29107 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29108 this.alignment = config.alignment;
29113 Roo.apply(Roo.bootstrap.Tooltip, {
29115 * @function init initialize tooltip monitoring.
29119 currentTip : false,
29120 currentRegion : false,
29126 Roo.get(document).on('mouseover', this.enter ,this);
29127 Roo.get(document).on('mouseout', this.leave, this);
29130 this.currentTip = new Roo.bootstrap.Tooltip();
29133 enter : function(ev)
29135 var dom = ev.getTarget();
29137 //Roo.log(['enter',dom]);
29138 var el = Roo.fly(dom);
29139 if (this.currentEl) {
29141 //Roo.log(this.currentEl);
29142 //Roo.log(this.currentEl.contains(dom));
29143 if (this.currentEl == el) {
29146 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29152 if (this.currentTip.el) {
29153 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29157 if(!el || el.dom == document){
29163 if (!el.attr('tooltip')) {
29164 pel = el.findParent("[tooltip]");
29166 bindEl = Roo.get(pel);
29172 // you can not look for children, as if el is the body.. then everythign is the child..
29173 if (!pel && !el.attr('tooltip')) { //
29174 if (!el.select("[tooltip]").elements.length) {
29177 // is the mouse over this child...?
29178 bindEl = el.select("[tooltip]").first();
29179 var xy = ev.getXY();
29180 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29181 //Roo.log("not in region.");
29184 //Roo.log("child element over..");
29187 this.currentEl = el;
29188 this.currentTip.bind(bindEl);
29189 this.currentRegion = Roo.lib.Region.getRegion(dom);
29190 this.currentTip.enter();
29193 leave : function(ev)
29195 var dom = ev.getTarget();
29196 //Roo.log(['leave',dom]);
29197 if (!this.currentEl) {
29202 if (dom != this.currentEl.dom) {
29205 var xy = ev.getXY();
29206 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
29209 // only activate leave if mouse cursor is outside... bounding box..
29214 if (this.currentTip) {
29215 this.currentTip.leave();
29217 //Roo.log('clear currentEl');
29218 this.currentEl = false;
29223 'left' : ['r-l', [-2,0], 'right'],
29224 'right' : ['l-r', [2,0], 'left'],
29225 'bottom' : ['t-b', [0,2], 'top'],
29226 'top' : [ 'b-t', [0,-2], 'bottom']
29232 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
29237 delay : null, // can be { show : 300 , hide: 500}
29241 hoverState : null, //???
29243 placement : 'bottom',
29247 getAutoCreate : function(){
29254 cls : 'tooltip-arrow arrow'
29257 cls : 'tooltip-inner'
29264 bind : function(el)
29269 initEvents : function()
29271 this.arrowEl = this.el.select('.arrow', true).first();
29272 this.innerEl = this.el.select('.tooltip-inner', true).first();
29275 enter : function () {
29277 if (this.timeout != null) {
29278 clearTimeout(this.timeout);
29281 this.hoverState = 'in';
29282 //Roo.log("enter - show");
29283 if (!this.delay || !this.delay.show) {
29288 this.timeout = setTimeout(function () {
29289 if (_t.hoverState == 'in') {
29292 }, this.delay.show);
29296 clearTimeout(this.timeout);
29298 this.hoverState = 'out';
29299 if (!this.delay || !this.delay.hide) {
29305 this.timeout = setTimeout(function () {
29306 //Roo.log("leave - timeout");
29308 if (_t.hoverState == 'out') {
29310 Roo.bootstrap.Tooltip.currentEl = false;
29315 show : function (msg)
29318 this.render(document.body);
29321 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29323 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29325 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29327 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29328 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29330 var placement = typeof this.placement == 'function' ?
29331 this.placement.call(this, this.el, on_el) :
29334 var autoToken = /\s?auto?\s?/i;
29335 var autoPlace = autoToken.test(placement);
29337 placement = placement.replace(autoToken, '') || 'top';
29341 //this.el.setXY([0,0]);
29343 //this.el.dom.style.display='block';
29345 //this.el.appendTo(on_el);
29347 var p = this.getPosition();
29348 var box = this.el.getBox();
29354 var align = this.alignment[placement];
29356 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29358 if(placement == 'top' || placement == 'bottom'){
29360 placement = 'right';
29363 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29364 placement = 'left';
29367 var scroll = Roo.select('body', true).first().getScroll();
29369 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29373 align = this.alignment[placement];
29375 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29379 var elems = document.getElementsByTagName('div');
29380 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29381 for (var i = 0; i < elems.length; i++) {
29382 var zindex = Number.parseInt(
29383 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29386 if (zindex > highest) {
29393 this.el.dom.style.zIndex = highest;
29395 this.el.alignTo(this.bindEl, align[0],align[1]);
29396 //var arrow = this.el.select('.arrow',true).first();
29397 //arrow.set(align[2],
29399 this.el.addClass(placement);
29400 this.el.addClass("bs-tooltip-"+ placement);
29402 this.el.addClass('in fade show');
29404 this.hoverState = null;
29406 if (this.el.hasClass('fade')) {
29421 //this.el.setXY([0,0]);
29422 this.el.removeClass(['show', 'in']);
29438 * @class Roo.bootstrap.LocationPicker
29439 * @extends Roo.bootstrap.Component
29440 * Bootstrap LocationPicker class
29441 * @cfg {Number} latitude Position when init default 0
29442 * @cfg {Number} longitude Position when init default 0
29443 * @cfg {Number} zoom default 15
29444 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29445 * @cfg {Boolean} mapTypeControl default false
29446 * @cfg {Boolean} disableDoubleClickZoom default false
29447 * @cfg {Boolean} scrollwheel default true
29448 * @cfg {Boolean} streetViewControl default false
29449 * @cfg {Number} radius default 0
29450 * @cfg {String} locationName
29451 * @cfg {Boolean} draggable default true
29452 * @cfg {Boolean} enableAutocomplete default false
29453 * @cfg {Boolean} enableReverseGeocode default true
29454 * @cfg {String} markerTitle
29457 * Create a new LocationPicker
29458 * @param {Object} config The config object
29462 Roo.bootstrap.LocationPicker = function(config){
29464 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29469 * Fires when the picker initialized.
29470 * @param {Roo.bootstrap.LocationPicker} this
29471 * @param {Google Location} location
29475 * @event positionchanged
29476 * Fires when the picker position changed.
29477 * @param {Roo.bootstrap.LocationPicker} this
29478 * @param {Google Location} location
29480 positionchanged : true,
29483 * Fires when the map resize.
29484 * @param {Roo.bootstrap.LocationPicker} this
29489 * Fires when the map show.
29490 * @param {Roo.bootstrap.LocationPicker} this
29495 * Fires when the map hide.
29496 * @param {Roo.bootstrap.LocationPicker} this
29501 * Fires when click the map.
29502 * @param {Roo.bootstrap.LocationPicker} this
29503 * @param {Map event} e
29507 * @event mapRightClick
29508 * Fires when right click the map.
29509 * @param {Roo.bootstrap.LocationPicker} this
29510 * @param {Map event} e
29512 mapRightClick : true,
29514 * @event markerClick
29515 * Fires when click the marker.
29516 * @param {Roo.bootstrap.LocationPicker} this
29517 * @param {Map event} e
29519 markerClick : true,
29521 * @event markerRightClick
29522 * Fires when right click the marker.
29523 * @param {Roo.bootstrap.LocationPicker} this
29524 * @param {Map event} e
29526 markerRightClick : true,
29528 * @event OverlayViewDraw
29529 * Fires when OverlayView Draw
29530 * @param {Roo.bootstrap.LocationPicker} this
29532 OverlayViewDraw : true,
29534 * @event OverlayViewOnAdd
29535 * Fires when OverlayView Draw
29536 * @param {Roo.bootstrap.LocationPicker} this
29538 OverlayViewOnAdd : true,
29540 * @event OverlayViewOnRemove
29541 * Fires when OverlayView Draw
29542 * @param {Roo.bootstrap.LocationPicker} this
29544 OverlayViewOnRemove : true,
29546 * @event OverlayViewShow
29547 * Fires when OverlayView Draw
29548 * @param {Roo.bootstrap.LocationPicker} this
29549 * @param {Pixel} cpx
29551 OverlayViewShow : true,
29553 * @event OverlayViewHide
29554 * Fires when OverlayView Draw
29555 * @param {Roo.bootstrap.LocationPicker} this
29557 OverlayViewHide : true,
29559 * @event loadexception
29560 * Fires when load google lib failed.
29561 * @param {Roo.bootstrap.LocationPicker} this
29563 loadexception : true
29568 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
29570 gMapContext: false,
29576 mapTypeControl: false,
29577 disableDoubleClickZoom: false,
29579 streetViewControl: false,
29583 enableAutocomplete: false,
29584 enableReverseGeocode: true,
29587 getAutoCreate: function()
29592 cls: 'roo-location-picker'
29598 initEvents: function(ct, position)
29600 if(!this.el.getWidth() || this.isApplied()){
29604 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29609 initial: function()
29611 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29612 this.fireEvent('loadexception', this);
29616 if(!this.mapTypeId){
29617 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29620 this.gMapContext = this.GMapContext();
29622 this.initOverlayView();
29624 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29628 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29629 _this.setPosition(_this.gMapContext.marker.position);
29632 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29633 _this.fireEvent('mapClick', this, event);
29637 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29638 _this.fireEvent('mapRightClick', this, event);
29642 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29643 _this.fireEvent('markerClick', this, event);
29647 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29648 _this.fireEvent('markerRightClick', this, event);
29652 this.setPosition(this.gMapContext.location);
29654 this.fireEvent('initial', this, this.gMapContext.location);
29657 initOverlayView: function()
29661 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29665 _this.fireEvent('OverlayViewDraw', _this);
29670 _this.fireEvent('OverlayViewOnAdd', _this);
29673 onRemove: function()
29675 _this.fireEvent('OverlayViewOnRemove', _this);
29678 show: function(cpx)
29680 _this.fireEvent('OverlayViewShow', _this, cpx);
29685 _this.fireEvent('OverlayViewHide', _this);
29691 fromLatLngToContainerPixel: function(event)
29693 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29696 isApplied: function()
29698 return this.getGmapContext() == false ? false : true;
29701 getGmapContext: function()
29703 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29706 GMapContext: function()
29708 var position = new google.maps.LatLng(this.latitude, this.longitude);
29710 var _map = new google.maps.Map(this.el.dom, {
29713 mapTypeId: this.mapTypeId,
29714 mapTypeControl: this.mapTypeControl,
29715 disableDoubleClickZoom: this.disableDoubleClickZoom,
29716 scrollwheel: this.scrollwheel,
29717 streetViewControl: this.streetViewControl,
29718 locationName: this.locationName,
29719 draggable: this.draggable,
29720 enableAutocomplete: this.enableAutocomplete,
29721 enableReverseGeocode: this.enableReverseGeocode
29724 var _marker = new google.maps.Marker({
29725 position: position,
29727 title: this.markerTitle,
29728 draggable: this.draggable
29735 location: position,
29736 radius: this.radius,
29737 locationName: this.locationName,
29738 addressComponents: {
29739 formatted_address: null,
29740 addressLine1: null,
29741 addressLine2: null,
29743 streetNumber: null,
29747 stateOrProvince: null
29750 domContainer: this.el.dom,
29751 geodecoder: new google.maps.Geocoder()
29755 drawCircle: function(center, radius, options)
29757 if (this.gMapContext.circle != null) {
29758 this.gMapContext.circle.setMap(null);
29762 options = Roo.apply({}, options, {
29763 strokeColor: "#0000FF",
29764 strokeOpacity: .35,
29766 fillColor: "#0000FF",
29770 options.map = this.gMapContext.map;
29771 options.radius = radius;
29772 options.center = center;
29773 this.gMapContext.circle = new google.maps.Circle(options);
29774 return this.gMapContext.circle;
29780 setPosition: function(location)
29782 this.gMapContext.location = location;
29783 this.gMapContext.marker.setPosition(location);
29784 this.gMapContext.map.panTo(location);
29785 this.drawCircle(location, this.gMapContext.radius, {});
29789 if (this.gMapContext.settings.enableReverseGeocode) {
29790 this.gMapContext.geodecoder.geocode({
29791 latLng: this.gMapContext.location
29792 }, function(results, status) {
29794 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29795 _this.gMapContext.locationName = results[0].formatted_address;
29796 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29798 _this.fireEvent('positionchanged', this, location);
29805 this.fireEvent('positionchanged', this, location);
29810 google.maps.event.trigger(this.gMapContext.map, "resize");
29812 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29814 this.fireEvent('resize', this);
29817 setPositionByLatLng: function(latitude, longitude)
29819 this.setPosition(new google.maps.LatLng(latitude, longitude));
29822 getCurrentPosition: function()
29825 latitude: this.gMapContext.location.lat(),
29826 longitude: this.gMapContext.location.lng()
29830 getAddressName: function()
29832 return this.gMapContext.locationName;
29835 getAddressComponents: function()
29837 return this.gMapContext.addressComponents;
29840 address_component_from_google_geocode: function(address_components)
29844 for (var i = 0; i < address_components.length; i++) {
29845 var component = address_components[i];
29846 if (component.types.indexOf("postal_code") >= 0) {
29847 result.postalCode = component.short_name;
29848 } else if (component.types.indexOf("street_number") >= 0) {
29849 result.streetNumber = component.short_name;
29850 } else if (component.types.indexOf("route") >= 0) {
29851 result.streetName = component.short_name;
29852 } else if (component.types.indexOf("neighborhood") >= 0) {
29853 result.city = component.short_name;
29854 } else if (component.types.indexOf("locality") >= 0) {
29855 result.city = component.short_name;
29856 } else if (component.types.indexOf("sublocality") >= 0) {
29857 result.district = component.short_name;
29858 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29859 result.stateOrProvince = component.short_name;
29860 } else if (component.types.indexOf("country") >= 0) {
29861 result.country = component.short_name;
29865 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29866 result.addressLine2 = "";
29870 setZoomLevel: function(zoom)
29872 this.gMapContext.map.setZoom(zoom);
29885 this.fireEvent('show', this);
29896 this.fireEvent('hide', this);
29901 Roo.apply(Roo.bootstrap.LocationPicker, {
29903 OverlayView : function(map, options)
29905 options = options || {};
29912 * @class Roo.bootstrap.Alert
29913 * @extends Roo.bootstrap.Component
29914 * Bootstrap Alert class - shows an alert area box
29916 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29917 Enter a valid email address
29920 * @cfg {String} title The title of alert
29921 * @cfg {String} html The content of alert
29922 * @cfg {String} weight (success|info|warning|danger) Weight of the message
29923 * @cfg {String} fa font-awesomeicon
29924 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
29925 * @cfg {Boolean} close true to show a x closer
29929 * Create a new alert
29930 * @param {Object} config The config object
29934 Roo.bootstrap.Alert = function(config){
29935 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29939 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
29945 faicon: false, // BC
29949 getAutoCreate : function()
29961 style : this.close ? '' : 'display:none'
29965 cls : 'roo-alert-icon'
29970 cls : 'roo-alert-title',
29975 cls : 'roo-alert-text',
29982 cfg.cn[0].cls += ' fa ' + this.faicon;
29985 cfg.cn[0].cls += ' fa ' + this.fa;
29989 cfg.cls += ' alert-' + this.weight;
29995 initEvents: function()
29997 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29998 this.titleEl = this.el.select('.roo-alert-title',true).first();
29999 this.iconEl = this.el.select('.roo-alert-icon',true).first();
30000 this.htmlEl = this.el.select('.roo-alert-text',true).first();
30001 if (this.seconds > 0) {
30002 this.hide.defer(this.seconds, this);
30006 * Set the Title Message HTML
30007 * @param {String} html
30009 setTitle : function(str)
30011 this.titleEl.dom.innerHTML = str;
30015 * Set the Body Message HTML
30016 * @param {String} html
30018 setHtml : function(str)
30020 this.htmlEl.dom.innerHTML = str;
30023 * Set the Weight of the alert
30024 * @param {String} (success|info|warning|danger) weight
30027 setWeight : function(weight)
30030 this.el.removeClass('alert-' + this.weight);
30033 this.weight = weight;
30035 this.el.addClass('alert-' + this.weight);
30038 * Set the Icon of the alert
30039 * @param {String} see fontawsome names (name without the 'fa-' bit)
30041 setIcon : function(icon)
30044 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30047 this.faicon = icon;
30049 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30074 * @class Roo.bootstrap.UploadCropbox
30075 * @extends Roo.bootstrap.Component
30076 * Bootstrap UploadCropbox class
30077 * @cfg {String} emptyText show when image has been loaded
30078 * @cfg {String} rotateNotify show when image too small to rotate
30079 * @cfg {Number} errorTimeout default 3000
30080 * @cfg {Number} minWidth default 300
30081 * @cfg {Number} minHeight default 300
30082 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30083 * @cfg {Boolean} isDocument (true|false) default false
30084 * @cfg {String} url action url
30085 * @cfg {String} paramName default 'imageUpload'
30086 * @cfg {String} method default POST
30087 * @cfg {Boolean} loadMask (true|false) default true
30088 * @cfg {Boolean} loadingText default 'Loading...'
30091 * Create a new UploadCropbox
30092 * @param {Object} config The config object
30095 Roo.bootstrap.UploadCropbox = function(config){
30096 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30100 * @event beforeselectfile
30101 * Fire before select file
30102 * @param {Roo.bootstrap.UploadCropbox} this
30104 "beforeselectfile" : true,
30107 * Fire after initEvent
30108 * @param {Roo.bootstrap.UploadCropbox} this
30113 * Fire after initEvent
30114 * @param {Roo.bootstrap.UploadCropbox} this
30115 * @param {String} data
30120 * Fire when preparing the file data
30121 * @param {Roo.bootstrap.UploadCropbox} this
30122 * @param {Object} file
30127 * Fire when get exception
30128 * @param {Roo.bootstrap.UploadCropbox} this
30129 * @param {XMLHttpRequest} xhr
30131 "exception" : true,
30133 * @event beforeloadcanvas
30134 * Fire before load the canvas
30135 * @param {Roo.bootstrap.UploadCropbox} this
30136 * @param {String} src
30138 "beforeloadcanvas" : true,
30141 * Fire when trash image
30142 * @param {Roo.bootstrap.UploadCropbox} this
30147 * Fire when download the image
30148 * @param {Roo.bootstrap.UploadCropbox} this
30152 * @event footerbuttonclick
30153 * Fire when footerbuttonclick
30154 * @param {Roo.bootstrap.UploadCropbox} this
30155 * @param {String} type
30157 "footerbuttonclick" : true,
30161 * @param {Roo.bootstrap.UploadCropbox} this
30166 * Fire when rotate the image
30167 * @param {Roo.bootstrap.UploadCropbox} this
30168 * @param {String} pos
30173 * Fire when inspect the file
30174 * @param {Roo.bootstrap.UploadCropbox} this
30175 * @param {Object} file
30180 * Fire when xhr upload the file
30181 * @param {Roo.bootstrap.UploadCropbox} this
30182 * @param {Object} data
30187 * Fire when arrange the file data
30188 * @param {Roo.bootstrap.UploadCropbox} this
30189 * @param {Object} formData
30194 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30197 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
30199 emptyText : 'Click to upload image',
30200 rotateNotify : 'Image is too small to rotate',
30201 errorTimeout : 3000,
30215 cropType : 'image/jpeg',
30217 canvasLoaded : false,
30218 isDocument : false,
30220 paramName : 'imageUpload',
30222 loadingText : 'Loading...',
30225 getAutoCreate : function()
30229 cls : 'roo-upload-cropbox',
30233 cls : 'roo-upload-cropbox-selector',
30238 cls : 'roo-upload-cropbox-body',
30239 style : 'cursor:pointer',
30243 cls : 'roo-upload-cropbox-preview'
30247 cls : 'roo-upload-cropbox-thumb'
30251 cls : 'roo-upload-cropbox-empty-notify',
30252 html : this.emptyText
30256 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30257 html : this.rotateNotify
30263 cls : 'roo-upload-cropbox-footer',
30266 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30276 onRender : function(ct, position)
30278 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30280 if (this.buttons.length) {
30282 Roo.each(this.buttons, function(bb) {
30284 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30286 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30292 this.maskEl = this.el;
30296 initEvents : function()
30298 this.urlAPI = (window.createObjectURL && window) ||
30299 (window.URL && URL.revokeObjectURL && URL) ||
30300 (window.webkitURL && webkitURL);
30302 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30303 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30305 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30306 this.selectorEl.hide();
30308 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30309 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30311 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30312 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30313 this.thumbEl.hide();
30315 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30316 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30318 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30319 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30320 this.errorEl.hide();
30322 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30323 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30324 this.footerEl.hide();
30326 this.setThumbBoxSize();
30332 this.fireEvent('initial', this);
30339 window.addEventListener("resize", function() { _this.resize(); } );
30341 this.bodyEl.on('click', this.beforeSelectFile, this);
30344 this.bodyEl.on('touchstart', this.onTouchStart, this);
30345 this.bodyEl.on('touchmove', this.onTouchMove, this);
30346 this.bodyEl.on('touchend', this.onTouchEnd, this);
30350 this.bodyEl.on('mousedown', this.onMouseDown, this);
30351 this.bodyEl.on('mousemove', this.onMouseMove, this);
30352 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30353 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30354 Roo.get(document).on('mouseup', this.onMouseUp, this);
30357 this.selectorEl.on('change', this.onFileSelected, this);
30363 this.baseScale = 1;
30365 this.baseRotate = 1;
30366 this.dragable = false;
30367 this.pinching = false;
30370 this.cropData = false;
30371 this.notifyEl.dom.innerHTML = this.emptyText;
30373 this.selectorEl.dom.value = '';
30377 resize : function()
30379 if(this.fireEvent('resize', this) != false){
30380 this.setThumbBoxPosition();
30381 this.setCanvasPosition();
30385 onFooterButtonClick : function(e, el, o, type)
30388 case 'rotate-left' :
30389 this.onRotateLeft(e);
30391 case 'rotate-right' :
30392 this.onRotateRight(e);
30395 this.beforeSelectFile(e);
30410 this.fireEvent('footerbuttonclick', this, type);
30413 beforeSelectFile : function(e)
30415 e.preventDefault();
30417 if(this.fireEvent('beforeselectfile', this) != false){
30418 this.selectorEl.dom.click();
30422 onFileSelected : function(e)
30424 e.preventDefault();
30426 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30430 var file = this.selectorEl.dom.files[0];
30432 if(this.fireEvent('inspect', this, file) != false){
30433 this.prepare(file);
30438 trash : function(e)
30440 this.fireEvent('trash', this);
30443 download : function(e)
30445 this.fireEvent('download', this);
30448 loadCanvas : function(src)
30450 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30454 this.imageEl = document.createElement('img');
30458 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30460 this.imageEl.src = src;
30464 onLoadCanvas : function()
30466 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30467 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30469 this.bodyEl.un('click', this.beforeSelectFile, this);
30471 this.notifyEl.hide();
30472 this.thumbEl.show();
30473 this.footerEl.show();
30475 this.baseRotateLevel();
30477 if(this.isDocument){
30478 this.setThumbBoxSize();
30481 this.setThumbBoxPosition();
30483 this.baseScaleLevel();
30489 this.canvasLoaded = true;
30492 this.maskEl.unmask();
30497 setCanvasPosition : function()
30499 if(!this.canvasEl){
30503 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30504 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30506 this.previewEl.setLeft(pw);
30507 this.previewEl.setTop(ph);
30511 onMouseDown : function(e)
30515 this.dragable = true;
30516 this.pinching = false;
30518 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30519 this.dragable = false;
30523 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30524 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30528 onMouseMove : function(e)
30532 if(!this.canvasLoaded){
30536 if (!this.dragable){
30540 var minX = Math.ceil(this.thumbEl.getLeft(true));
30541 var minY = Math.ceil(this.thumbEl.getTop(true));
30543 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30544 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30546 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30547 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30549 x = x - this.mouseX;
30550 y = y - this.mouseY;
30552 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30553 var bgY = Math.ceil(y + this.previewEl.getTop(true));
30555 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30556 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30558 this.previewEl.setLeft(bgX);
30559 this.previewEl.setTop(bgY);
30561 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30562 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30565 onMouseUp : function(e)
30569 this.dragable = false;
30572 onMouseWheel : function(e)
30576 this.startScale = this.scale;
30578 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30580 if(!this.zoomable()){
30581 this.scale = this.startScale;
30590 zoomable : function()
30592 var minScale = this.thumbEl.getWidth() / this.minWidth;
30594 if(this.minWidth < this.minHeight){
30595 minScale = this.thumbEl.getHeight() / this.minHeight;
30598 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30599 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30603 (this.rotate == 0 || this.rotate == 180) &&
30605 width > this.imageEl.OriginWidth ||
30606 height > this.imageEl.OriginHeight ||
30607 (width < this.minWidth && height < this.minHeight)
30615 (this.rotate == 90 || this.rotate == 270) &&
30617 width > this.imageEl.OriginWidth ||
30618 height > this.imageEl.OriginHeight ||
30619 (width < this.minHeight && height < this.minWidth)
30626 !this.isDocument &&
30627 (this.rotate == 0 || this.rotate == 180) &&
30629 width < this.minWidth ||
30630 width > this.imageEl.OriginWidth ||
30631 height < this.minHeight ||
30632 height > this.imageEl.OriginHeight
30639 !this.isDocument &&
30640 (this.rotate == 90 || this.rotate == 270) &&
30642 width < this.minHeight ||
30643 width > this.imageEl.OriginWidth ||
30644 height < this.minWidth ||
30645 height > this.imageEl.OriginHeight
30655 onRotateLeft : function(e)
30657 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30659 var minScale = this.thumbEl.getWidth() / this.minWidth;
30661 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30662 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30664 this.startScale = this.scale;
30666 while (this.getScaleLevel() < minScale){
30668 this.scale = this.scale + 1;
30670 if(!this.zoomable()){
30675 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30676 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30681 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30688 this.scale = this.startScale;
30690 this.onRotateFail();
30695 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30697 if(this.isDocument){
30698 this.setThumbBoxSize();
30699 this.setThumbBoxPosition();
30700 this.setCanvasPosition();
30705 this.fireEvent('rotate', this, 'left');
30709 onRotateRight : function(e)
30711 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30713 var minScale = this.thumbEl.getWidth() / this.minWidth;
30715 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30716 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30718 this.startScale = this.scale;
30720 while (this.getScaleLevel() < minScale){
30722 this.scale = this.scale + 1;
30724 if(!this.zoomable()){
30729 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30730 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30735 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30742 this.scale = this.startScale;
30744 this.onRotateFail();
30749 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30751 if(this.isDocument){
30752 this.setThumbBoxSize();
30753 this.setThumbBoxPosition();
30754 this.setCanvasPosition();
30759 this.fireEvent('rotate', this, 'right');
30762 onRotateFail : function()
30764 this.errorEl.show(true);
30768 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30773 this.previewEl.dom.innerHTML = '';
30775 var canvasEl = document.createElement("canvas");
30777 var contextEl = canvasEl.getContext("2d");
30779 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30780 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30781 var center = this.imageEl.OriginWidth / 2;
30783 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30784 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30785 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30786 center = this.imageEl.OriginHeight / 2;
30789 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30791 contextEl.translate(center, center);
30792 contextEl.rotate(this.rotate * Math.PI / 180);
30794 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30796 this.canvasEl = document.createElement("canvas");
30798 this.contextEl = this.canvasEl.getContext("2d");
30800 switch (this.rotate) {
30803 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30804 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30806 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30811 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30812 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30814 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30815 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);
30819 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30824 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30825 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30827 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30828 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);
30832 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);
30837 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30838 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30840 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30841 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30845 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);
30852 this.previewEl.appendChild(this.canvasEl);
30854 this.setCanvasPosition();
30859 if(!this.canvasLoaded){
30863 var imageCanvas = document.createElement("canvas");
30865 var imageContext = imageCanvas.getContext("2d");
30867 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30868 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30870 var center = imageCanvas.width / 2;
30872 imageContext.translate(center, center);
30874 imageContext.rotate(this.rotate * Math.PI / 180);
30876 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30878 var canvas = document.createElement("canvas");
30880 var context = canvas.getContext("2d");
30882 canvas.width = this.minWidth;
30883 canvas.height = this.minHeight;
30885 switch (this.rotate) {
30888 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30889 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30891 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30892 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30894 var targetWidth = this.minWidth - 2 * x;
30895 var targetHeight = this.minHeight - 2 * y;
30899 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30900 scale = targetWidth / width;
30903 if(x > 0 && y == 0){
30904 scale = targetHeight / height;
30907 if(x > 0 && y > 0){
30908 scale = targetWidth / width;
30910 if(width < height){
30911 scale = targetHeight / height;
30915 context.scale(scale, scale);
30917 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30918 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30920 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30921 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30923 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30928 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30929 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30931 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30932 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30934 var targetWidth = this.minWidth - 2 * x;
30935 var targetHeight = this.minHeight - 2 * y;
30939 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30940 scale = targetWidth / width;
30943 if(x > 0 && y == 0){
30944 scale = targetHeight / height;
30947 if(x > 0 && y > 0){
30948 scale = targetWidth / width;
30950 if(width < height){
30951 scale = targetHeight / height;
30955 context.scale(scale, scale);
30957 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30958 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30960 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30961 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30963 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30965 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30970 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30971 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30973 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30974 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30976 var targetWidth = this.minWidth - 2 * x;
30977 var targetHeight = this.minHeight - 2 * y;
30981 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30982 scale = targetWidth / width;
30985 if(x > 0 && y == 0){
30986 scale = targetHeight / height;
30989 if(x > 0 && y > 0){
30990 scale = targetWidth / width;
30992 if(width < height){
30993 scale = targetHeight / height;
30997 context.scale(scale, scale);
30999 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31000 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31002 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31003 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31005 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31006 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31008 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31013 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31014 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31016 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31017 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31019 var targetWidth = this.minWidth - 2 * x;
31020 var targetHeight = this.minHeight - 2 * y;
31024 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31025 scale = targetWidth / width;
31028 if(x > 0 && y == 0){
31029 scale = targetHeight / height;
31032 if(x > 0 && y > 0){
31033 scale = targetWidth / width;
31035 if(width < height){
31036 scale = targetHeight / height;
31040 context.scale(scale, scale);
31042 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31043 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31045 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31046 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31048 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31050 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31057 this.cropData = canvas.toDataURL(this.cropType);
31059 if(this.fireEvent('crop', this, this.cropData) !== false){
31060 this.process(this.file, this.cropData);
31067 setThumbBoxSize : function()
31071 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31072 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31073 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31075 this.minWidth = width;
31076 this.minHeight = height;
31078 if(this.rotate == 90 || this.rotate == 270){
31079 this.minWidth = height;
31080 this.minHeight = width;
31085 width = Math.ceil(this.minWidth * height / this.minHeight);
31087 if(this.minWidth > this.minHeight){
31089 height = Math.ceil(this.minHeight * width / this.minWidth);
31092 this.thumbEl.setStyle({
31093 width : width + 'px',
31094 height : height + 'px'
31101 setThumbBoxPosition : function()
31103 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31104 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31106 this.thumbEl.setLeft(x);
31107 this.thumbEl.setTop(y);
31111 baseRotateLevel : function()
31113 this.baseRotate = 1;
31116 typeof(this.exif) != 'undefined' &&
31117 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31118 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31120 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31123 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31127 baseScaleLevel : function()
31131 if(this.isDocument){
31133 if(this.baseRotate == 6 || this.baseRotate == 8){
31135 height = this.thumbEl.getHeight();
31136 this.baseScale = height / this.imageEl.OriginWidth;
31138 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31139 width = this.thumbEl.getWidth();
31140 this.baseScale = width / this.imageEl.OriginHeight;
31146 height = this.thumbEl.getHeight();
31147 this.baseScale = height / this.imageEl.OriginHeight;
31149 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31150 width = this.thumbEl.getWidth();
31151 this.baseScale = width / this.imageEl.OriginWidth;
31157 if(this.baseRotate == 6 || this.baseRotate == 8){
31159 width = this.thumbEl.getHeight();
31160 this.baseScale = width / this.imageEl.OriginHeight;
31162 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31163 height = this.thumbEl.getWidth();
31164 this.baseScale = height / this.imageEl.OriginHeight;
31167 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31168 height = this.thumbEl.getWidth();
31169 this.baseScale = height / this.imageEl.OriginHeight;
31171 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31172 width = this.thumbEl.getHeight();
31173 this.baseScale = width / this.imageEl.OriginWidth;
31180 width = this.thumbEl.getWidth();
31181 this.baseScale = width / this.imageEl.OriginWidth;
31183 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31184 height = this.thumbEl.getHeight();
31185 this.baseScale = height / this.imageEl.OriginHeight;
31188 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31190 height = this.thumbEl.getHeight();
31191 this.baseScale = height / this.imageEl.OriginHeight;
31193 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31194 width = this.thumbEl.getWidth();
31195 this.baseScale = width / this.imageEl.OriginWidth;
31203 getScaleLevel : function()
31205 return this.baseScale * Math.pow(1.1, this.scale);
31208 onTouchStart : function(e)
31210 if(!this.canvasLoaded){
31211 this.beforeSelectFile(e);
31215 var touches = e.browserEvent.touches;
31221 if(touches.length == 1){
31222 this.onMouseDown(e);
31226 if(touches.length != 2){
31232 for(var i = 0, finger; finger = touches[i]; i++){
31233 coords.push(finger.pageX, finger.pageY);
31236 var x = Math.pow(coords[0] - coords[2], 2);
31237 var y = Math.pow(coords[1] - coords[3], 2);
31239 this.startDistance = Math.sqrt(x + y);
31241 this.startScale = this.scale;
31243 this.pinching = true;
31244 this.dragable = false;
31248 onTouchMove : function(e)
31250 if(!this.pinching && !this.dragable){
31254 var touches = e.browserEvent.touches;
31261 this.onMouseMove(e);
31267 for(var i = 0, finger; finger = touches[i]; i++){
31268 coords.push(finger.pageX, finger.pageY);
31271 var x = Math.pow(coords[0] - coords[2], 2);
31272 var y = Math.pow(coords[1] - coords[3], 2);
31274 this.endDistance = Math.sqrt(x + y);
31276 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31278 if(!this.zoomable()){
31279 this.scale = this.startScale;
31287 onTouchEnd : function(e)
31289 this.pinching = false;
31290 this.dragable = false;
31294 process : function(file, crop)
31297 this.maskEl.mask(this.loadingText);
31300 this.xhr = new XMLHttpRequest();
31302 file.xhr = this.xhr;
31304 this.xhr.open(this.method, this.url, true);
31307 "Accept": "application/json",
31308 "Cache-Control": "no-cache",
31309 "X-Requested-With": "XMLHttpRequest"
31312 for (var headerName in headers) {
31313 var headerValue = headers[headerName];
31315 this.xhr.setRequestHeader(headerName, headerValue);
31321 this.xhr.onload = function()
31323 _this.xhrOnLoad(_this.xhr);
31326 this.xhr.onerror = function()
31328 _this.xhrOnError(_this.xhr);
31331 var formData = new FormData();
31333 formData.append('returnHTML', 'NO');
31336 formData.append('crop', crop);
31339 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31340 formData.append(this.paramName, file, file.name);
31343 if(typeof(file.filename) != 'undefined'){
31344 formData.append('filename', file.filename);
31347 if(typeof(file.mimetype) != 'undefined'){
31348 formData.append('mimetype', file.mimetype);
31351 if(this.fireEvent('arrange', this, formData) != false){
31352 this.xhr.send(formData);
31356 xhrOnLoad : function(xhr)
31359 this.maskEl.unmask();
31362 if (xhr.readyState !== 4) {
31363 this.fireEvent('exception', this, xhr);
31367 var response = Roo.decode(xhr.responseText);
31369 if(!response.success){
31370 this.fireEvent('exception', this, xhr);
31374 var response = Roo.decode(xhr.responseText);
31376 this.fireEvent('upload', this, response);
31380 xhrOnError : function()
31383 this.maskEl.unmask();
31386 Roo.log('xhr on error');
31388 var response = Roo.decode(xhr.responseText);
31394 prepare : function(file)
31397 this.maskEl.mask(this.loadingText);
31403 if(typeof(file) === 'string'){
31404 this.loadCanvas(file);
31408 if(!file || !this.urlAPI){
31413 this.cropType = file.type;
31417 if(this.fireEvent('prepare', this, this.file) != false){
31419 var reader = new FileReader();
31421 reader.onload = function (e) {
31422 if (e.target.error) {
31423 Roo.log(e.target.error);
31427 var buffer = e.target.result,
31428 dataView = new DataView(buffer),
31430 maxOffset = dataView.byteLength - 4,
31434 if (dataView.getUint16(0) === 0xffd8) {
31435 while (offset < maxOffset) {
31436 markerBytes = dataView.getUint16(offset);
31438 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31439 markerLength = dataView.getUint16(offset + 2) + 2;
31440 if (offset + markerLength > dataView.byteLength) {
31441 Roo.log('Invalid meta data: Invalid segment size.');
31445 if(markerBytes == 0xffe1){
31446 _this.parseExifData(
31453 offset += markerLength;
31463 var url = _this.urlAPI.createObjectURL(_this.file);
31465 _this.loadCanvas(url);
31470 reader.readAsArrayBuffer(this.file);
31476 parseExifData : function(dataView, offset, length)
31478 var tiffOffset = offset + 10,
31482 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31483 // No Exif data, might be XMP data instead
31487 // Check for the ASCII code for "Exif" (0x45786966):
31488 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31489 // No Exif data, might be XMP data instead
31492 if (tiffOffset + 8 > dataView.byteLength) {
31493 Roo.log('Invalid Exif data: Invalid segment size.');
31496 // Check for the two null bytes:
31497 if (dataView.getUint16(offset + 8) !== 0x0000) {
31498 Roo.log('Invalid Exif data: Missing byte alignment offset.');
31501 // Check the byte alignment:
31502 switch (dataView.getUint16(tiffOffset)) {
31504 littleEndian = true;
31507 littleEndian = false;
31510 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31513 // Check for the TIFF tag marker (0x002A):
31514 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31515 Roo.log('Invalid Exif data: Missing TIFF marker.');
31518 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31519 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31521 this.parseExifTags(
31524 tiffOffset + dirOffset,
31529 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31534 if (dirOffset + 6 > dataView.byteLength) {
31535 Roo.log('Invalid Exif data: Invalid directory offset.');
31538 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31539 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31540 if (dirEndOffset + 4 > dataView.byteLength) {
31541 Roo.log('Invalid Exif data: Invalid directory size.');
31544 for (i = 0; i < tagsNumber; i += 1) {
31548 dirOffset + 2 + 12 * i, // tag offset
31552 // Return the offset to the next directory:
31553 return dataView.getUint32(dirEndOffset, littleEndian);
31556 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
31558 var tag = dataView.getUint16(offset, littleEndian);
31560 this.exif[tag] = this.getExifValue(
31564 dataView.getUint16(offset + 2, littleEndian), // tag type
31565 dataView.getUint32(offset + 4, littleEndian), // tag length
31570 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31572 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31581 Roo.log('Invalid Exif data: Invalid tag type.');
31585 tagSize = tagType.size * length;
31586 // Determine if the value is contained in the dataOffset bytes,
31587 // or if the value at the dataOffset is a pointer to the actual data:
31588 dataOffset = tagSize > 4 ?
31589 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31590 if (dataOffset + tagSize > dataView.byteLength) {
31591 Roo.log('Invalid Exif data: Invalid data offset.');
31594 if (length === 1) {
31595 return tagType.getValue(dataView, dataOffset, littleEndian);
31598 for (i = 0; i < length; i += 1) {
31599 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31602 if (tagType.ascii) {
31604 // Concatenate the chars:
31605 for (i = 0; i < values.length; i += 1) {
31607 // Ignore the terminating NULL byte(s):
31608 if (c === '\u0000') {
31620 Roo.apply(Roo.bootstrap.UploadCropbox, {
31622 'Orientation': 0x0112
31626 1: 0, //'top-left',
31628 3: 180, //'bottom-right',
31629 // 4: 'bottom-left',
31631 6: 90, //'right-top',
31632 // 7: 'right-bottom',
31633 8: 270 //'left-bottom'
31637 // byte, 8-bit unsigned int:
31639 getValue: function (dataView, dataOffset) {
31640 return dataView.getUint8(dataOffset);
31644 // ascii, 8-bit byte:
31646 getValue: function (dataView, dataOffset) {
31647 return String.fromCharCode(dataView.getUint8(dataOffset));
31652 // short, 16 bit int:
31654 getValue: function (dataView, dataOffset, littleEndian) {
31655 return dataView.getUint16(dataOffset, littleEndian);
31659 // long, 32 bit int:
31661 getValue: function (dataView, dataOffset, littleEndian) {
31662 return dataView.getUint32(dataOffset, littleEndian);
31666 // rational = two long values, first is numerator, second is denominator:
31668 getValue: function (dataView, dataOffset, littleEndian) {
31669 return dataView.getUint32(dataOffset, littleEndian) /
31670 dataView.getUint32(dataOffset + 4, littleEndian);
31674 // slong, 32 bit signed int:
31676 getValue: function (dataView, dataOffset, littleEndian) {
31677 return dataView.getInt32(dataOffset, littleEndian);
31681 // srational, two slongs, first is numerator, second is denominator:
31683 getValue: function (dataView, dataOffset, littleEndian) {
31684 return dataView.getInt32(dataOffset, littleEndian) /
31685 dataView.getInt32(dataOffset + 4, littleEndian);
31695 cls : 'btn-group roo-upload-cropbox-rotate-left',
31696 action : 'rotate-left',
31700 cls : 'btn btn-default',
31701 html : '<i class="fa fa-undo"></i>'
31707 cls : 'btn-group roo-upload-cropbox-picture',
31708 action : 'picture',
31712 cls : 'btn btn-default',
31713 html : '<i class="fa fa-picture-o"></i>'
31719 cls : 'btn-group roo-upload-cropbox-rotate-right',
31720 action : 'rotate-right',
31724 cls : 'btn btn-default',
31725 html : '<i class="fa fa-repeat"></i>'
31733 cls : 'btn-group roo-upload-cropbox-rotate-left',
31734 action : 'rotate-left',
31738 cls : 'btn btn-default',
31739 html : '<i class="fa fa-undo"></i>'
31745 cls : 'btn-group roo-upload-cropbox-download',
31746 action : 'download',
31750 cls : 'btn btn-default',
31751 html : '<i class="fa fa-download"></i>'
31757 cls : 'btn-group roo-upload-cropbox-crop',
31762 cls : 'btn btn-default',
31763 html : '<i class="fa fa-crop"></i>'
31769 cls : 'btn-group roo-upload-cropbox-trash',
31774 cls : 'btn btn-default',
31775 html : '<i class="fa fa-trash"></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-rotate-right',
31808 action : 'rotate-right',
31812 cls : 'btn btn-default',
31813 html : '<i class="fa fa-repeat"></i>'
31826 * @class Roo.bootstrap.DocumentManager
31827 * @extends Roo.bootstrap.Component
31828 * Bootstrap DocumentManager class
31829 * @cfg {String} paramName default 'imageUpload'
31830 * @cfg {String} toolTipName default 'filename'
31831 * @cfg {String} method default POST
31832 * @cfg {String} url action url
31833 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31834 * @cfg {Boolean} multiple multiple upload default true
31835 * @cfg {Number} thumbSize default 300
31836 * @cfg {String} fieldLabel
31837 * @cfg {Number} labelWidth default 4
31838 * @cfg {String} labelAlign (left|top) default left
31839 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31840 * @cfg {Number} labellg set the width of label (1-12)
31841 * @cfg {Number} labelmd set the width of label (1-12)
31842 * @cfg {Number} labelsm set the width of label (1-12)
31843 * @cfg {Number} labelxs set the width of label (1-12)
31846 * Create a new DocumentManager
31847 * @param {Object} config The config object
31850 Roo.bootstrap.DocumentManager = function(config){
31851 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31854 this.delegates = [];
31859 * Fire when initial the DocumentManager
31860 * @param {Roo.bootstrap.DocumentManager} this
31865 * inspect selected file
31866 * @param {Roo.bootstrap.DocumentManager} this
31867 * @param {File} file
31872 * Fire when xhr load exception
31873 * @param {Roo.bootstrap.DocumentManager} this
31874 * @param {XMLHttpRequest} xhr
31876 "exception" : true,
31878 * @event afterupload
31879 * Fire when xhr load exception
31880 * @param {Roo.bootstrap.DocumentManager} this
31881 * @param {XMLHttpRequest} xhr
31883 "afterupload" : true,
31886 * prepare the form data
31887 * @param {Roo.bootstrap.DocumentManager} this
31888 * @param {Object} formData
31893 * Fire when remove the file
31894 * @param {Roo.bootstrap.DocumentManager} this
31895 * @param {Object} file
31900 * Fire after refresh the file
31901 * @param {Roo.bootstrap.DocumentManager} this
31906 * Fire after click the image
31907 * @param {Roo.bootstrap.DocumentManager} this
31908 * @param {Object} file
31913 * Fire when upload a image and editable set to true
31914 * @param {Roo.bootstrap.DocumentManager} this
31915 * @param {Object} file
31919 * @event beforeselectfile
31920 * Fire before select file
31921 * @param {Roo.bootstrap.DocumentManager} this
31923 "beforeselectfile" : true,
31926 * Fire before process file
31927 * @param {Roo.bootstrap.DocumentManager} this
31928 * @param {Object} file
31932 * @event previewrendered
31933 * Fire when preview rendered
31934 * @param {Roo.bootstrap.DocumentManager} this
31935 * @param {Object} file
31937 "previewrendered" : true,
31940 "previewResize" : true
31945 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
31954 paramName : 'imageUpload',
31955 toolTipName : 'filename',
31958 labelAlign : 'left',
31968 getAutoCreate : function()
31970 var managerWidget = {
31972 cls : 'roo-document-manager',
31976 cls : 'roo-document-manager-selector',
31981 cls : 'roo-document-manager-uploader',
31985 cls : 'roo-document-manager-upload-btn',
31986 html : '<i class="fa fa-plus"></i>'
31997 cls : 'column col-md-12',
32002 if(this.fieldLabel.length){
32007 cls : 'column col-md-12',
32008 html : this.fieldLabel
32012 cls : 'column col-md-12',
32017 if(this.labelAlign == 'left'){
32022 html : this.fieldLabel
32031 if(this.labelWidth > 12){
32032 content[0].style = "width: " + this.labelWidth + 'px';
32035 if(this.labelWidth < 13 && this.labelmd == 0){
32036 this.labelmd = this.labelWidth;
32039 if(this.labellg > 0){
32040 content[0].cls += ' col-lg-' + this.labellg;
32041 content[1].cls += ' col-lg-' + (12 - this.labellg);
32044 if(this.labelmd > 0){
32045 content[0].cls += ' col-md-' + this.labelmd;
32046 content[1].cls += ' col-md-' + (12 - this.labelmd);
32049 if(this.labelsm > 0){
32050 content[0].cls += ' col-sm-' + this.labelsm;
32051 content[1].cls += ' col-sm-' + (12 - this.labelsm);
32054 if(this.labelxs > 0){
32055 content[0].cls += ' col-xs-' + this.labelxs;
32056 content[1].cls += ' col-xs-' + (12 - this.labelxs);
32064 cls : 'row clearfix',
32072 initEvents : function()
32074 this.managerEl = this.el.select('.roo-document-manager', true).first();
32075 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32077 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32078 this.selectorEl.hide();
32081 this.selectorEl.attr('multiple', 'multiple');
32084 this.selectorEl.on('change', this.onFileSelected, this);
32086 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32087 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32089 this.uploader.on('click', this.onUploaderClick, this);
32091 this.renderProgressDialog();
32095 window.addEventListener("resize", function() { _this.refresh(); } );
32097 this.fireEvent('initial', this);
32100 renderProgressDialog : function()
32104 this.progressDialog = new Roo.bootstrap.Modal({
32105 cls : 'roo-document-manager-progress-dialog',
32106 allow_close : false,
32117 btnclick : function() {
32118 _this.uploadCancel();
32124 this.progressDialog.render(Roo.get(document.body));
32126 this.progress = new Roo.bootstrap.Progress({
32127 cls : 'roo-document-manager-progress',
32132 this.progress.render(this.progressDialog.getChildContainer());
32134 this.progressBar = new Roo.bootstrap.ProgressBar({
32135 cls : 'roo-document-manager-progress-bar',
32138 aria_valuemax : 12,
32142 this.progressBar.render(this.progress.getChildContainer());
32145 onUploaderClick : function(e)
32147 e.preventDefault();
32149 if(this.fireEvent('beforeselectfile', this) != false){
32150 this.selectorEl.dom.click();
32155 onFileSelected : function(e)
32157 e.preventDefault();
32159 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32163 Roo.each(this.selectorEl.dom.files, function(file){
32164 if(this.fireEvent('inspect', this, file) != false){
32165 this.files.push(file);
32175 this.selectorEl.dom.value = '';
32177 if(!this.files || !this.files.length){
32181 if(this.boxes > 0 && this.files.length > this.boxes){
32182 this.files = this.files.slice(0, this.boxes);
32185 this.uploader.show();
32187 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32188 this.uploader.hide();
32197 Roo.each(this.files, function(file){
32199 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32200 var f = this.renderPreview(file);
32205 if(file.type.indexOf('image') != -1){
32206 this.delegates.push(
32208 _this.process(file);
32209 }).createDelegate(this)
32217 _this.process(file);
32218 }).createDelegate(this)
32223 this.files = files;
32225 this.delegates = this.delegates.concat(docs);
32227 if(!this.delegates.length){
32232 this.progressBar.aria_valuemax = this.delegates.length;
32239 arrange : function()
32241 if(!this.delegates.length){
32242 this.progressDialog.hide();
32247 var delegate = this.delegates.shift();
32249 this.progressDialog.show();
32251 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32253 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32258 refresh : function()
32260 this.uploader.show();
32262 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32263 this.uploader.hide();
32266 Roo.isTouch ? this.closable(false) : this.closable(true);
32268 this.fireEvent('refresh', this);
32271 onRemove : function(e, el, o)
32273 e.preventDefault();
32275 this.fireEvent('remove', this, o);
32279 remove : function(o)
32283 Roo.each(this.files, function(file){
32284 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32293 this.files = files;
32300 Roo.each(this.files, function(file){
32305 file.target.remove();
32314 onClick : function(e, el, o)
32316 e.preventDefault();
32318 this.fireEvent('click', this, o);
32322 closable : function(closable)
32324 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32326 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32338 xhrOnLoad : function(xhr)
32340 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32344 if (xhr.readyState !== 4) {
32346 this.fireEvent('exception', this, xhr);
32350 var response = Roo.decode(xhr.responseText);
32352 if(!response.success){
32354 this.fireEvent('exception', this, xhr);
32358 var file = this.renderPreview(response.data);
32360 this.files.push(file);
32364 this.fireEvent('afterupload', this, xhr);
32368 xhrOnError : function(xhr)
32370 Roo.log('xhr on error');
32372 var response = Roo.decode(xhr.responseText);
32379 process : function(file)
32381 if(this.fireEvent('process', this, file) !== false){
32382 if(this.editable && file.type.indexOf('image') != -1){
32383 this.fireEvent('edit', this, file);
32387 this.uploadStart(file, false);
32394 uploadStart : function(file, crop)
32396 this.xhr = new XMLHttpRequest();
32398 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32403 file.xhr = this.xhr;
32405 this.managerEl.createChild({
32407 cls : 'roo-document-manager-loading',
32411 tooltip : file.name,
32412 cls : 'roo-document-manager-thumb',
32413 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32419 this.xhr.open(this.method, this.url, true);
32422 "Accept": "application/json",
32423 "Cache-Control": "no-cache",
32424 "X-Requested-With": "XMLHttpRequest"
32427 for (var headerName in headers) {
32428 var headerValue = headers[headerName];
32430 this.xhr.setRequestHeader(headerName, headerValue);
32436 this.xhr.onload = function()
32438 _this.xhrOnLoad(_this.xhr);
32441 this.xhr.onerror = function()
32443 _this.xhrOnError(_this.xhr);
32446 var formData = new FormData();
32448 formData.append('returnHTML', 'NO');
32451 formData.append('crop', crop);
32454 formData.append(this.paramName, file, file.name);
32461 if(this.fireEvent('prepare', this, formData, options) != false){
32463 if(options.manually){
32467 this.xhr.send(formData);
32471 this.uploadCancel();
32474 uploadCancel : function()
32480 this.delegates = [];
32482 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32489 renderPreview : function(file)
32491 if(typeof(file.target) != 'undefined' && file.target){
32495 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32497 var previewEl = this.managerEl.createChild({
32499 cls : 'roo-document-manager-preview',
32503 tooltip : file[this.toolTipName],
32504 cls : 'roo-document-manager-thumb',
32505 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32510 html : '<i class="fa fa-times-circle"></i>'
32515 var close = previewEl.select('button.close', true).first();
32517 close.on('click', this.onRemove, this, file);
32519 file.target = previewEl;
32521 var image = previewEl.select('img', true).first();
32525 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32527 image.on('click', this.onClick, this, file);
32529 this.fireEvent('previewrendered', this, file);
32535 onPreviewLoad : function(file, image)
32537 if(typeof(file.target) == 'undefined' || !file.target){
32541 var width = image.dom.naturalWidth || image.dom.width;
32542 var height = image.dom.naturalHeight || image.dom.height;
32544 if(!this.previewResize) {
32548 if(width > height){
32549 file.target.addClass('wide');
32553 file.target.addClass('tall');
32558 uploadFromSource : function(file, crop)
32560 this.xhr = new XMLHttpRequest();
32562 this.managerEl.createChild({
32564 cls : 'roo-document-manager-loading',
32568 tooltip : file.name,
32569 cls : 'roo-document-manager-thumb',
32570 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32576 this.xhr.open(this.method, this.url, true);
32579 "Accept": "application/json",
32580 "Cache-Control": "no-cache",
32581 "X-Requested-With": "XMLHttpRequest"
32584 for (var headerName in headers) {
32585 var headerValue = headers[headerName];
32587 this.xhr.setRequestHeader(headerName, headerValue);
32593 this.xhr.onload = function()
32595 _this.xhrOnLoad(_this.xhr);
32598 this.xhr.onerror = function()
32600 _this.xhrOnError(_this.xhr);
32603 var formData = new FormData();
32605 formData.append('returnHTML', 'NO');
32607 formData.append('crop', crop);
32609 if(typeof(file.filename) != 'undefined'){
32610 formData.append('filename', file.filename);
32613 if(typeof(file.mimetype) != 'undefined'){
32614 formData.append('mimetype', file.mimetype);
32619 if(this.fireEvent('prepare', this, formData) != false){
32620 this.xhr.send(formData);
32630 * @class Roo.bootstrap.DocumentViewer
32631 * @extends Roo.bootstrap.Component
32632 * Bootstrap DocumentViewer class
32633 * @cfg {Boolean} showDownload (true|false) show download button (default true)
32634 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32637 * Create a new DocumentViewer
32638 * @param {Object} config The config object
32641 Roo.bootstrap.DocumentViewer = function(config){
32642 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32647 * Fire after initEvent
32648 * @param {Roo.bootstrap.DocumentViewer} this
32654 * @param {Roo.bootstrap.DocumentViewer} this
32659 * Fire after download button
32660 * @param {Roo.bootstrap.DocumentViewer} this
32665 * Fire after trash button
32666 * @param {Roo.bootstrap.DocumentViewer} this
32673 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
32675 showDownload : true,
32679 getAutoCreate : function()
32683 cls : 'roo-document-viewer',
32687 cls : 'roo-document-viewer-body',
32691 cls : 'roo-document-viewer-thumb',
32695 cls : 'roo-document-viewer-image'
32703 cls : 'roo-document-viewer-footer',
32706 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32710 cls : 'btn-group roo-document-viewer-download',
32714 cls : 'btn btn-default',
32715 html : '<i class="fa fa-download"></i>'
32721 cls : 'btn-group roo-document-viewer-trash',
32725 cls : 'btn btn-default',
32726 html : '<i class="fa fa-trash"></i>'
32739 initEvents : function()
32741 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32742 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32744 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32745 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32747 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32748 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32750 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32751 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32753 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32754 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32756 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32757 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32759 this.bodyEl.on('click', this.onClick, this);
32760 this.downloadBtn.on('click', this.onDownload, this);
32761 this.trashBtn.on('click', this.onTrash, this);
32763 this.downloadBtn.hide();
32764 this.trashBtn.hide();
32766 if(this.showDownload){
32767 this.downloadBtn.show();
32770 if(this.showTrash){
32771 this.trashBtn.show();
32774 if(!this.showDownload && !this.showTrash) {
32775 this.footerEl.hide();
32780 initial : function()
32782 this.fireEvent('initial', this);
32786 onClick : function(e)
32788 e.preventDefault();
32790 this.fireEvent('click', this);
32793 onDownload : function(e)
32795 e.preventDefault();
32797 this.fireEvent('download', this);
32800 onTrash : function(e)
32802 e.preventDefault();
32804 this.fireEvent('trash', this);
32816 * @class Roo.bootstrap.NavProgressBar
32817 * @extends Roo.bootstrap.Component
32818 * Bootstrap NavProgressBar class
32821 * Create a new nav progress bar
32822 * @param {Object} config The config object
32825 Roo.bootstrap.NavProgressBar = function(config){
32826 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32828 this.bullets = this.bullets || [];
32830 // Roo.bootstrap.NavProgressBar.register(this);
32834 * Fires when the active item changes
32835 * @param {Roo.bootstrap.NavProgressBar} this
32836 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32837 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
32844 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
32849 getAutoCreate : function()
32851 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32855 cls : 'roo-navigation-bar-group',
32859 cls : 'roo-navigation-top-bar'
32863 cls : 'roo-navigation-bullets-bar',
32867 cls : 'roo-navigation-bar'
32874 cls : 'roo-navigation-bottom-bar'
32884 initEvents: function()
32889 onRender : function(ct, position)
32891 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32893 if(this.bullets.length){
32894 Roo.each(this.bullets, function(b){
32903 addItem : function(cfg)
32905 var item = new Roo.bootstrap.NavProgressItem(cfg);
32907 item.parentId = this.id;
32908 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32911 var top = new Roo.bootstrap.Element({
32913 cls : 'roo-navigation-bar-text'
32916 var bottom = new Roo.bootstrap.Element({
32918 cls : 'roo-navigation-bar-text'
32921 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32922 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32924 var topText = new Roo.bootstrap.Element({
32926 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32929 var bottomText = new Roo.bootstrap.Element({
32931 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32934 topText.onRender(top.el, null);
32935 bottomText.onRender(bottom.el, null);
32938 item.bottomEl = bottom;
32941 this.barItems.push(item);
32946 getActive : function()
32948 var active = false;
32950 Roo.each(this.barItems, function(v){
32952 if (!v.isActive()) {
32964 setActiveItem : function(item)
32968 Roo.each(this.barItems, function(v){
32969 if (v.rid == item.rid) {
32973 if (v.isActive()) {
32974 v.setActive(false);
32979 item.setActive(true);
32981 this.fireEvent('changed', this, item, prev);
32984 getBarItem: function(rid)
32988 Roo.each(this.barItems, function(e) {
32989 if (e.rid != rid) {
33000 indexOfItem : function(item)
33004 Roo.each(this.barItems, function(v, i){
33006 if (v.rid != item.rid) {
33017 setActiveNext : function()
33019 var i = this.indexOfItem(this.getActive());
33021 if (i > this.barItems.length) {
33025 this.setActiveItem(this.barItems[i+1]);
33028 setActivePrev : function()
33030 var i = this.indexOfItem(this.getActive());
33036 this.setActiveItem(this.barItems[i-1]);
33039 format : function()
33041 if(!this.barItems.length){
33045 var width = 100 / this.barItems.length;
33047 Roo.each(this.barItems, function(i){
33048 i.el.setStyle('width', width + '%');
33049 i.topEl.el.setStyle('width', width + '%');
33050 i.bottomEl.el.setStyle('width', width + '%');
33059 * Nav Progress Item
33064 * @class Roo.bootstrap.NavProgressItem
33065 * @extends Roo.bootstrap.Component
33066 * Bootstrap NavProgressItem class
33067 * @cfg {String} rid the reference id
33068 * @cfg {Boolean} active (true|false) Is item active default false
33069 * @cfg {Boolean} disabled (true|false) Is item active default false
33070 * @cfg {String} html
33071 * @cfg {String} position (top|bottom) text position default bottom
33072 * @cfg {String} icon show icon instead of number
33075 * Create a new NavProgressItem
33076 * @param {Object} config The config object
33078 Roo.bootstrap.NavProgressItem = function(config){
33079 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33084 * The raw click event for the entire grid.
33085 * @param {Roo.bootstrap.NavProgressItem} this
33086 * @param {Roo.EventObject} e
33093 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
33099 position : 'bottom',
33102 getAutoCreate : function()
33104 var iconCls = 'roo-navigation-bar-item-icon';
33106 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33110 cls: 'roo-navigation-bar-item',
33120 cfg.cls += ' active';
33123 cfg.cls += ' disabled';
33129 disable : function()
33131 this.setDisabled(true);
33134 enable : function()
33136 this.setDisabled(false);
33139 initEvents: function()
33141 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33143 this.iconEl.on('click', this.onClick, this);
33146 onClick : function(e)
33148 e.preventDefault();
33154 if(this.fireEvent('click', this, e) === false){
33158 this.parent().setActiveItem(this);
33161 isActive: function ()
33163 return this.active;
33166 setActive : function(state)
33168 if(this.active == state){
33172 this.active = state;
33175 this.el.addClass('active');
33179 this.el.removeClass('active');
33184 setDisabled : function(state)
33186 if(this.disabled == state){
33190 this.disabled = state;
33193 this.el.addClass('disabled');
33197 this.el.removeClass('disabled');
33200 tooltipEl : function()
33202 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33215 * @class Roo.bootstrap.FieldLabel
33216 * @extends Roo.bootstrap.Component
33217 * Bootstrap FieldLabel class
33218 * @cfg {String} html contents of the element
33219 * @cfg {String} tag tag of the element default label
33220 * @cfg {String} cls class of the element
33221 * @cfg {String} target label target
33222 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33223 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33224 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33225 * @cfg {String} iconTooltip default "This field is required"
33226 * @cfg {String} indicatorpos (left|right) default left
33229 * Create a new FieldLabel
33230 * @param {Object} config The config object
33233 Roo.bootstrap.FieldLabel = function(config){
33234 Roo.bootstrap.Element.superclass.constructor.call(this, config);
33239 * Fires after the field has been marked as invalid.
33240 * @param {Roo.form.FieldLabel} this
33241 * @param {String} msg The validation message
33246 * Fires after the field has been validated with no errors.
33247 * @param {Roo.form.FieldLabel} this
33253 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
33260 invalidClass : 'has-warning',
33261 validClass : 'has-success',
33262 iconTooltip : 'This field is required',
33263 indicatorpos : 'left',
33265 getAutoCreate : function(){
33268 if (!this.allowBlank) {
33274 cls : 'roo-bootstrap-field-label ' + this.cls,
33279 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33280 tooltip : this.iconTooltip
33289 if(this.indicatorpos == 'right'){
33292 cls : 'roo-bootstrap-field-label ' + this.cls,
33301 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33302 tooltip : this.iconTooltip
33311 initEvents: function()
33313 Roo.bootstrap.Element.superclass.initEvents.call(this);
33315 this.indicator = this.indicatorEl();
33317 if(this.indicator){
33318 this.indicator.removeClass('visible');
33319 this.indicator.addClass('invisible');
33322 Roo.bootstrap.FieldLabel.register(this);
33325 indicatorEl : function()
33327 var indicator = this.el.select('i.roo-required-indicator',true).first();
33338 * Mark this field as valid
33340 markValid : function()
33342 if(this.indicator){
33343 this.indicator.removeClass('visible');
33344 this.indicator.addClass('invisible');
33346 if (Roo.bootstrap.version == 3) {
33347 this.el.removeClass(this.invalidClass);
33348 this.el.addClass(this.validClass);
33350 this.el.removeClass('is-invalid');
33351 this.el.addClass('is-valid');
33355 this.fireEvent('valid', this);
33359 * Mark this field as invalid
33360 * @param {String} msg The validation message
33362 markInvalid : function(msg)
33364 if(this.indicator){
33365 this.indicator.removeClass('invisible');
33366 this.indicator.addClass('visible');
33368 if (Roo.bootstrap.version == 3) {
33369 this.el.removeClass(this.validClass);
33370 this.el.addClass(this.invalidClass);
33372 this.el.removeClass('is-valid');
33373 this.el.addClass('is-invalid');
33377 this.fireEvent('invalid', this, msg);
33383 Roo.apply(Roo.bootstrap.FieldLabel, {
33388 * register a FieldLabel Group
33389 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33391 register : function(label)
33393 if(this.groups.hasOwnProperty(label.target)){
33397 this.groups[label.target] = label;
33401 * fetch a FieldLabel Group based on the target
33402 * @param {string} target
33403 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33405 get: function(target) {
33406 if (typeof(this.groups[target]) == 'undefined') {
33410 return this.groups[target] ;
33419 * page DateSplitField.
33425 * @class Roo.bootstrap.DateSplitField
33426 * @extends Roo.bootstrap.Component
33427 * Bootstrap DateSplitField class
33428 * @cfg {string} fieldLabel - the label associated
33429 * @cfg {Number} labelWidth set the width of label (0-12)
33430 * @cfg {String} labelAlign (top|left)
33431 * @cfg {Boolean} dayAllowBlank (true|false) default false
33432 * @cfg {Boolean} monthAllowBlank (true|false) default false
33433 * @cfg {Boolean} yearAllowBlank (true|false) default false
33434 * @cfg {string} dayPlaceholder
33435 * @cfg {string} monthPlaceholder
33436 * @cfg {string} yearPlaceholder
33437 * @cfg {string} dayFormat default 'd'
33438 * @cfg {string} monthFormat default 'm'
33439 * @cfg {string} yearFormat default 'Y'
33440 * @cfg {Number} labellg set the width of label (1-12)
33441 * @cfg {Number} labelmd set the width of label (1-12)
33442 * @cfg {Number} labelsm set the width of label (1-12)
33443 * @cfg {Number} labelxs set the width of label (1-12)
33447 * Create a new DateSplitField
33448 * @param {Object} config The config object
33451 Roo.bootstrap.DateSplitField = function(config){
33452 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33458 * getting the data of years
33459 * @param {Roo.bootstrap.DateSplitField} this
33460 * @param {Object} years
33465 * getting the data of days
33466 * @param {Roo.bootstrap.DateSplitField} this
33467 * @param {Object} days
33472 * Fires after the field has been marked as invalid.
33473 * @param {Roo.form.Field} this
33474 * @param {String} msg The validation message
33479 * Fires after the field has been validated with no errors.
33480 * @param {Roo.form.Field} this
33486 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33489 labelAlign : 'top',
33491 dayAllowBlank : false,
33492 monthAllowBlank : false,
33493 yearAllowBlank : false,
33494 dayPlaceholder : '',
33495 monthPlaceholder : '',
33496 yearPlaceholder : '',
33500 isFormField : true,
33506 getAutoCreate : function()
33510 cls : 'row roo-date-split-field-group',
33515 cls : 'form-hidden-field roo-date-split-field-group-value',
33521 var labelCls = 'col-md-12';
33522 var contentCls = 'col-md-4';
33524 if(this.fieldLabel){
33528 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33532 html : this.fieldLabel
33537 if(this.labelAlign == 'left'){
33539 if(this.labelWidth > 12){
33540 label.style = "width: " + this.labelWidth + 'px';
33543 if(this.labelWidth < 13 && this.labelmd == 0){
33544 this.labelmd = this.labelWidth;
33547 if(this.labellg > 0){
33548 labelCls = ' col-lg-' + this.labellg;
33549 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33552 if(this.labelmd > 0){
33553 labelCls = ' col-md-' + this.labelmd;
33554 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33557 if(this.labelsm > 0){
33558 labelCls = ' col-sm-' + this.labelsm;
33559 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33562 if(this.labelxs > 0){
33563 labelCls = ' col-xs-' + this.labelxs;
33564 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33568 label.cls += ' ' + labelCls;
33570 cfg.cn.push(label);
33573 Roo.each(['day', 'month', 'year'], function(t){
33576 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33583 inputEl: function ()
33585 return this.el.select('.roo-date-split-field-group-value', true).first();
33588 onRender : function(ct, position)
33592 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33594 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33596 this.dayField = new Roo.bootstrap.ComboBox({
33597 allowBlank : this.dayAllowBlank,
33598 alwaysQuery : true,
33599 displayField : 'value',
33602 forceSelection : true,
33604 placeholder : this.dayPlaceholder,
33605 selectOnFocus : true,
33606 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33607 triggerAction : 'all',
33609 valueField : 'value',
33610 store : new Roo.data.SimpleStore({
33611 data : (function() {
33613 _this.fireEvent('days', _this, days);
33616 fields : [ 'value' ]
33619 select : function (_self, record, index)
33621 _this.setValue(_this.getValue());
33626 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33628 this.monthField = new Roo.bootstrap.MonthField({
33629 after : '<i class=\"fa fa-calendar\"></i>',
33630 allowBlank : this.monthAllowBlank,
33631 placeholder : this.monthPlaceholder,
33634 render : function (_self)
33636 this.el.select('span.input-group-addon', true).first().on('click', function(e){
33637 e.preventDefault();
33641 select : function (_self, oldvalue, newvalue)
33643 _this.setValue(_this.getValue());
33648 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33650 this.yearField = new Roo.bootstrap.ComboBox({
33651 allowBlank : this.yearAllowBlank,
33652 alwaysQuery : true,
33653 displayField : 'value',
33656 forceSelection : true,
33658 placeholder : this.yearPlaceholder,
33659 selectOnFocus : true,
33660 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33661 triggerAction : 'all',
33663 valueField : 'value',
33664 store : new Roo.data.SimpleStore({
33665 data : (function() {
33667 _this.fireEvent('years', _this, years);
33670 fields : [ 'value' ]
33673 select : function (_self, record, index)
33675 _this.setValue(_this.getValue());
33680 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33683 setValue : function(v, format)
33685 this.inputEl.dom.value = v;
33687 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33689 var d = Date.parseDate(v, f);
33696 this.setDay(d.format(this.dayFormat));
33697 this.setMonth(d.format(this.monthFormat));
33698 this.setYear(d.format(this.yearFormat));
33705 setDay : function(v)
33707 this.dayField.setValue(v);
33708 this.inputEl.dom.value = this.getValue();
33713 setMonth : function(v)
33715 this.monthField.setValue(v, true);
33716 this.inputEl.dom.value = this.getValue();
33721 setYear : function(v)
33723 this.yearField.setValue(v);
33724 this.inputEl.dom.value = this.getValue();
33729 getDay : function()
33731 return this.dayField.getValue();
33734 getMonth : function()
33736 return this.monthField.getValue();
33739 getYear : function()
33741 return this.yearField.getValue();
33744 getValue : function()
33746 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33748 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33758 this.inputEl.dom.value = '';
33763 validate : function()
33765 var d = this.dayField.validate();
33766 var m = this.monthField.validate();
33767 var y = this.yearField.validate();
33772 (!this.dayAllowBlank && !d) ||
33773 (!this.monthAllowBlank && !m) ||
33774 (!this.yearAllowBlank && !y)
33779 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33788 this.markInvalid();
33793 markValid : function()
33796 var label = this.el.select('label', true).first();
33797 var icon = this.el.select('i.fa-star', true).first();
33803 this.fireEvent('valid', this);
33807 * Mark this field as invalid
33808 * @param {String} msg The validation message
33810 markInvalid : function(msg)
33813 var label = this.el.select('label', true).first();
33814 var icon = this.el.select('i.fa-star', true).first();
33816 if(label && !icon){
33817 this.el.select('.roo-date-split-field-label', true).createChild({
33819 cls : 'text-danger fa fa-lg fa-star',
33820 tooltip : 'This field is required',
33821 style : 'margin-right:5px;'
33825 this.fireEvent('invalid', this, msg);
33828 clearInvalid : function()
33830 var label = this.el.select('label', true).first();
33831 var icon = this.el.select('i.fa-star', true).first();
33837 this.fireEvent('valid', this);
33840 getName: function()
33850 * http://masonry.desandro.com
33852 * The idea is to render all the bricks based on vertical width...
33854 * The original code extends 'outlayer' - we might need to use that....
33860 * @class Roo.bootstrap.LayoutMasonry
33861 * @extends Roo.bootstrap.Component
33862 * Bootstrap Layout Masonry class
33865 * Create a new Element
33866 * @param {Object} config The config object
33869 Roo.bootstrap.LayoutMasonry = function(config){
33871 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33875 Roo.bootstrap.LayoutMasonry.register(this);
33881 * Fire after layout the items
33882 * @param {Roo.bootstrap.LayoutMasonry} this
33883 * @param {Roo.EventObject} e
33890 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
33893 * @cfg {Boolean} isLayoutInstant = no animation?
33895 isLayoutInstant : false, // needed?
33898 * @cfg {Number} boxWidth width of the columns
33903 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
33908 * @cfg {Number} padWidth padding below box..
33913 * @cfg {Number} gutter gutter width..
33918 * @cfg {Number} maxCols maximum number of columns
33924 * @cfg {Boolean} isAutoInitial defalut true
33926 isAutoInitial : true,
33931 * @cfg {Boolean} isHorizontal defalut false
33933 isHorizontal : false,
33935 currentSize : null,
33941 bricks: null, //CompositeElement
33945 _isLayoutInited : false,
33947 // isAlternative : false, // only use for vertical layout...
33950 * @cfg {Number} alternativePadWidth padding below box..
33952 alternativePadWidth : 50,
33954 selectedBrick : [],
33956 getAutoCreate : function(){
33958 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33962 cls: 'blog-masonary-wrapper ' + this.cls,
33964 cls : 'mas-boxes masonary'
33971 getChildContainer: function( )
33973 if (this.boxesEl) {
33974 return this.boxesEl;
33977 this.boxesEl = this.el.select('.mas-boxes').first();
33979 return this.boxesEl;
33983 initEvents : function()
33987 if(this.isAutoInitial){
33988 Roo.log('hook children rendered');
33989 this.on('childrenrendered', function() {
33990 Roo.log('children rendered');
33996 initial : function()
33998 this.selectedBrick = [];
34000 this.currentSize = this.el.getBox(true);
34002 Roo.EventManager.onWindowResize(this.resize, this);
34004 if(!this.isAutoInitial){
34012 //this.layout.defer(500,this);
34016 resize : function()
34018 var cs = this.el.getBox(true);
34021 this.currentSize.width == cs.width &&
34022 this.currentSize.x == cs.x &&
34023 this.currentSize.height == cs.height &&
34024 this.currentSize.y == cs.y
34026 Roo.log("no change in with or X or Y");
34030 this.currentSize = cs;
34036 layout : function()
34038 this._resetLayout();
34040 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34042 this.layoutItems( isInstant );
34044 this._isLayoutInited = true;
34046 this.fireEvent('layout', this);
34050 _resetLayout : function()
34052 if(this.isHorizontal){
34053 this.horizontalMeasureColumns();
34057 this.verticalMeasureColumns();
34061 verticalMeasureColumns : function()
34063 this.getContainerWidth();
34065 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34066 // this.colWidth = Math.floor(this.containerWidth * 0.8);
34070 var boxWidth = this.boxWidth + this.padWidth;
34072 if(this.containerWidth < this.boxWidth){
34073 boxWidth = this.containerWidth
34076 var containerWidth = this.containerWidth;
34078 var cols = Math.floor(containerWidth / boxWidth);
34080 this.cols = Math.max( cols, 1 );
34082 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34084 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34086 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34088 this.colWidth = boxWidth + avail - this.padWidth;
34090 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34091 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
34094 horizontalMeasureColumns : function()
34096 this.getContainerWidth();
34098 var boxWidth = this.boxWidth;
34100 if(this.containerWidth < boxWidth){
34101 boxWidth = this.containerWidth;
34104 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34106 this.el.setHeight(boxWidth);
34110 getContainerWidth : function()
34112 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34115 layoutItems : function( isInstant )
34117 Roo.log(this.bricks);
34119 var items = Roo.apply([], this.bricks);
34121 if(this.isHorizontal){
34122 this._horizontalLayoutItems( items , isInstant );
34126 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34127 // this._verticalAlternativeLayoutItems( items , isInstant );
34131 this._verticalLayoutItems( items , isInstant );
34135 _verticalLayoutItems : function ( items , isInstant)
34137 if ( !items || !items.length ) {
34142 ['xs', 'xs', 'xs', 'tall'],
34143 ['xs', 'xs', 'tall'],
34144 ['xs', 'xs', 'sm'],
34145 ['xs', 'xs', 'xs'],
34151 ['sm', 'xs', 'xs'],
34155 ['tall', 'xs', 'xs', 'xs'],
34156 ['tall', 'xs', 'xs'],
34168 Roo.each(items, function(item, k){
34170 switch (item.size) {
34171 // these layouts take up a full box,
34182 boxes.push([item]);
34205 var filterPattern = function(box, length)
34213 var pattern = box.slice(0, length);
34217 Roo.each(pattern, function(i){
34218 format.push(i.size);
34221 Roo.each(standard, function(s){
34223 if(String(s) != String(format)){
34232 if(!match && length == 1){
34237 filterPattern(box, length - 1);
34241 queue.push(pattern);
34243 box = box.slice(length, box.length);
34245 filterPattern(box, 4);
34251 Roo.each(boxes, function(box, k){
34257 if(box.length == 1){
34262 filterPattern(box, 4);
34266 this._processVerticalLayoutQueue( queue, isInstant );
34270 // _verticalAlternativeLayoutItems : function( items , isInstant )
34272 // if ( !items || !items.length ) {
34276 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
34280 _horizontalLayoutItems : function ( items , isInstant)
34282 if ( !items || !items.length || items.length < 3) {
34288 var eItems = items.slice(0, 3);
34290 items = items.slice(3, items.length);
34293 ['xs', 'xs', 'xs', 'wide'],
34294 ['xs', 'xs', 'wide'],
34295 ['xs', 'xs', 'sm'],
34296 ['xs', 'xs', 'xs'],
34302 ['sm', 'xs', 'xs'],
34306 ['wide', 'xs', 'xs', 'xs'],
34307 ['wide', 'xs', 'xs'],
34320 Roo.each(items, function(item, k){
34322 switch (item.size) {
34333 boxes.push([item]);
34357 var filterPattern = function(box, length)
34365 var pattern = box.slice(0, length);
34369 Roo.each(pattern, function(i){
34370 format.push(i.size);
34373 Roo.each(standard, function(s){
34375 if(String(s) != String(format)){
34384 if(!match && length == 1){
34389 filterPattern(box, length - 1);
34393 queue.push(pattern);
34395 box = box.slice(length, box.length);
34397 filterPattern(box, 4);
34403 Roo.each(boxes, function(box, k){
34409 if(box.length == 1){
34414 filterPattern(box, 4);
34421 var pos = this.el.getBox(true);
34425 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34427 var hit_end = false;
34429 Roo.each(queue, function(box){
34433 Roo.each(box, function(b){
34435 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34445 Roo.each(box, function(b){
34447 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34450 mx = Math.max(mx, b.x);
34454 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34458 Roo.each(box, function(b){
34460 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34474 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34477 /** Sets position of item in DOM
34478 * @param {Element} item
34479 * @param {Number} x - horizontal position
34480 * @param {Number} y - vertical position
34481 * @param {Boolean} isInstant - disables transitions
34483 _processVerticalLayoutQueue : function( queue, isInstant )
34485 var pos = this.el.getBox(true);
34490 for (var i = 0; i < this.cols; i++){
34494 Roo.each(queue, function(box, k){
34496 var col = k % this.cols;
34498 Roo.each(box, function(b,kk){
34500 b.el.position('absolute');
34502 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34503 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34505 if(b.size == 'md-left' || b.size == 'md-right'){
34506 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34507 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34510 b.el.setWidth(width);
34511 b.el.setHeight(height);
34513 b.el.select('iframe',true).setSize(width,height);
34517 for (var i = 0; i < this.cols; i++){
34519 if(maxY[i] < maxY[col]){
34524 col = Math.min(col, i);
34528 x = pos.x + col * (this.colWidth + this.padWidth);
34532 var positions = [];
34534 switch (box.length){
34536 positions = this.getVerticalOneBoxColPositions(x, y, box);
34539 positions = this.getVerticalTwoBoxColPositions(x, y, box);
34542 positions = this.getVerticalThreeBoxColPositions(x, y, box);
34545 positions = this.getVerticalFourBoxColPositions(x, y, box);
34551 Roo.each(box, function(b,kk){
34553 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34555 var sz = b.el.getSize();
34557 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34565 for (var i = 0; i < this.cols; i++){
34566 mY = Math.max(mY, maxY[i]);
34569 this.el.setHeight(mY - pos.y);
34573 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34575 // var pos = this.el.getBox(true);
34578 // var maxX = pos.right;
34580 // var maxHeight = 0;
34582 // Roo.each(items, function(item, k){
34586 // item.el.position('absolute');
34588 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34590 // item.el.setWidth(width);
34592 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34594 // item.el.setHeight(height);
34597 // item.el.setXY([x, y], isInstant ? false : true);
34599 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34602 // y = y + height + this.alternativePadWidth;
34604 // maxHeight = maxHeight + height + this.alternativePadWidth;
34608 // this.el.setHeight(maxHeight);
34612 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34614 var pos = this.el.getBox(true);
34619 var maxX = pos.right;
34621 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34623 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34625 Roo.each(queue, function(box, k){
34627 Roo.each(box, function(b, kk){
34629 b.el.position('absolute');
34631 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34632 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34634 if(b.size == 'md-left' || b.size == 'md-right'){
34635 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34636 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34639 b.el.setWidth(width);
34640 b.el.setHeight(height);
34648 var positions = [];
34650 switch (box.length){
34652 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34655 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34658 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34661 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34667 Roo.each(box, function(b,kk){
34669 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34671 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34679 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34681 Roo.each(eItems, function(b,k){
34683 b.size = (k == 0) ? 'sm' : 'xs';
34684 b.x = (k == 0) ? 2 : 1;
34685 b.y = (k == 0) ? 2 : 1;
34687 b.el.position('absolute');
34689 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34691 b.el.setWidth(width);
34693 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34695 b.el.setHeight(height);
34699 var positions = [];
34702 x : maxX - this.unitWidth * 2 - this.gutter,
34707 x : maxX - this.unitWidth,
34708 y : minY + (this.unitWidth + this.gutter) * 2
34712 x : maxX - this.unitWidth * 3 - this.gutter * 2,
34716 Roo.each(eItems, function(b,k){
34718 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34724 getVerticalOneBoxColPositions : function(x, y, box)
34728 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34730 if(box[0].size == 'md-left'){
34734 if(box[0].size == 'md-right'){
34739 x : x + (this.unitWidth + this.gutter) * rand,
34746 getVerticalTwoBoxColPositions : function(x, y, box)
34750 if(box[0].size == 'xs'){
34754 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34758 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34772 x : x + (this.unitWidth + this.gutter) * 2,
34773 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34780 getVerticalThreeBoxColPositions : function(x, y, box)
34784 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34792 x : x + (this.unitWidth + this.gutter) * 1,
34797 x : x + (this.unitWidth + this.gutter) * 2,
34805 if(box[0].size == 'xs' && box[1].size == 'xs'){
34814 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34818 x : x + (this.unitWidth + this.gutter) * 1,
34832 x : x + (this.unitWidth + this.gutter) * 2,
34837 x : x + (this.unitWidth + this.gutter) * 2,
34838 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34845 getVerticalFourBoxColPositions : function(x, y, box)
34849 if(box[0].size == 'xs'){
34858 y : y + (this.unitHeight + this.gutter) * 1
34863 y : y + (this.unitHeight + this.gutter) * 2
34867 x : x + (this.unitWidth + this.gutter) * 1,
34881 x : x + (this.unitWidth + this.gutter) * 2,
34886 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34887 y : y + (this.unitHeight + this.gutter) * 1
34891 x : x + (this.unitWidth + this.gutter) * 2,
34892 y : y + (this.unitWidth + this.gutter) * 2
34899 getHorizontalOneBoxColPositions : function(maxX, minY, box)
34903 if(box[0].size == 'md-left'){
34905 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34912 if(box[0].size == 'md-right'){
34914 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34915 y : minY + (this.unitWidth + this.gutter) * 1
34921 var rand = Math.floor(Math.random() * (4 - box[0].y));
34924 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34925 y : minY + (this.unitWidth + this.gutter) * rand
34932 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34936 if(box[0].size == 'xs'){
34939 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34944 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34945 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34953 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34958 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34959 y : minY + (this.unitWidth + this.gutter) * 2
34966 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34970 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34973 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34978 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34979 y : minY + (this.unitWidth + this.gutter) * 1
34983 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34984 y : minY + (this.unitWidth + this.gutter) * 2
34991 if(box[0].size == 'xs' && box[1].size == 'xs'){
34994 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34999 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35004 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35005 y : minY + (this.unitWidth + this.gutter) * 1
35013 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35018 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35019 y : minY + (this.unitWidth + this.gutter) * 2
35023 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35024 y : minY + (this.unitWidth + this.gutter) * 2
35031 getHorizontalFourBoxColPositions : function(maxX, minY, box)
35035 if(box[0].size == 'xs'){
35038 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35043 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35048 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),
35053 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35054 y : minY + (this.unitWidth + this.gutter) * 1
35062 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35067 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35068 y : minY + (this.unitWidth + this.gutter) * 2
35072 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35073 y : minY + (this.unitWidth + this.gutter) * 2
35077 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),
35078 y : minY + (this.unitWidth + this.gutter) * 2
35086 * remove a Masonry Brick
35087 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35089 removeBrick : function(brick_id)
35095 for (var i = 0; i<this.bricks.length; i++) {
35096 if (this.bricks[i].id == brick_id) {
35097 this.bricks.splice(i,1);
35098 this.el.dom.removeChild(Roo.get(brick_id).dom);
35105 * adds a Masonry Brick
35106 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35108 addBrick : function(cfg)
35110 var cn = new Roo.bootstrap.MasonryBrick(cfg);
35111 //this.register(cn);
35112 cn.parentId = this.id;
35113 cn.render(this.el);
35118 * register a Masonry Brick
35119 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35122 register : function(brick)
35124 this.bricks.push(brick);
35125 brick.masonryId = this.id;
35129 * clear all the Masonry Brick
35131 clearAll : function()
35134 //this.getChildContainer().dom.innerHTML = "";
35135 this.el.dom.innerHTML = '';
35138 getSelected : function()
35140 if (!this.selectedBrick) {
35144 return this.selectedBrick;
35148 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35152 * register a Masonry Layout
35153 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35156 register : function(layout)
35158 this.groups[layout.id] = layout;
35161 * fetch a Masonry Layout based on the masonry layout ID
35162 * @param {string} the masonry layout to add
35163 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35166 get: function(layout_id) {
35167 if (typeof(this.groups[layout_id]) == 'undefined') {
35170 return this.groups[layout_id] ;
35182 * http://masonry.desandro.com
35184 * The idea is to render all the bricks based on vertical width...
35186 * The original code extends 'outlayer' - we might need to use that....
35192 * @class Roo.bootstrap.LayoutMasonryAuto
35193 * @extends Roo.bootstrap.Component
35194 * Bootstrap Layout Masonry class
35197 * Create a new Element
35198 * @param {Object} config The config object
35201 Roo.bootstrap.LayoutMasonryAuto = function(config){
35202 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35205 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
35208 * @cfg {Boolean} isFitWidth - resize the width..
35210 isFitWidth : false, // options..
35212 * @cfg {Boolean} isOriginLeft = left align?
35214 isOriginLeft : true,
35216 * @cfg {Boolean} isOriginTop = top align?
35218 isOriginTop : false,
35220 * @cfg {Boolean} isLayoutInstant = no animation?
35222 isLayoutInstant : false, // needed?
35224 * @cfg {Boolean} isResizingContainer = not sure if this is used..
35226 isResizingContainer : true,
35228 * @cfg {Number} columnWidth width of the columns
35234 * @cfg {Number} maxCols maximum number of columns
35239 * @cfg {Number} padHeight padding below box..
35245 * @cfg {Boolean} isAutoInitial defalut true
35248 isAutoInitial : true,
35254 initialColumnWidth : 0,
35255 currentSize : null,
35257 colYs : null, // array.
35264 bricks: null, //CompositeElement
35265 cols : 0, // array?
35266 // element : null, // wrapped now this.el
35267 _isLayoutInited : null,
35270 getAutoCreate : function(){
35274 cls: 'blog-masonary-wrapper ' + this.cls,
35276 cls : 'mas-boxes masonary'
35283 getChildContainer: function( )
35285 if (this.boxesEl) {
35286 return this.boxesEl;
35289 this.boxesEl = this.el.select('.mas-boxes').first();
35291 return this.boxesEl;
35295 initEvents : function()
35299 if(this.isAutoInitial){
35300 Roo.log('hook children rendered');
35301 this.on('childrenrendered', function() {
35302 Roo.log('children rendered');
35309 initial : function()
35311 this.reloadItems();
35313 this.currentSize = this.el.getBox(true);
35315 /// was window resize... - let's see if this works..
35316 Roo.EventManager.onWindowResize(this.resize, this);
35318 if(!this.isAutoInitial){
35323 this.layout.defer(500,this);
35326 reloadItems: function()
35328 this.bricks = this.el.select('.masonry-brick', true);
35330 this.bricks.each(function(b) {
35331 //Roo.log(b.getSize());
35332 if (!b.attr('originalwidth')) {
35333 b.attr('originalwidth', b.getSize().width);
35338 Roo.log(this.bricks.elements.length);
35341 resize : function()
35344 var cs = this.el.getBox(true);
35346 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35347 Roo.log("no change in with or X");
35350 this.currentSize = cs;
35354 layout : function()
35357 this._resetLayout();
35358 //this._manageStamps();
35360 // don't animate first layout
35361 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35362 this.layoutItems( isInstant );
35364 // flag for initalized
35365 this._isLayoutInited = true;
35368 layoutItems : function( isInstant )
35370 //var items = this._getItemsForLayout( this.items );
35371 // original code supports filtering layout items.. we just ignore it..
35373 this._layoutItems( this.bricks , isInstant );
35375 this._postLayout();
35377 _layoutItems : function ( items , isInstant)
35379 //this.fireEvent( 'layout', this, items );
35382 if ( !items || !items.elements.length ) {
35383 // no items, emit event with empty array
35388 items.each(function(item) {
35389 Roo.log("layout item");
35391 // get x/y object from method
35392 var position = this._getItemLayoutPosition( item );
35394 position.item = item;
35395 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35396 queue.push( position );
35399 this._processLayoutQueue( queue );
35401 /** Sets position of item in DOM
35402 * @param {Element} item
35403 * @param {Number} x - horizontal position
35404 * @param {Number} y - vertical position
35405 * @param {Boolean} isInstant - disables transitions
35407 _processLayoutQueue : function( queue )
35409 for ( var i=0, len = queue.length; i < len; i++ ) {
35410 var obj = queue[i];
35411 obj.item.position('absolute');
35412 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35418 * Any logic you want to do after each layout,
35419 * i.e. size the container
35421 _postLayout : function()
35423 this.resizeContainer();
35426 resizeContainer : function()
35428 if ( !this.isResizingContainer ) {
35431 var size = this._getContainerSize();
35433 this.el.setSize(size.width,size.height);
35434 this.boxesEl.setSize(size.width,size.height);
35440 _resetLayout : function()
35442 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35443 this.colWidth = this.el.getWidth();
35444 //this.gutter = this.el.getWidth();
35446 this.measureColumns();
35452 this.colYs.push( 0 );
35458 measureColumns : function()
35460 this.getContainerWidth();
35461 // if columnWidth is 0, default to outerWidth of first item
35462 if ( !this.columnWidth ) {
35463 var firstItem = this.bricks.first();
35464 Roo.log(firstItem);
35465 this.columnWidth = this.containerWidth;
35466 if (firstItem && firstItem.attr('originalwidth') ) {
35467 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35469 // columnWidth fall back to item of first element
35470 Roo.log("set column width?");
35471 this.initialColumnWidth = this.columnWidth ;
35473 // if first elem has no width, default to size of container
35478 if (this.initialColumnWidth) {
35479 this.columnWidth = this.initialColumnWidth;
35484 // column width is fixed at the top - however if container width get's smaller we should
35487 // this bit calcs how man columns..
35489 var columnWidth = this.columnWidth += this.gutter;
35491 // calculate columns
35492 var containerWidth = this.containerWidth + this.gutter;
35494 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35495 // fix rounding errors, typically with gutters
35496 var excess = columnWidth - containerWidth % columnWidth;
35499 // if overshoot is less than a pixel, round up, otherwise floor it
35500 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35501 cols = Math[ mathMethod ]( cols );
35502 this.cols = Math.max( cols, 1 );
35503 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35505 // padding positioning..
35506 var totalColWidth = this.cols * this.columnWidth;
35507 var padavail = this.containerWidth - totalColWidth;
35508 // so for 2 columns - we need 3 'pads'
35510 var padNeeded = (1+this.cols) * this.padWidth;
35512 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35514 this.columnWidth += padExtra
35515 //this.padWidth = Math.floor(padavail / ( this.cols));
35517 // adjust colum width so that padding is fixed??
35519 // we have 3 columns ... total = width * 3
35520 // we have X left over... that should be used by
35522 //if (this.expandC) {
35530 getContainerWidth : function()
35532 /* // container is parent if fit width
35533 var container = this.isFitWidth ? this.element.parentNode : this.element;
35534 // check that this.size and size are there
35535 // IE8 triggers resize on body size change, so they might not be
35537 var size = getSize( container ); //FIXME
35538 this.containerWidth = size && size.innerWidth; //FIXME
35541 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
35545 _getItemLayoutPosition : function( item ) // what is item?
35547 // we resize the item to our columnWidth..
35549 item.setWidth(this.columnWidth);
35550 item.autoBoxAdjust = false;
35552 var sz = item.getSize();
35554 // how many columns does this brick span
35555 var remainder = this.containerWidth % this.columnWidth;
35557 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35558 // round if off by 1 pixel, otherwise use ceil
35559 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
35560 colSpan = Math.min( colSpan, this.cols );
35562 // normally this should be '1' as we dont' currently allow multi width columns..
35564 var colGroup = this._getColGroup( colSpan );
35565 // get the minimum Y value from the columns
35566 var minimumY = Math.min.apply( Math, colGroup );
35567 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35569 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35571 // position the brick
35573 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35574 y: this.currentSize.y + minimumY + this.padHeight
35578 // apply setHeight to necessary columns
35579 var setHeight = minimumY + sz.height + this.padHeight;
35580 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35582 var setSpan = this.cols + 1 - colGroup.length;
35583 for ( var i = 0; i < setSpan; i++ ) {
35584 this.colYs[ shortColIndex + i ] = setHeight ;
35591 * @param {Number} colSpan - number of columns the element spans
35592 * @returns {Array} colGroup
35594 _getColGroup : function( colSpan )
35596 if ( colSpan < 2 ) {
35597 // if brick spans only one column, use all the column Ys
35602 // how many different places could this brick fit horizontally
35603 var groupCount = this.cols + 1 - colSpan;
35604 // for each group potential horizontal position
35605 for ( var i = 0; i < groupCount; i++ ) {
35606 // make an array of colY values for that one group
35607 var groupColYs = this.colYs.slice( i, i + colSpan );
35608 // and get the max value of the array
35609 colGroup[i] = Math.max.apply( Math, groupColYs );
35614 _manageStamp : function( stamp )
35616 var stampSize = stamp.getSize();
35617 var offset = stamp.getBox();
35618 // get the columns that this stamp affects
35619 var firstX = this.isOriginLeft ? offset.x : offset.right;
35620 var lastX = firstX + stampSize.width;
35621 var firstCol = Math.floor( firstX / this.columnWidth );
35622 firstCol = Math.max( 0, firstCol );
35624 var lastCol = Math.floor( lastX / this.columnWidth );
35625 // lastCol should not go over if multiple of columnWidth #425
35626 lastCol -= lastX % this.columnWidth ? 0 : 1;
35627 lastCol = Math.min( this.cols - 1, lastCol );
35629 // set colYs to bottom of the stamp
35630 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35633 for ( var i = firstCol; i <= lastCol; i++ ) {
35634 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35639 _getContainerSize : function()
35641 this.maxY = Math.max.apply( Math, this.colYs );
35646 if ( this.isFitWidth ) {
35647 size.width = this._getContainerFitWidth();
35653 _getContainerFitWidth : function()
35655 var unusedCols = 0;
35656 // count unused columns
35659 if ( this.colYs[i] !== 0 ) {
35664 // fit container to columns that have been used
35665 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35668 needsResizeLayout : function()
35670 var previousWidth = this.containerWidth;
35671 this.getContainerWidth();
35672 return previousWidth !== this.containerWidth;
35687 * @class Roo.bootstrap.MasonryBrick
35688 * @extends Roo.bootstrap.Component
35689 * Bootstrap MasonryBrick class
35692 * Create a new MasonryBrick
35693 * @param {Object} config The config object
35696 Roo.bootstrap.MasonryBrick = function(config){
35698 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35700 Roo.bootstrap.MasonryBrick.register(this);
35706 * When a MasonryBrick is clcik
35707 * @param {Roo.bootstrap.MasonryBrick} this
35708 * @param {Roo.EventObject} e
35714 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
35717 * @cfg {String} title
35721 * @cfg {String} html
35725 * @cfg {String} bgimage
35729 * @cfg {String} videourl
35733 * @cfg {String} cls
35737 * @cfg {String} href
35741 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35746 * @cfg {String} placetitle (center|bottom)
35751 * @cfg {Boolean} isFitContainer defalut true
35753 isFitContainer : true,
35756 * @cfg {Boolean} preventDefault defalut false
35758 preventDefault : false,
35761 * @cfg {Boolean} inverse defalut false
35763 maskInverse : false,
35765 getAutoCreate : function()
35767 if(!this.isFitContainer){
35768 return this.getSplitAutoCreate();
35771 var cls = 'masonry-brick masonry-brick-full';
35773 if(this.href.length){
35774 cls += ' masonry-brick-link';
35777 if(this.bgimage.length){
35778 cls += ' masonry-brick-image';
35781 if(this.maskInverse){
35782 cls += ' mask-inverse';
35785 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35786 cls += ' enable-mask';
35790 cls += ' masonry-' + this.size + '-brick';
35793 if(this.placetitle.length){
35795 switch (this.placetitle) {
35797 cls += ' masonry-center-title';
35800 cls += ' masonry-bottom-title';
35807 if(!this.html.length && !this.bgimage.length){
35808 cls += ' masonry-center-title';
35811 if(!this.html.length && this.bgimage.length){
35812 cls += ' masonry-bottom-title';
35817 cls += ' ' + this.cls;
35821 tag: (this.href.length) ? 'a' : 'div',
35826 cls: 'masonry-brick-mask'
35830 cls: 'masonry-brick-paragraph',
35836 if(this.href.length){
35837 cfg.href = this.href;
35840 var cn = cfg.cn[1].cn;
35842 if(this.title.length){
35845 cls: 'masonry-brick-title',
35850 if(this.html.length){
35853 cls: 'masonry-brick-text',
35858 if (!this.title.length && !this.html.length) {
35859 cfg.cn[1].cls += ' hide';
35862 if(this.bgimage.length){
35865 cls: 'masonry-brick-image-view',
35870 if(this.videourl.length){
35871 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35872 // youtube support only?
35875 cls: 'masonry-brick-image-view',
35878 allowfullscreen : true
35886 getSplitAutoCreate : function()
35888 var cls = 'masonry-brick masonry-brick-split';
35890 if(this.href.length){
35891 cls += ' masonry-brick-link';
35894 if(this.bgimage.length){
35895 cls += ' masonry-brick-image';
35899 cls += ' masonry-' + this.size + '-brick';
35902 switch (this.placetitle) {
35904 cls += ' masonry-center-title';
35907 cls += ' masonry-bottom-title';
35910 if(!this.bgimage.length){
35911 cls += ' masonry-center-title';
35914 if(this.bgimage.length){
35915 cls += ' masonry-bottom-title';
35921 cls += ' ' + this.cls;
35925 tag: (this.href.length) ? 'a' : 'div',
35930 cls: 'masonry-brick-split-head',
35934 cls: 'masonry-brick-paragraph',
35941 cls: 'masonry-brick-split-body',
35947 if(this.href.length){
35948 cfg.href = this.href;
35951 if(this.title.length){
35952 cfg.cn[0].cn[0].cn.push({
35954 cls: 'masonry-brick-title',
35959 if(this.html.length){
35960 cfg.cn[1].cn.push({
35962 cls: 'masonry-brick-text',
35967 if(this.bgimage.length){
35968 cfg.cn[0].cn.push({
35970 cls: 'masonry-brick-image-view',
35975 if(this.videourl.length){
35976 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35977 // youtube support only?
35978 cfg.cn[0].cn.cn.push({
35980 cls: 'masonry-brick-image-view',
35983 allowfullscreen : true
35990 initEvents: function()
35992 switch (this.size) {
36025 this.el.on('touchstart', this.onTouchStart, this);
36026 this.el.on('touchmove', this.onTouchMove, this);
36027 this.el.on('touchend', this.onTouchEnd, this);
36028 this.el.on('contextmenu', this.onContextMenu, this);
36030 this.el.on('mouseenter' ,this.enter, this);
36031 this.el.on('mouseleave', this.leave, this);
36032 this.el.on('click', this.onClick, this);
36035 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36036 this.parent().bricks.push(this);
36041 onClick: function(e, el)
36043 var time = this.endTimer - this.startTimer;
36044 // Roo.log(e.preventDefault());
36047 e.preventDefault();
36052 if(!this.preventDefault){
36056 e.preventDefault();
36058 if (this.activeClass != '') {
36059 this.selectBrick();
36062 this.fireEvent('click', this, e);
36065 enter: function(e, el)
36067 e.preventDefault();
36069 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36073 if(this.bgimage.length && this.html.length){
36074 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36078 leave: function(e, el)
36080 e.preventDefault();
36082 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36086 if(this.bgimage.length && this.html.length){
36087 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36091 onTouchStart: function(e, el)
36093 // e.preventDefault();
36095 this.touchmoved = false;
36097 if(!this.isFitContainer){
36101 if(!this.bgimage.length || !this.html.length){
36105 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36107 this.timer = new Date().getTime();
36111 onTouchMove: function(e, el)
36113 this.touchmoved = true;
36116 onContextMenu : function(e,el)
36118 e.preventDefault();
36119 e.stopPropagation();
36123 onTouchEnd: function(e, el)
36125 // e.preventDefault();
36127 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36134 if(!this.bgimage.length || !this.html.length){
36136 if(this.href.length){
36137 window.location.href = this.href;
36143 if(!this.isFitContainer){
36147 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36149 window.location.href = this.href;
36152 //selection on single brick only
36153 selectBrick : function() {
36155 if (!this.parentId) {
36159 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36160 var index = m.selectedBrick.indexOf(this.id);
36163 m.selectedBrick.splice(index,1);
36164 this.el.removeClass(this.activeClass);
36168 for(var i = 0; i < m.selectedBrick.length; i++) {
36169 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36170 b.el.removeClass(b.activeClass);
36173 m.selectedBrick = [];
36175 m.selectedBrick.push(this.id);
36176 this.el.addClass(this.activeClass);
36180 isSelected : function(){
36181 return this.el.hasClass(this.activeClass);
36186 Roo.apply(Roo.bootstrap.MasonryBrick, {
36189 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36191 * register a Masonry Brick
36192 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36195 register : function(brick)
36197 //this.groups[brick.id] = brick;
36198 this.groups.add(brick.id, brick);
36201 * fetch a masonry brick based on the masonry brick ID
36202 * @param {string} the masonry brick to add
36203 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36206 get: function(brick_id)
36208 // if (typeof(this.groups[brick_id]) == 'undefined') {
36211 // return this.groups[brick_id] ;
36213 if(this.groups.key(brick_id)) {
36214 return this.groups.key(brick_id);
36232 * @class Roo.bootstrap.Brick
36233 * @extends Roo.bootstrap.Component
36234 * Bootstrap Brick class
36237 * Create a new Brick
36238 * @param {Object} config The config object
36241 Roo.bootstrap.Brick = function(config){
36242 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36248 * When a Brick is click
36249 * @param {Roo.bootstrap.Brick} this
36250 * @param {Roo.EventObject} e
36256 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
36259 * @cfg {String} title
36263 * @cfg {String} html
36267 * @cfg {String} bgimage
36271 * @cfg {String} cls
36275 * @cfg {String} href
36279 * @cfg {String} video
36283 * @cfg {Boolean} square
36287 getAutoCreate : function()
36289 var cls = 'roo-brick';
36291 if(this.href.length){
36292 cls += ' roo-brick-link';
36295 if(this.bgimage.length){
36296 cls += ' roo-brick-image';
36299 if(!this.html.length && !this.bgimage.length){
36300 cls += ' roo-brick-center-title';
36303 if(!this.html.length && this.bgimage.length){
36304 cls += ' roo-brick-bottom-title';
36308 cls += ' ' + this.cls;
36312 tag: (this.href.length) ? 'a' : 'div',
36317 cls: 'roo-brick-paragraph',
36323 if(this.href.length){
36324 cfg.href = this.href;
36327 var cn = cfg.cn[0].cn;
36329 if(this.title.length){
36332 cls: 'roo-brick-title',
36337 if(this.html.length){
36340 cls: 'roo-brick-text',
36347 if(this.bgimage.length){
36350 cls: 'roo-brick-image-view',
36358 initEvents: function()
36360 if(this.title.length || this.html.length){
36361 this.el.on('mouseenter' ,this.enter, this);
36362 this.el.on('mouseleave', this.leave, this);
36365 Roo.EventManager.onWindowResize(this.resize, this);
36367 if(this.bgimage.length){
36368 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36369 this.imageEl.on('load', this.onImageLoad, this);
36376 onImageLoad : function()
36381 resize : function()
36383 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36385 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36387 if(this.bgimage.length){
36388 var image = this.el.select('.roo-brick-image-view', true).first();
36390 image.setWidth(paragraph.getWidth());
36393 image.setHeight(paragraph.getWidth());
36396 this.el.setHeight(image.getHeight());
36397 paragraph.setHeight(image.getHeight());
36403 enter: function(e, el)
36405 e.preventDefault();
36407 if(this.bgimage.length){
36408 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36409 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36413 leave: function(e, el)
36415 e.preventDefault();
36417 if(this.bgimage.length){
36418 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36419 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36434 * @class Roo.bootstrap.NumberField
36435 * @extends Roo.bootstrap.Input
36436 * Bootstrap NumberField class
36442 * Create a new NumberField
36443 * @param {Object} config The config object
36446 Roo.bootstrap.NumberField = function(config){
36447 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36450 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36453 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36455 allowDecimals : true,
36457 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36459 decimalSeparator : ".",
36461 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36463 decimalPrecision : 2,
36465 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36467 allowNegative : true,
36470 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36474 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36476 minValue : Number.NEGATIVE_INFINITY,
36478 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36480 maxValue : Number.MAX_VALUE,
36482 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36484 minText : "The minimum value for this field is {0}",
36486 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36488 maxText : "The maximum value for this field is {0}",
36490 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36491 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36493 nanText : "{0} is not a valid number",
36495 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36497 thousandsDelimiter : false,
36499 * @cfg {String} valueAlign alignment of value
36501 valueAlign : "left",
36503 getAutoCreate : function()
36505 var hiddenInput = {
36509 cls: 'hidden-number-input'
36513 hiddenInput.name = this.name;
36518 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36520 this.name = hiddenInput.name;
36522 if(cfg.cn.length > 0) {
36523 cfg.cn.push(hiddenInput);
36530 initEvents : function()
36532 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36534 var allowed = "0123456789";
36536 if(this.allowDecimals){
36537 allowed += this.decimalSeparator;
36540 if(this.allowNegative){
36544 if(this.thousandsDelimiter) {
36548 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36550 var keyPress = function(e){
36552 var k = e.getKey();
36554 var c = e.getCharCode();
36557 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36558 allowed.indexOf(String.fromCharCode(c)) === -1
36564 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36568 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36573 this.el.on("keypress", keyPress, this);
36576 validateValue : function(value)
36579 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36583 var num = this.parseValue(value);
36586 this.markInvalid(String.format(this.nanText, value));
36590 if(num < this.minValue){
36591 this.markInvalid(String.format(this.minText, this.minValue));
36595 if(num > this.maxValue){
36596 this.markInvalid(String.format(this.maxText, this.maxValue));
36603 getValue : function()
36605 var v = this.hiddenEl().getValue();
36607 return this.fixPrecision(this.parseValue(v));
36610 parseValue : function(value)
36612 if(this.thousandsDelimiter) {
36614 r = new RegExp(",", "g");
36615 value = value.replace(r, "");
36618 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36619 return isNaN(value) ? '' : value;
36622 fixPrecision : function(value)
36624 if(this.thousandsDelimiter) {
36626 r = new RegExp(",", "g");
36627 value = value.replace(r, "");
36630 var nan = isNaN(value);
36632 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36633 return nan ? '' : value;
36635 return parseFloat(value).toFixed(this.decimalPrecision);
36638 setValue : function(v)
36640 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36646 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36648 this.inputEl().dom.value = (v == '') ? '' :
36649 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36651 if(!this.allowZero && v === '0') {
36652 this.hiddenEl().dom.value = '';
36653 this.inputEl().dom.value = '';
36660 decimalPrecisionFcn : function(v)
36662 return Math.floor(v);
36665 beforeBlur : function()
36667 var v = this.parseValue(this.getRawValue());
36669 if(v || v === 0 || v === ''){
36674 hiddenEl : function()
36676 return this.el.select('input.hidden-number-input',true).first();
36688 * @class Roo.bootstrap.DocumentSlider
36689 * @extends Roo.bootstrap.Component
36690 * Bootstrap DocumentSlider class
36693 * Create a new DocumentViewer
36694 * @param {Object} config The config object
36697 Roo.bootstrap.DocumentSlider = function(config){
36698 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36705 * Fire after initEvent
36706 * @param {Roo.bootstrap.DocumentSlider} this
36711 * Fire after update
36712 * @param {Roo.bootstrap.DocumentSlider} this
36718 * @param {Roo.bootstrap.DocumentSlider} this
36724 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
36730 getAutoCreate : function()
36734 cls : 'roo-document-slider',
36738 cls : 'roo-document-slider-header',
36742 cls : 'roo-document-slider-header-title'
36748 cls : 'roo-document-slider-body',
36752 cls : 'roo-document-slider-prev',
36756 cls : 'fa fa-chevron-left'
36762 cls : 'roo-document-slider-thumb',
36766 cls : 'roo-document-slider-image'
36772 cls : 'roo-document-slider-next',
36776 cls : 'fa fa-chevron-right'
36788 initEvents : function()
36790 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36791 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36793 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36794 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36796 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36797 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36799 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36800 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36802 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36803 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36805 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36806 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36808 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36809 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36811 this.thumbEl.on('click', this.onClick, this);
36813 this.prevIndicator.on('click', this.prev, this);
36815 this.nextIndicator.on('click', this.next, this);
36819 initial : function()
36821 if(this.files.length){
36822 this.indicator = 1;
36826 this.fireEvent('initial', this);
36829 update : function()
36831 this.imageEl.attr('src', this.files[this.indicator - 1]);
36833 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36835 this.prevIndicator.show();
36837 if(this.indicator == 1){
36838 this.prevIndicator.hide();
36841 this.nextIndicator.show();
36843 if(this.indicator == this.files.length){
36844 this.nextIndicator.hide();
36847 this.thumbEl.scrollTo('top');
36849 this.fireEvent('update', this);
36852 onClick : function(e)
36854 e.preventDefault();
36856 this.fireEvent('click', this);
36861 e.preventDefault();
36863 this.indicator = Math.max(1, this.indicator - 1);
36870 e.preventDefault();
36872 this.indicator = Math.min(this.files.length, this.indicator + 1);
36886 * @class Roo.bootstrap.RadioSet
36887 * @extends Roo.bootstrap.Input
36888 * Bootstrap RadioSet class
36889 * @cfg {String} indicatorpos (left|right) default left
36890 * @cfg {Boolean} inline (true|false) inline the element (default true)
36891 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36893 * Create a new RadioSet
36894 * @param {Object} config The config object
36897 Roo.bootstrap.RadioSet = function(config){
36899 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36903 Roo.bootstrap.RadioSet.register(this);
36908 * Fires when the element is checked or unchecked.
36909 * @param {Roo.bootstrap.RadioSet} this This radio
36910 * @param {Roo.bootstrap.Radio} item The checked item
36915 * Fires when the element is click.
36916 * @param {Roo.bootstrap.RadioSet} this This radio set
36917 * @param {Roo.bootstrap.Radio} item The checked item
36918 * @param {Roo.EventObject} e The event object
36925 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
36933 indicatorpos : 'left',
36935 getAutoCreate : function()
36939 cls : 'roo-radio-set-label',
36943 html : this.fieldLabel
36947 if (Roo.bootstrap.version == 3) {
36950 if(this.indicatorpos == 'left'){
36953 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36954 tooltip : 'This field is required'
36959 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36960 tooltip : 'This field is required'
36966 cls : 'roo-radio-set-items'
36969 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36971 if (align === 'left' && this.fieldLabel.length) {
36974 cls : "roo-radio-set-right",
36980 if(this.labelWidth > 12){
36981 label.style = "width: " + this.labelWidth + 'px';
36984 if(this.labelWidth < 13 && this.labelmd == 0){
36985 this.labelmd = this.labelWidth;
36988 if(this.labellg > 0){
36989 label.cls += ' col-lg-' + this.labellg;
36990 items.cls += ' col-lg-' + (12 - this.labellg);
36993 if(this.labelmd > 0){
36994 label.cls += ' col-md-' + this.labelmd;
36995 items.cls += ' col-md-' + (12 - this.labelmd);
36998 if(this.labelsm > 0){
36999 label.cls += ' col-sm-' + this.labelsm;
37000 items.cls += ' col-sm-' + (12 - this.labelsm);
37003 if(this.labelxs > 0){
37004 label.cls += ' col-xs-' + this.labelxs;
37005 items.cls += ' col-xs-' + (12 - this.labelxs);
37011 cls : 'roo-radio-set',
37015 cls : 'roo-radio-set-input',
37018 value : this.value ? this.value : ''
37025 if(this.weight.length){
37026 cfg.cls += ' roo-radio-' + this.weight;
37030 cfg.cls += ' roo-radio-set-inline';
37034 ['xs','sm','md','lg'].map(function(size){
37035 if (settings[size]) {
37036 cfg.cls += ' col-' + size + '-' + settings[size];
37044 initEvents : function()
37046 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37047 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37049 if(!this.fieldLabel.length){
37050 this.labelEl.hide();
37053 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37054 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37056 this.indicator = this.indicatorEl();
37058 if(this.indicator){
37059 this.indicator.addClass('invisible');
37062 this.originalValue = this.getValue();
37066 inputEl: function ()
37068 return this.el.select('.roo-radio-set-input', true).first();
37071 getChildContainer : function()
37073 return this.itemsEl;
37076 register : function(item)
37078 this.radioes.push(item);
37082 validate : function()
37084 if(this.getVisibilityEl().hasClass('hidden')){
37090 Roo.each(this.radioes, function(i){
37099 if(this.allowBlank) {
37103 if(this.disabled || valid){
37108 this.markInvalid();
37113 markValid : function()
37115 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37116 this.indicatorEl().removeClass('visible');
37117 this.indicatorEl().addClass('invisible');
37121 if (Roo.bootstrap.version == 3) {
37122 this.el.removeClass([this.invalidClass, this.validClass]);
37123 this.el.addClass(this.validClass);
37125 this.el.removeClass(['is-invalid','is-valid']);
37126 this.el.addClass(['is-valid']);
37128 this.fireEvent('valid', this);
37131 markInvalid : function(msg)
37133 if(this.allowBlank || this.disabled){
37137 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37138 this.indicatorEl().removeClass('invisible');
37139 this.indicatorEl().addClass('visible');
37141 if (Roo.bootstrap.version == 3) {
37142 this.el.removeClass([this.invalidClass, this.validClass]);
37143 this.el.addClass(this.invalidClass);
37145 this.el.removeClass(['is-invalid','is-valid']);
37146 this.el.addClass(['is-invalid']);
37149 this.fireEvent('invalid', this, msg);
37153 setValue : function(v, suppressEvent)
37155 if(this.value === v){
37162 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37165 Roo.each(this.radioes, function(i){
37167 i.el.removeClass('checked');
37170 Roo.each(this.radioes, function(i){
37172 if(i.value === v || i.value.toString() === v.toString()){
37174 i.el.addClass('checked');
37176 if(suppressEvent !== true){
37177 this.fireEvent('check', this, i);
37188 clearInvalid : function(){
37190 if(!this.el || this.preventMark){
37194 this.el.removeClass([this.invalidClass]);
37196 this.fireEvent('valid', this);
37201 Roo.apply(Roo.bootstrap.RadioSet, {
37205 register : function(set)
37207 this.groups[set.name] = set;
37210 get: function(name)
37212 if (typeof(this.groups[name]) == 'undefined') {
37216 return this.groups[name] ;
37222 * Ext JS Library 1.1.1
37223 * Copyright(c) 2006-2007, Ext JS, LLC.
37225 * Originally Released Under LGPL - original licence link has changed is not relivant.
37228 * <script type="text/javascript">
37233 * @class Roo.bootstrap.SplitBar
37234 * @extends Roo.util.Observable
37235 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37239 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37240 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37241 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37242 split.minSize = 100;
37243 split.maxSize = 600;
37244 split.animate = true;
37245 split.on('moved', splitterMoved);
37248 * Create a new SplitBar
37249 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
37250 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
37251 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37252 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
37253 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37254 position of the SplitBar).
37256 Roo.bootstrap.SplitBar = function(cfg){
37261 // dragElement : elm
37262 // resizingElement: el,
37264 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37265 // placement : Roo.bootstrap.SplitBar.LEFT ,
37266 // existingProxy ???
37269 this.el = Roo.get(cfg.dragElement, true);
37270 this.el.dom.unselectable = "on";
37272 this.resizingEl = Roo.get(cfg.resizingElement, true);
37276 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37277 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37280 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37283 * The minimum size of the resizing element. (Defaults to 0)
37289 * The maximum size of the resizing element. (Defaults to 2000)
37292 this.maxSize = 2000;
37295 * Whether to animate the transition to the new size
37298 this.animate = false;
37301 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37304 this.useShim = false;
37309 if(!cfg.existingProxy){
37311 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37313 this.proxy = Roo.get(cfg.existingProxy).dom;
37316 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37319 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37322 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37325 this.dragSpecs = {};
37328 * @private The adapter to use to positon and resize elements
37330 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37331 this.adapter.init(this);
37333 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37335 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37336 this.el.addClass("roo-splitbar-h");
37339 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37340 this.el.addClass("roo-splitbar-v");
37346 * Fires when the splitter is moved (alias for {@link #event-moved})
37347 * @param {Roo.bootstrap.SplitBar} this
37348 * @param {Number} newSize the new width or height
37353 * Fires when the splitter is moved
37354 * @param {Roo.bootstrap.SplitBar} this
37355 * @param {Number} newSize the new width or height
37359 * @event beforeresize
37360 * Fires before the splitter is dragged
37361 * @param {Roo.bootstrap.SplitBar} this
37363 "beforeresize" : true,
37365 "beforeapply" : true
37368 Roo.util.Observable.call(this);
37371 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37372 onStartProxyDrag : function(x, y){
37373 this.fireEvent("beforeresize", this);
37375 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37377 o.enableDisplayMode("block");
37378 // all splitbars share the same overlay
37379 Roo.bootstrap.SplitBar.prototype.overlay = o;
37381 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37382 this.overlay.show();
37383 Roo.get(this.proxy).setDisplayed("block");
37384 var size = this.adapter.getElementSize(this);
37385 this.activeMinSize = this.getMinimumSize();;
37386 this.activeMaxSize = this.getMaximumSize();;
37387 var c1 = size - this.activeMinSize;
37388 var c2 = Math.max(this.activeMaxSize - size, 0);
37389 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37390 this.dd.resetConstraints();
37391 this.dd.setXConstraint(
37392 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37393 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37395 this.dd.setYConstraint(0, 0);
37397 this.dd.resetConstraints();
37398 this.dd.setXConstraint(0, 0);
37399 this.dd.setYConstraint(
37400 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37401 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37404 this.dragSpecs.startSize = size;
37405 this.dragSpecs.startPoint = [x, y];
37406 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37410 * @private Called after the drag operation by the DDProxy
37412 onEndProxyDrag : function(e){
37413 Roo.get(this.proxy).setDisplayed(false);
37414 var endPoint = Roo.lib.Event.getXY(e);
37416 this.overlay.hide();
37419 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37420 newSize = this.dragSpecs.startSize +
37421 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37422 endPoint[0] - this.dragSpecs.startPoint[0] :
37423 this.dragSpecs.startPoint[0] - endPoint[0]
37426 newSize = this.dragSpecs.startSize +
37427 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37428 endPoint[1] - this.dragSpecs.startPoint[1] :
37429 this.dragSpecs.startPoint[1] - endPoint[1]
37432 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37433 if(newSize != this.dragSpecs.startSize){
37434 if(this.fireEvent('beforeapply', this, newSize) !== false){
37435 this.adapter.setElementSize(this, newSize);
37436 this.fireEvent("moved", this, newSize);
37437 this.fireEvent("resize", this, newSize);
37443 * Get the adapter this SplitBar uses
37444 * @return The adapter object
37446 getAdapter : function(){
37447 return this.adapter;
37451 * Set the adapter this SplitBar uses
37452 * @param {Object} adapter A SplitBar adapter object
37454 setAdapter : function(adapter){
37455 this.adapter = adapter;
37456 this.adapter.init(this);
37460 * Gets the minimum size for the resizing element
37461 * @return {Number} The minimum size
37463 getMinimumSize : function(){
37464 return this.minSize;
37468 * Sets the minimum size for the resizing element
37469 * @param {Number} minSize The minimum size
37471 setMinimumSize : function(minSize){
37472 this.minSize = minSize;
37476 * Gets the maximum size for the resizing element
37477 * @return {Number} The maximum size
37479 getMaximumSize : function(){
37480 return this.maxSize;
37484 * Sets the maximum size for the resizing element
37485 * @param {Number} maxSize The maximum size
37487 setMaximumSize : function(maxSize){
37488 this.maxSize = maxSize;
37492 * Sets the initialize size for the resizing element
37493 * @param {Number} size The initial size
37495 setCurrentSize : function(size){
37496 var oldAnimate = this.animate;
37497 this.animate = false;
37498 this.adapter.setElementSize(this, size);
37499 this.animate = oldAnimate;
37503 * Destroy this splitbar.
37504 * @param {Boolean} removeEl True to remove the element
37506 destroy : function(removeEl){
37508 this.shim.remove();
37511 this.proxy.parentNode.removeChild(this.proxy);
37519 * @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.
37521 Roo.bootstrap.SplitBar.createProxy = function(dir){
37522 var proxy = new Roo.Element(document.createElement("div"));
37523 proxy.unselectable();
37524 var cls = 'roo-splitbar-proxy';
37525 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37526 document.body.appendChild(proxy.dom);
37531 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37532 * Default Adapter. It assumes the splitter and resizing element are not positioned
37533 * elements and only gets/sets the width of the element. Generally used for table based layouts.
37535 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37538 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37539 // do nothing for now
37540 init : function(s){
37544 * Called before drag operations to get the current size of the resizing element.
37545 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37547 getElementSize : function(s){
37548 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37549 return s.resizingEl.getWidth();
37551 return s.resizingEl.getHeight();
37556 * Called after drag operations to set the size of the resizing element.
37557 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37558 * @param {Number} newSize The new size to set
37559 * @param {Function} onComplete A function to be invoked when resizing is complete
37561 setElementSize : function(s, newSize, onComplete){
37562 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37564 s.resizingEl.setWidth(newSize);
37566 onComplete(s, newSize);
37569 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37574 s.resizingEl.setHeight(newSize);
37576 onComplete(s, newSize);
37579 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37586 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37587 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37588 * Adapter that moves the splitter element to align with the resized sizing element.
37589 * Used with an absolute positioned SplitBar.
37590 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37591 * document.body, make sure you assign an id to the body element.
37593 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37594 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37595 this.container = Roo.get(container);
37598 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37599 init : function(s){
37600 this.basic.init(s);
37603 getElementSize : function(s){
37604 return this.basic.getElementSize(s);
37607 setElementSize : function(s, newSize, onComplete){
37608 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37611 moveSplitter : function(s){
37612 var yes = Roo.bootstrap.SplitBar;
37613 switch(s.placement){
37615 s.el.setX(s.resizingEl.getRight());
37618 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37621 s.el.setY(s.resizingEl.getBottom());
37624 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37631 * Orientation constant - Create a vertical SplitBar
37635 Roo.bootstrap.SplitBar.VERTICAL = 1;
37638 * Orientation constant - Create a horizontal SplitBar
37642 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37645 * Placement constant - The resizing element is to the left of the splitter element
37649 Roo.bootstrap.SplitBar.LEFT = 1;
37652 * Placement constant - The resizing element is to the right of the splitter element
37656 Roo.bootstrap.SplitBar.RIGHT = 2;
37659 * Placement constant - The resizing element is positioned above the splitter element
37663 Roo.bootstrap.SplitBar.TOP = 3;
37666 * Placement constant - The resizing element is positioned under splitter element
37670 Roo.bootstrap.SplitBar.BOTTOM = 4;
37671 Roo.namespace("Roo.bootstrap.layout");/*
37673 * Ext JS Library 1.1.1
37674 * Copyright(c) 2006-2007, Ext JS, LLC.
37676 * Originally Released Under LGPL - original licence link has changed is not relivant.
37679 * <script type="text/javascript">
37683 * @class Roo.bootstrap.layout.Manager
37684 * @extends Roo.bootstrap.Component
37685 * Base class for layout managers.
37687 Roo.bootstrap.layout.Manager = function(config)
37689 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37695 /** false to disable window resize monitoring @type Boolean */
37696 this.monitorWindowResize = true;
37701 * Fires when a layout is performed.
37702 * @param {Roo.LayoutManager} this
37706 * @event regionresized
37707 * Fires when the user resizes a region.
37708 * @param {Roo.LayoutRegion} region The resized region
37709 * @param {Number} newSize The new size (width for east/west, height for north/south)
37711 "regionresized" : true,
37713 * @event regioncollapsed
37714 * Fires when a region is collapsed.
37715 * @param {Roo.LayoutRegion} region The collapsed region
37717 "regioncollapsed" : true,
37719 * @event regionexpanded
37720 * Fires when a region is expanded.
37721 * @param {Roo.LayoutRegion} region The expanded region
37723 "regionexpanded" : true
37725 this.updating = false;
37728 this.el = Roo.get(config.el);
37734 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37739 monitorWindowResize : true,
37745 onRender : function(ct, position)
37748 this.el = Roo.get(ct);
37751 //this.fireEvent('render',this);
37755 initEvents: function()
37759 // ie scrollbar fix
37760 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37761 document.body.scroll = "no";
37762 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37763 this.el.position('relative');
37765 this.id = this.el.id;
37766 this.el.addClass("roo-layout-container");
37767 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37768 if(this.el.dom != document.body ) {
37769 this.el.on('resize', this.layout,this);
37770 this.el.on('show', this.layout,this);
37776 * Returns true if this layout is currently being updated
37777 * @return {Boolean}
37779 isUpdating : function(){
37780 return this.updating;
37784 * Suspend the LayoutManager from doing auto-layouts while
37785 * making multiple add or remove calls
37787 beginUpdate : function(){
37788 this.updating = true;
37792 * Restore auto-layouts and optionally disable the manager from performing a layout
37793 * @param {Boolean} noLayout true to disable a layout update
37795 endUpdate : function(noLayout){
37796 this.updating = false;
37802 layout: function(){
37806 onRegionResized : function(region, newSize){
37807 this.fireEvent("regionresized", region, newSize);
37811 onRegionCollapsed : function(region){
37812 this.fireEvent("regioncollapsed", region);
37815 onRegionExpanded : function(region){
37816 this.fireEvent("regionexpanded", region);
37820 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37821 * performs box-model adjustments.
37822 * @return {Object} The size as an object {width: (the width), height: (the height)}
37824 getViewSize : function()
37827 if(this.el.dom != document.body){
37828 size = this.el.getSize();
37830 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37832 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37833 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37838 * Returns the Element this layout is bound to.
37839 * @return {Roo.Element}
37841 getEl : function(){
37846 * Returns the specified region.
37847 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37848 * @return {Roo.LayoutRegion}
37850 getRegion : function(target){
37851 return this.regions[target.toLowerCase()];
37854 onWindowResize : function(){
37855 if(this.monitorWindowResize){
37862 * Ext JS Library 1.1.1
37863 * Copyright(c) 2006-2007, Ext JS, LLC.
37865 * Originally Released Under LGPL - original licence link has changed is not relivant.
37868 * <script type="text/javascript">
37871 * @class Roo.bootstrap.layout.Border
37872 * @extends Roo.bootstrap.layout.Manager
37873 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37874 * please see: examples/bootstrap/nested.html<br><br>
37876 <b>The container the layout is rendered into can be either the body element or any other element.
37877 If it is not the body element, the container needs to either be an absolute positioned element,
37878 or you will need to add "position:relative" to the css of the container. You will also need to specify
37879 the container size if it is not the body element.</b>
37882 * Create a new Border
37883 * @param {Object} config Configuration options
37885 Roo.bootstrap.layout.Border = function(config){
37886 config = config || {};
37887 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37891 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37892 if(config[region]){
37893 config[region].region = region;
37894 this.addRegion(config[region]);
37900 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
37902 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37904 parent : false, // this might point to a 'nest' or a ???
37907 * Creates and adds a new region if it doesn't already exist.
37908 * @param {String} target The target region key (north, south, east, west or center).
37909 * @param {Object} config The regions config object
37910 * @return {BorderLayoutRegion} The new region
37912 addRegion : function(config)
37914 if(!this.regions[config.region]){
37915 var r = this.factory(config);
37916 this.bindRegion(r);
37918 return this.regions[config.region];
37922 bindRegion : function(r){
37923 this.regions[r.config.region] = r;
37925 r.on("visibilitychange", this.layout, this);
37926 r.on("paneladded", this.layout, this);
37927 r.on("panelremoved", this.layout, this);
37928 r.on("invalidated", this.layout, this);
37929 r.on("resized", this.onRegionResized, this);
37930 r.on("collapsed", this.onRegionCollapsed, this);
37931 r.on("expanded", this.onRegionExpanded, this);
37935 * Performs a layout update.
37937 layout : function()
37939 if(this.updating) {
37943 // render all the rebions if they have not been done alreayd?
37944 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37945 if(this.regions[region] && !this.regions[region].bodyEl){
37946 this.regions[region].onRender(this.el)
37950 var size = this.getViewSize();
37951 var w = size.width;
37952 var h = size.height;
37957 //var x = 0, y = 0;
37959 var rs = this.regions;
37960 var north = rs["north"];
37961 var south = rs["south"];
37962 var west = rs["west"];
37963 var east = rs["east"];
37964 var center = rs["center"];
37965 //if(this.hideOnLayout){ // not supported anymore
37966 //c.el.setStyle("display", "none");
37968 if(north && north.isVisible()){
37969 var b = north.getBox();
37970 var m = north.getMargins();
37971 b.width = w - (m.left+m.right);
37974 centerY = b.height + b.y + m.bottom;
37975 centerH -= centerY;
37976 north.updateBox(this.safeBox(b));
37978 if(south && south.isVisible()){
37979 var b = south.getBox();
37980 var m = south.getMargins();
37981 b.width = w - (m.left+m.right);
37983 var totalHeight = (b.height + m.top + m.bottom);
37984 b.y = h - totalHeight + m.top;
37985 centerH -= totalHeight;
37986 south.updateBox(this.safeBox(b));
37988 if(west && west.isVisible()){
37989 var b = west.getBox();
37990 var m = west.getMargins();
37991 b.height = centerH - (m.top+m.bottom);
37993 b.y = centerY + m.top;
37994 var totalWidth = (b.width + m.left + m.right);
37995 centerX += totalWidth;
37996 centerW -= totalWidth;
37997 west.updateBox(this.safeBox(b));
37999 if(east && east.isVisible()){
38000 var b = east.getBox();
38001 var m = east.getMargins();
38002 b.height = centerH - (m.top+m.bottom);
38003 var totalWidth = (b.width + m.left + m.right);
38004 b.x = w - totalWidth + m.left;
38005 b.y = centerY + m.top;
38006 centerW -= totalWidth;
38007 east.updateBox(this.safeBox(b));
38010 var m = center.getMargins();
38012 x: centerX + m.left,
38013 y: centerY + m.top,
38014 width: centerW - (m.left+m.right),
38015 height: centerH - (m.top+m.bottom)
38017 //if(this.hideOnLayout){
38018 //center.el.setStyle("display", "block");
38020 center.updateBox(this.safeBox(centerBox));
38023 this.fireEvent("layout", this);
38027 safeBox : function(box){
38028 box.width = Math.max(0, box.width);
38029 box.height = Math.max(0, box.height);
38034 * Adds a ContentPanel (or subclass) to this layout.
38035 * @param {String} target The target region key (north, south, east, west or center).
38036 * @param {Roo.ContentPanel} panel The panel to add
38037 * @return {Roo.ContentPanel} The added panel
38039 add : function(target, panel){
38041 target = target.toLowerCase();
38042 return this.regions[target].add(panel);
38046 * Remove a ContentPanel (or subclass) to this layout.
38047 * @param {String} target The target region key (north, south, east, west or center).
38048 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38049 * @return {Roo.ContentPanel} The removed panel
38051 remove : function(target, panel){
38052 target = target.toLowerCase();
38053 return this.regions[target].remove(panel);
38057 * Searches all regions for a panel with the specified id
38058 * @param {String} panelId
38059 * @return {Roo.ContentPanel} The panel or null if it wasn't found
38061 findPanel : function(panelId){
38062 var rs = this.regions;
38063 for(var target in rs){
38064 if(typeof rs[target] != "function"){
38065 var p = rs[target].getPanel(panelId);
38075 * Searches all regions for a panel with the specified id and activates (shows) it.
38076 * @param {String/ContentPanel} panelId The panels id or the panel itself
38077 * @return {Roo.ContentPanel} The shown panel or null
38079 showPanel : function(panelId) {
38080 var rs = this.regions;
38081 for(var target in rs){
38082 var r = rs[target];
38083 if(typeof r != "function"){
38084 if(r.hasPanel(panelId)){
38085 return r.showPanel(panelId);
38093 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38094 * @param {Roo.state.Provider} provider (optional) An alternate state provider
38097 restoreState : function(provider){
38099 provider = Roo.state.Manager;
38101 var sm = new Roo.LayoutStateManager();
38102 sm.init(this, provider);
38108 * Adds a xtype elements to the layout.
38112 xtype : 'ContentPanel',
38119 xtype : 'NestedLayoutPanel',
38125 items : [ ... list of content panels or nested layout panels.. ]
38129 * @param {Object} cfg Xtype definition of item to add.
38131 addxtype : function(cfg)
38133 // basically accepts a pannel...
38134 // can accept a layout region..!?!?
38135 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38138 // theory? children can only be panels??
38140 //if (!cfg.xtype.match(/Panel$/)) {
38145 if (typeof(cfg.region) == 'undefined') {
38146 Roo.log("Failed to add Panel, region was not set");
38150 var region = cfg.region;
38156 xitems = cfg.items;
38161 if ( region == 'center') {
38162 Roo.log("Center: " + cfg.title);
38168 case 'Content': // ContentPanel (el, cfg)
38169 case 'Scroll': // ContentPanel (el, cfg)
38171 cfg.autoCreate = cfg.autoCreate || true;
38172 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38174 // var el = this.el.createChild();
38175 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38178 this.add(region, ret);
38182 case 'TreePanel': // our new panel!
38183 cfg.el = this.el.createChild();
38184 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38185 this.add(region, ret);
38190 // create a new Layout (which is a Border Layout...
38192 var clayout = cfg.layout;
38193 clayout.el = this.el.createChild();
38194 clayout.items = clayout.items || [];
38198 // replace this exitems with the clayout ones..
38199 xitems = clayout.items;
38201 // force background off if it's in center...
38202 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38203 cfg.background = false;
38205 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
38208 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38209 //console.log('adding nested layout panel ' + cfg.toSource());
38210 this.add(region, ret);
38211 nb = {}; /// find first...
38216 // needs grid and region
38218 //var el = this.getRegion(region).el.createChild();
38220 *var el = this.el.createChild();
38221 // create the grid first...
38222 cfg.grid.container = el;
38223 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38226 if (region == 'center' && this.active ) {
38227 cfg.background = false;
38230 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38232 this.add(region, ret);
38234 if (cfg.background) {
38235 // render grid on panel activation (if panel background)
38236 ret.on('activate', function(gp) {
38237 if (!gp.grid.rendered) {
38238 // gp.grid.render(el);
38242 // cfg.grid.render(el);
38248 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38249 // it was the old xcomponent building that caused this before.
38250 // espeically if border is the top element in the tree.
38260 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38262 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38263 this.add(region, ret);
38267 throw "Can not add '" + cfg.xtype + "' to Border";
38273 this.beginUpdate();
38277 Roo.each(xitems, function(i) {
38278 region = nb && i.region ? i.region : false;
38280 var add = ret.addxtype(i);
38283 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38284 if (!i.background) {
38285 abn[region] = nb[region] ;
38292 // make the last non-background panel active..
38293 //if (nb) { Roo.log(abn); }
38296 for(var r in abn) {
38297 region = this.getRegion(r);
38299 // tried using nb[r], but it does not work..
38301 region.showPanel(abn[r]);
38312 factory : function(cfg)
38315 var validRegions = Roo.bootstrap.layout.Border.regions;
38317 var target = cfg.region;
38320 var r = Roo.bootstrap.layout;
38324 return new r.North(cfg);
38326 return new r.South(cfg);
38328 return new r.East(cfg);
38330 return new r.West(cfg);
38332 return new r.Center(cfg);
38334 throw 'Layout region "'+target+'" not supported.';
38341 * Ext JS Library 1.1.1
38342 * Copyright(c) 2006-2007, Ext JS, LLC.
38344 * Originally Released Under LGPL - original licence link has changed is not relivant.
38347 * <script type="text/javascript">
38351 * @class Roo.bootstrap.layout.Basic
38352 * @extends Roo.util.Observable
38353 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38354 * and does not have a titlebar, tabs or any other features. All it does is size and position
38355 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38356 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38357 * @cfg {string} region the region that it inhabits..
38358 * @cfg {bool} skipConfig skip config?
38362 Roo.bootstrap.layout.Basic = function(config){
38364 this.mgr = config.mgr;
38366 this.position = config.region;
38368 var skipConfig = config.skipConfig;
38372 * @scope Roo.BasicLayoutRegion
38376 * @event beforeremove
38377 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38378 * @param {Roo.LayoutRegion} this
38379 * @param {Roo.ContentPanel} panel The panel
38380 * @param {Object} e The cancel event object
38382 "beforeremove" : true,
38384 * @event invalidated
38385 * Fires when the layout for this region is changed.
38386 * @param {Roo.LayoutRegion} this
38388 "invalidated" : true,
38390 * @event visibilitychange
38391 * Fires when this region is shown or hidden
38392 * @param {Roo.LayoutRegion} this
38393 * @param {Boolean} visibility true or false
38395 "visibilitychange" : true,
38397 * @event paneladded
38398 * Fires when a panel is added.
38399 * @param {Roo.LayoutRegion} this
38400 * @param {Roo.ContentPanel} panel The panel
38402 "paneladded" : true,
38404 * @event panelremoved
38405 * Fires when a panel is removed.
38406 * @param {Roo.LayoutRegion} this
38407 * @param {Roo.ContentPanel} panel The panel
38409 "panelremoved" : true,
38411 * @event beforecollapse
38412 * Fires when this region before collapse.
38413 * @param {Roo.LayoutRegion} this
38415 "beforecollapse" : true,
38418 * Fires when this region is collapsed.
38419 * @param {Roo.LayoutRegion} this
38421 "collapsed" : true,
38424 * Fires when this region is expanded.
38425 * @param {Roo.LayoutRegion} this
38430 * Fires when this region is slid into view.
38431 * @param {Roo.LayoutRegion} this
38433 "slideshow" : true,
38436 * Fires when this region slides out of view.
38437 * @param {Roo.LayoutRegion} this
38439 "slidehide" : true,
38441 * @event panelactivated
38442 * Fires when a panel is activated.
38443 * @param {Roo.LayoutRegion} this
38444 * @param {Roo.ContentPanel} panel The activated panel
38446 "panelactivated" : true,
38449 * Fires when the user resizes this region.
38450 * @param {Roo.LayoutRegion} this
38451 * @param {Number} newSize The new size (width for east/west, height for north/south)
38455 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38456 this.panels = new Roo.util.MixedCollection();
38457 this.panels.getKey = this.getPanelId.createDelegate(this);
38459 this.activePanel = null;
38460 // ensure listeners are added...
38462 if (config.listeners || config.events) {
38463 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38464 listeners : config.listeners || {},
38465 events : config.events || {}
38469 if(skipConfig !== true){
38470 this.applyConfig(config);
38474 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38476 getPanelId : function(p){
38480 applyConfig : function(config){
38481 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38482 this.config = config;
38487 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38488 * the width, for horizontal (north, south) the height.
38489 * @param {Number} newSize The new width or height
38491 resizeTo : function(newSize){
38492 var el = this.el ? this.el :
38493 (this.activePanel ? this.activePanel.getEl() : null);
38495 switch(this.position){
38498 el.setWidth(newSize);
38499 this.fireEvent("resized", this, newSize);
38503 el.setHeight(newSize);
38504 this.fireEvent("resized", this, newSize);
38510 getBox : function(){
38511 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38514 getMargins : function(){
38515 return this.margins;
38518 updateBox : function(box){
38520 var el = this.activePanel.getEl();
38521 el.dom.style.left = box.x + "px";
38522 el.dom.style.top = box.y + "px";
38523 this.activePanel.setSize(box.width, box.height);
38527 * Returns the container element for this region.
38528 * @return {Roo.Element}
38530 getEl : function(){
38531 return this.activePanel;
38535 * Returns true if this region is currently visible.
38536 * @return {Boolean}
38538 isVisible : function(){
38539 return this.activePanel ? true : false;
38542 setActivePanel : function(panel){
38543 panel = this.getPanel(panel);
38544 if(this.activePanel && this.activePanel != panel){
38545 this.activePanel.setActiveState(false);
38546 this.activePanel.getEl().setLeftTop(-10000,-10000);
38548 this.activePanel = panel;
38549 panel.setActiveState(true);
38551 panel.setSize(this.box.width, this.box.height);
38553 this.fireEvent("panelactivated", this, panel);
38554 this.fireEvent("invalidated");
38558 * Show the specified panel.
38559 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38560 * @return {Roo.ContentPanel} The shown panel or null
38562 showPanel : function(panel){
38563 panel = this.getPanel(panel);
38565 this.setActivePanel(panel);
38571 * Get the active panel for this region.
38572 * @return {Roo.ContentPanel} The active panel or null
38574 getActivePanel : function(){
38575 return this.activePanel;
38579 * Add the passed ContentPanel(s)
38580 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38581 * @return {Roo.ContentPanel} The panel added (if only one was added)
38583 add : function(panel){
38584 if(arguments.length > 1){
38585 for(var i = 0, len = arguments.length; i < len; i++) {
38586 this.add(arguments[i]);
38590 if(this.hasPanel(panel)){
38591 this.showPanel(panel);
38594 var el = panel.getEl();
38595 if(el.dom.parentNode != this.mgr.el.dom){
38596 this.mgr.el.dom.appendChild(el.dom);
38598 if(panel.setRegion){
38599 panel.setRegion(this);
38601 this.panels.add(panel);
38602 el.setStyle("position", "absolute");
38603 if(!panel.background){
38604 this.setActivePanel(panel);
38605 if(this.config.initialSize && this.panels.getCount()==1){
38606 this.resizeTo(this.config.initialSize);
38609 this.fireEvent("paneladded", this, panel);
38614 * Returns true if the panel is in this region.
38615 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38616 * @return {Boolean}
38618 hasPanel : function(panel){
38619 if(typeof panel == "object"){ // must be panel obj
38620 panel = panel.getId();
38622 return this.getPanel(panel) ? true : false;
38626 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38627 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38628 * @param {Boolean} preservePanel Overrides the config preservePanel option
38629 * @return {Roo.ContentPanel} The panel that was removed
38631 remove : function(panel, preservePanel){
38632 panel = this.getPanel(panel);
38637 this.fireEvent("beforeremove", this, panel, e);
38638 if(e.cancel === true){
38641 var panelId = panel.getId();
38642 this.panels.removeKey(panelId);
38647 * Returns the panel specified or null if it's not in this region.
38648 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38649 * @return {Roo.ContentPanel}
38651 getPanel : function(id){
38652 if(typeof id == "object"){ // must be panel obj
38655 return this.panels.get(id);
38659 * Returns this regions position (north/south/east/west/center).
38662 getPosition: function(){
38663 return this.position;
38667 * Ext JS Library 1.1.1
38668 * Copyright(c) 2006-2007, Ext JS, LLC.
38670 * Originally Released Under LGPL - original licence link has changed is not relivant.
38673 * <script type="text/javascript">
38677 * @class Roo.bootstrap.layout.Region
38678 * @extends Roo.bootstrap.layout.Basic
38679 * This class represents a region in a layout manager.
38681 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38682 * @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})
38683 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
38684 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
38685 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
38686 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
38687 * @cfg {String} title The title for the region (overrides panel titles)
38688 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
38689 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38690 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
38691 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38692 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
38693 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38694 * the space available, similar to FireFox 1.5 tabs (defaults to false)
38695 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
38696 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
38697 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
38699 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
38700 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
38701 * @cfg {Boolean} disableTabTips True to disable tab tooltips
38702 * @cfg {Number} width For East/West panels
38703 * @cfg {Number} height For North/South panels
38704 * @cfg {Boolean} split To show the splitter
38705 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
38707 * @cfg {string} cls Extra CSS classes to add to region
38709 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38710 * @cfg {string} region the region that it inhabits..
38713 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
38714 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
38716 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
38717 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
38718 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
38720 Roo.bootstrap.layout.Region = function(config)
38722 this.applyConfig(config);
38724 var mgr = config.mgr;
38725 var pos = config.region;
38726 config.skipConfig = true;
38727 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38730 this.onRender(mgr.el);
38733 this.visible = true;
38734 this.collapsed = false;
38735 this.unrendered_panels = [];
38738 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38740 position: '', // set by wrapper (eg. north/south etc..)
38741 unrendered_panels : null, // unrendered panels.
38743 tabPosition : false,
38745 mgr: false, // points to 'Border'
38748 createBody : function(){
38749 /** This region's body element
38750 * @type Roo.Element */
38751 this.bodyEl = this.el.createChild({
38753 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38757 onRender: function(ctr, pos)
38759 var dh = Roo.DomHelper;
38760 /** This region's container element
38761 * @type Roo.Element */
38762 this.el = dh.append(ctr.dom, {
38764 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38766 /** This region's title element
38767 * @type Roo.Element */
38769 this.titleEl = dh.append(this.el.dom, {
38771 unselectable: "on",
38772 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38774 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38775 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38779 this.titleEl.enableDisplayMode();
38780 /** This region's title text element
38781 * @type HTMLElement */
38782 this.titleTextEl = this.titleEl.dom.firstChild;
38783 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38785 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38786 this.closeBtn.enableDisplayMode();
38787 this.closeBtn.on("click", this.closeClicked, this);
38788 this.closeBtn.hide();
38790 this.createBody(this.config);
38791 if(this.config.hideWhenEmpty){
38793 this.on("paneladded", this.validateVisibility, this);
38794 this.on("panelremoved", this.validateVisibility, this);
38796 if(this.autoScroll){
38797 this.bodyEl.setStyle("overflow", "auto");
38799 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38801 //if(c.titlebar !== false){
38802 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38803 this.titleEl.hide();
38805 this.titleEl.show();
38806 if(this.config.title){
38807 this.titleTextEl.innerHTML = this.config.title;
38811 if(this.config.collapsed){
38812 this.collapse(true);
38814 if(this.config.hidden){
38818 if (this.unrendered_panels && this.unrendered_panels.length) {
38819 for (var i =0;i< this.unrendered_panels.length; i++) {
38820 this.add(this.unrendered_panels[i]);
38822 this.unrendered_panels = null;
38828 applyConfig : function(c)
38831 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38832 var dh = Roo.DomHelper;
38833 if(c.titlebar !== false){
38834 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38835 this.collapseBtn.on("click", this.collapse, this);
38836 this.collapseBtn.enableDisplayMode();
38838 if(c.showPin === true || this.showPin){
38839 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38840 this.stickBtn.enableDisplayMode();
38841 this.stickBtn.on("click", this.expand, this);
38842 this.stickBtn.hide();
38847 /** This region's collapsed element
38848 * @type Roo.Element */
38851 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38852 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38855 if(c.floatable !== false){
38856 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38857 this.collapsedEl.on("click", this.collapseClick, this);
38860 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38861 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38862 id: "message", unselectable: "on", style:{"float":"left"}});
38863 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38865 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38866 this.expandBtn.on("click", this.expand, this);
38870 if(this.collapseBtn){
38871 this.collapseBtn.setVisible(c.collapsible == true);
38874 this.cmargins = c.cmargins || this.cmargins ||
38875 (this.position == "west" || this.position == "east" ?
38876 {top: 0, left: 2, right:2, bottom: 0} :
38877 {top: 2, left: 0, right:0, bottom: 2});
38879 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38882 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38884 this.autoScroll = c.autoScroll || false;
38889 this.duration = c.duration || .30;
38890 this.slideDuration = c.slideDuration || .45;
38895 * Returns true if this region is currently visible.
38896 * @return {Boolean}
38898 isVisible : function(){
38899 return this.visible;
38903 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38904 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
38906 //setCollapsedTitle : function(title){
38907 // title = title || " ";
38908 // if(this.collapsedTitleTextEl){
38909 // this.collapsedTitleTextEl.innerHTML = title;
38913 getBox : function(){
38915 // if(!this.collapsed){
38916 b = this.el.getBox(false, true);
38918 // b = this.collapsedEl.getBox(false, true);
38923 getMargins : function(){
38924 return this.margins;
38925 //return this.collapsed ? this.cmargins : this.margins;
38928 highlight : function(){
38929 this.el.addClass("x-layout-panel-dragover");
38932 unhighlight : function(){
38933 this.el.removeClass("x-layout-panel-dragover");
38936 updateBox : function(box)
38938 if (!this.bodyEl) {
38939 return; // not rendered yet..
38943 if(!this.collapsed){
38944 this.el.dom.style.left = box.x + "px";
38945 this.el.dom.style.top = box.y + "px";
38946 this.updateBody(box.width, box.height);
38948 this.collapsedEl.dom.style.left = box.x + "px";
38949 this.collapsedEl.dom.style.top = box.y + "px";
38950 this.collapsedEl.setSize(box.width, box.height);
38953 this.tabs.autoSizeTabs();
38957 updateBody : function(w, h)
38960 this.el.setWidth(w);
38961 w -= this.el.getBorderWidth("rl");
38962 if(this.config.adjustments){
38963 w += this.config.adjustments[0];
38966 if(h !== null && h > 0){
38967 this.el.setHeight(h);
38968 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38969 h -= this.el.getBorderWidth("tb");
38970 if(this.config.adjustments){
38971 h += this.config.adjustments[1];
38973 this.bodyEl.setHeight(h);
38975 h = this.tabs.syncHeight(h);
38978 if(this.panelSize){
38979 w = w !== null ? w : this.panelSize.width;
38980 h = h !== null ? h : this.panelSize.height;
38982 if(this.activePanel){
38983 var el = this.activePanel.getEl();
38984 w = w !== null ? w : el.getWidth();
38985 h = h !== null ? h : el.getHeight();
38986 this.panelSize = {width: w, height: h};
38987 this.activePanel.setSize(w, h);
38989 if(Roo.isIE && this.tabs){
38990 this.tabs.el.repaint();
38995 * Returns the container element for this region.
38996 * @return {Roo.Element}
38998 getEl : function(){
39003 * Hides this region.
39006 //if(!this.collapsed){
39007 this.el.dom.style.left = "-2000px";
39010 // this.collapsedEl.dom.style.left = "-2000px";
39011 // this.collapsedEl.hide();
39013 this.visible = false;
39014 this.fireEvent("visibilitychange", this, false);
39018 * Shows this region if it was previously hidden.
39021 //if(!this.collapsed){
39024 // this.collapsedEl.show();
39026 this.visible = true;
39027 this.fireEvent("visibilitychange", this, true);
39030 closeClicked : function(){
39031 if(this.activePanel){
39032 this.remove(this.activePanel);
39036 collapseClick : function(e){
39038 e.stopPropagation();
39041 e.stopPropagation();
39047 * Collapses this region.
39048 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39051 collapse : function(skipAnim, skipCheck = false){
39052 if(this.collapsed) {
39056 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39058 this.collapsed = true;
39060 this.split.el.hide();
39062 if(this.config.animate && skipAnim !== true){
39063 this.fireEvent("invalidated", this);
39064 this.animateCollapse();
39066 this.el.setLocation(-20000,-20000);
39068 this.collapsedEl.show();
39069 this.fireEvent("collapsed", this);
39070 this.fireEvent("invalidated", this);
39076 animateCollapse : function(){
39081 * Expands this region if it was previously collapsed.
39082 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39083 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39086 expand : function(e, skipAnim){
39088 e.stopPropagation();
39090 if(!this.collapsed || this.el.hasActiveFx()) {
39094 this.afterSlideIn();
39097 this.collapsed = false;
39098 if(this.config.animate && skipAnim !== true){
39099 this.animateExpand();
39103 this.split.el.show();
39105 this.collapsedEl.setLocation(-2000,-2000);
39106 this.collapsedEl.hide();
39107 this.fireEvent("invalidated", this);
39108 this.fireEvent("expanded", this);
39112 animateExpand : function(){
39116 initTabs : function()
39118 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39120 var ts = new Roo.bootstrap.panel.Tabs({
39121 el: this.bodyEl.dom,
39123 tabPosition: this.tabPosition ? this.tabPosition : 'top',
39124 disableTooltips: this.config.disableTabTips,
39125 toolbar : this.config.toolbar
39128 if(this.config.hideTabs){
39129 ts.stripWrap.setDisplayed(false);
39132 ts.resizeTabs = this.config.resizeTabs === true;
39133 ts.minTabWidth = this.config.minTabWidth || 40;
39134 ts.maxTabWidth = this.config.maxTabWidth || 250;
39135 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39136 ts.monitorResize = false;
39137 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39138 ts.bodyEl.addClass('roo-layout-tabs-body');
39139 this.panels.each(this.initPanelAsTab, this);
39142 initPanelAsTab : function(panel){
39143 var ti = this.tabs.addTab(
39147 this.config.closeOnTab && panel.isClosable(),
39150 if(panel.tabTip !== undefined){
39151 ti.setTooltip(panel.tabTip);
39153 ti.on("activate", function(){
39154 this.setActivePanel(panel);
39157 if(this.config.closeOnTab){
39158 ti.on("beforeclose", function(t, e){
39160 this.remove(panel);
39164 panel.tabItem = ti;
39169 updatePanelTitle : function(panel, title)
39171 if(this.activePanel == panel){
39172 this.updateTitle(title);
39175 var ti = this.tabs.getTab(panel.getEl().id);
39177 if(panel.tabTip !== undefined){
39178 ti.setTooltip(panel.tabTip);
39183 updateTitle : function(title){
39184 if(this.titleTextEl && !this.config.title){
39185 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
39189 setActivePanel : function(panel)
39191 panel = this.getPanel(panel);
39192 if(this.activePanel && this.activePanel != panel){
39193 if(this.activePanel.setActiveState(false) === false){
39197 this.activePanel = panel;
39198 panel.setActiveState(true);
39199 if(this.panelSize){
39200 panel.setSize(this.panelSize.width, this.panelSize.height);
39203 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39205 this.updateTitle(panel.getTitle());
39207 this.fireEvent("invalidated", this);
39209 this.fireEvent("panelactivated", this, panel);
39213 * Shows the specified panel.
39214 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39215 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39217 showPanel : function(panel)
39219 panel = this.getPanel(panel);
39222 var tab = this.tabs.getTab(panel.getEl().id);
39223 if(tab.isHidden()){
39224 this.tabs.unhideTab(tab.id);
39228 this.setActivePanel(panel);
39235 * Get the active panel for this region.
39236 * @return {Roo.ContentPanel} The active panel or null
39238 getActivePanel : function(){
39239 return this.activePanel;
39242 validateVisibility : function(){
39243 if(this.panels.getCount() < 1){
39244 this.updateTitle(" ");
39245 this.closeBtn.hide();
39248 if(!this.isVisible()){
39255 * Adds the passed ContentPanel(s) to this region.
39256 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39257 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39259 add : function(panel)
39261 if(arguments.length > 1){
39262 for(var i = 0, len = arguments.length; i < len; i++) {
39263 this.add(arguments[i]);
39268 // if we have not been rendered yet, then we can not really do much of this..
39269 if (!this.bodyEl) {
39270 this.unrendered_panels.push(panel);
39277 if(this.hasPanel(panel)){
39278 this.showPanel(panel);
39281 panel.setRegion(this);
39282 this.panels.add(panel);
39283 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39284 // sinle panel - no tab...?? would it not be better to render it with the tabs,
39285 // and hide them... ???
39286 this.bodyEl.dom.appendChild(panel.getEl().dom);
39287 if(panel.background !== true){
39288 this.setActivePanel(panel);
39290 this.fireEvent("paneladded", this, panel);
39297 this.initPanelAsTab(panel);
39301 if(panel.background !== true){
39302 this.tabs.activate(panel.getEl().id);
39304 this.fireEvent("paneladded", this, panel);
39309 * Hides the tab for the specified panel.
39310 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39312 hidePanel : function(panel){
39313 if(this.tabs && (panel = this.getPanel(panel))){
39314 this.tabs.hideTab(panel.getEl().id);
39319 * Unhides the tab for a previously hidden panel.
39320 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39322 unhidePanel : function(panel){
39323 if(this.tabs && (panel = this.getPanel(panel))){
39324 this.tabs.unhideTab(panel.getEl().id);
39328 clearPanels : function(){
39329 while(this.panels.getCount() > 0){
39330 this.remove(this.panels.first());
39335 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39336 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39337 * @param {Boolean} preservePanel Overrides the config preservePanel option
39338 * @return {Roo.ContentPanel} The panel that was removed
39340 remove : function(panel, preservePanel)
39342 panel = this.getPanel(panel);
39347 this.fireEvent("beforeremove", this, panel, e);
39348 if(e.cancel === true){
39351 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39352 var panelId = panel.getId();
39353 this.panels.removeKey(panelId);
39355 document.body.appendChild(panel.getEl().dom);
39358 this.tabs.removeTab(panel.getEl().id);
39359 }else if (!preservePanel){
39360 this.bodyEl.dom.removeChild(panel.getEl().dom);
39362 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39363 var p = this.panels.first();
39364 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39365 tempEl.appendChild(p.getEl().dom);
39366 this.bodyEl.update("");
39367 this.bodyEl.dom.appendChild(p.getEl().dom);
39369 this.updateTitle(p.getTitle());
39371 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39372 this.setActivePanel(p);
39374 panel.setRegion(null);
39375 if(this.activePanel == panel){
39376 this.activePanel = null;
39378 if(this.config.autoDestroy !== false && preservePanel !== true){
39379 try{panel.destroy();}catch(e){}
39381 this.fireEvent("panelremoved", this, panel);
39386 * Returns the TabPanel component used by this region
39387 * @return {Roo.TabPanel}
39389 getTabs : function(){
39393 createTool : function(parentEl, className){
39394 var btn = Roo.DomHelper.append(parentEl, {
39396 cls: "x-layout-tools-button",
39399 cls: "roo-layout-tools-button-inner " + className,
39403 btn.addClassOnOver("roo-layout-tools-button-over");
39408 * Ext JS Library 1.1.1
39409 * Copyright(c) 2006-2007, Ext JS, LLC.
39411 * Originally Released Under LGPL - original licence link has changed is not relivant.
39414 * <script type="text/javascript">
39420 * @class Roo.SplitLayoutRegion
39421 * @extends Roo.LayoutRegion
39422 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39424 Roo.bootstrap.layout.Split = function(config){
39425 this.cursor = config.cursor;
39426 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39429 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39431 splitTip : "Drag to resize.",
39432 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39433 useSplitTips : false,
39435 applyConfig : function(config){
39436 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39439 onRender : function(ctr,pos) {
39441 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39442 if(!this.config.split){
39447 var splitEl = Roo.DomHelper.append(ctr.dom, {
39449 id: this.el.id + "-split",
39450 cls: "roo-layout-split roo-layout-split-"+this.position,
39453 /** The SplitBar for this region
39454 * @type Roo.SplitBar */
39455 // does not exist yet...
39456 Roo.log([this.position, this.orientation]);
39458 this.split = new Roo.bootstrap.SplitBar({
39459 dragElement : splitEl,
39460 resizingElement: this.el,
39461 orientation : this.orientation
39464 this.split.on("moved", this.onSplitMove, this);
39465 this.split.useShim = this.config.useShim === true;
39466 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39467 if(this.useSplitTips){
39468 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39470 //if(config.collapsible){
39471 // this.split.el.on("dblclick", this.collapse, this);
39474 if(typeof this.config.minSize != "undefined"){
39475 this.split.minSize = this.config.minSize;
39477 if(typeof this.config.maxSize != "undefined"){
39478 this.split.maxSize = this.config.maxSize;
39480 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39481 this.hideSplitter();
39486 getHMaxSize : function(){
39487 var cmax = this.config.maxSize || 10000;
39488 var center = this.mgr.getRegion("center");
39489 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39492 getVMaxSize : function(){
39493 var cmax = this.config.maxSize || 10000;
39494 var center = this.mgr.getRegion("center");
39495 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39498 onSplitMove : function(split, newSize){
39499 this.fireEvent("resized", this, newSize);
39503 * Returns the {@link Roo.SplitBar} for this region.
39504 * @return {Roo.SplitBar}
39506 getSplitBar : function(){
39511 this.hideSplitter();
39512 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39515 hideSplitter : function(){
39517 this.split.el.setLocation(-2000,-2000);
39518 this.split.el.hide();
39524 this.split.el.show();
39526 Roo.bootstrap.layout.Split.superclass.show.call(this);
39529 beforeSlide: function(){
39530 if(Roo.isGecko){// firefox overflow auto bug workaround
39531 this.bodyEl.clip();
39533 this.tabs.bodyEl.clip();
39535 if(this.activePanel){
39536 this.activePanel.getEl().clip();
39538 if(this.activePanel.beforeSlide){
39539 this.activePanel.beforeSlide();
39545 afterSlide : function(){
39546 if(Roo.isGecko){// firefox overflow auto bug workaround
39547 this.bodyEl.unclip();
39549 this.tabs.bodyEl.unclip();
39551 if(this.activePanel){
39552 this.activePanel.getEl().unclip();
39553 if(this.activePanel.afterSlide){
39554 this.activePanel.afterSlide();
39560 initAutoHide : function(){
39561 if(this.autoHide !== false){
39562 if(!this.autoHideHd){
39563 var st = new Roo.util.DelayedTask(this.slideIn, this);
39564 this.autoHideHd = {
39565 "mouseout": function(e){
39566 if(!e.within(this.el, true)){
39570 "mouseover" : function(e){
39576 this.el.on(this.autoHideHd);
39580 clearAutoHide : function(){
39581 if(this.autoHide !== false){
39582 this.el.un("mouseout", this.autoHideHd.mouseout);
39583 this.el.un("mouseover", this.autoHideHd.mouseover);
39587 clearMonitor : function(){
39588 Roo.get(document).un("click", this.slideInIf, this);
39591 // these names are backwards but not changed for compat
39592 slideOut : function(){
39593 if(this.isSlid || this.el.hasActiveFx()){
39596 this.isSlid = true;
39597 if(this.collapseBtn){
39598 this.collapseBtn.hide();
39600 this.closeBtnState = this.closeBtn.getStyle('display');
39601 this.closeBtn.hide();
39603 this.stickBtn.show();
39606 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39607 this.beforeSlide();
39608 this.el.setStyle("z-index", 10001);
39609 this.el.slideIn(this.getSlideAnchor(), {
39610 callback: function(){
39612 this.initAutoHide();
39613 Roo.get(document).on("click", this.slideInIf, this);
39614 this.fireEvent("slideshow", this);
39621 afterSlideIn : function(){
39622 this.clearAutoHide();
39623 this.isSlid = false;
39624 this.clearMonitor();
39625 this.el.setStyle("z-index", "");
39626 if(this.collapseBtn){
39627 this.collapseBtn.show();
39629 this.closeBtn.setStyle('display', this.closeBtnState);
39631 this.stickBtn.hide();
39633 this.fireEvent("slidehide", this);
39636 slideIn : function(cb){
39637 if(!this.isSlid || this.el.hasActiveFx()){
39641 this.isSlid = false;
39642 this.beforeSlide();
39643 this.el.slideOut(this.getSlideAnchor(), {
39644 callback: function(){
39645 this.el.setLeftTop(-10000, -10000);
39647 this.afterSlideIn();
39655 slideInIf : function(e){
39656 if(!e.within(this.el)){
39661 animateCollapse : function(){
39662 this.beforeSlide();
39663 this.el.setStyle("z-index", 20000);
39664 var anchor = this.getSlideAnchor();
39665 this.el.slideOut(anchor, {
39666 callback : function(){
39667 this.el.setStyle("z-index", "");
39668 this.collapsedEl.slideIn(anchor, {duration:.3});
39670 this.el.setLocation(-10000,-10000);
39672 this.fireEvent("collapsed", this);
39679 animateExpand : function(){
39680 this.beforeSlide();
39681 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39682 this.el.setStyle("z-index", 20000);
39683 this.collapsedEl.hide({
39686 this.el.slideIn(this.getSlideAnchor(), {
39687 callback : function(){
39688 this.el.setStyle("z-index", "");
39691 this.split.el.show();
39693 this.fireEvent("invalidated", this);
39694 this.fireEvent("expanded", this);
39722 getAnchor : function(){
39723 return this.anchors[this.position];
39726 getCollapseAnchor : function(){
39727 return this.canchors[this.position];
39730 getSlideAnchor : function(){
39731 return this.sanchors[this.position];
39734 getAlignAdj : function(){
39735 var cm = this.cmargins;
39736 switch(this.position){
39752 getExpandAdj : function(){
39753 var c = this.collapsedEl, cm = this.cmargins;
39754 switch(this.position){
39756 return [-(cm.right+c.getWidth()+cm.left), 0];
39759 return [cm.right+c.getWidth()+cm.left, 0];
39762 return [0, -(cm.top+cm.bottom+c.getHeight())];
39765 return [0, cm.top+cm.bottom+c.getHeight()];
39771 * Ext JS Library 1.1.1
39772 * Copyright(c) 2006-2007, Ext JS, LLC.
39774 * Originally Released Under LGPL - original licence link has changed is not relivant.
39777 * <script type="text/javascript">
39780 * These classes are private internal classes
39782 Roo.bootstrap.layout.Center = function(config){
39783 config.region = "center";
39784 Roo.bootstrap.layout.Region.call(this, config);
39785 this.visible = true;
39786 this.minWidth = config.minWidth || 20;
39787 this.minHeight = config.minHeight || 20;
39790 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39792 // center panel can't be hidden
39796 // center panel can't be hidden
39799 getMinWidth: function(){
39800 return this.minWidth;
39803 getMinHeight: function(){
39804 return this.minHeight;
39818 Roo.bootstrap.layout.North = function(config)
39820 config.region = 'north';
39821 config.cursor = 'n-resize';
39823 Roo.bootstrap.layout.Split.call(this, config);
39827 this.split.placement = Roo.bootstrap.SplitBar.TOP;
39828 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39829 this.split.el.addClass("roo-layout-split-v");
39831 //var size = config.initialSize || config.height;
39832 //if(this.el && typeof size != "undefined"){
39833 // this.el.setHeight(size);
39836 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39838 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39841 onRender : function(ctr, pos)
39843 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39844 var size = this.config.initialSize || this.config.height;
39845 if(this.el && typeof size != "undefined"){
39846 this.el.setHeight(size);
39851 getBox : function(){
39852 if(this.collapsed){
39853 return this.collapsedEl.getBox();
39855 var box = this.el.getBox();
39857 box.height += this.split.el.getHeight();
39862 updateBox : function(box){
39863 if(this.split && !this.collapsed){
39864 box.height -= this.split.el.getHeight();
39865 this.split.el.setLeft(box.x);
39866 this.split.el.setTop(box.y+box.height);
39867 this.split.el.setWidth(box.width);
39869 if(this.collapsed){
39870 this.updateBody(box.width, null);
39872 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39880 Roo.bootstrap.layout.South = function(config){
39881 config.region = 'south';
39882 config.cursor = 's-resize';
39883 Roo.bootstrap.layout.Split.call(this, config);
39885 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39886 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39887 this.split.el.addClass("roo-layout-split-v");
39892 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39893 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39895 onRender : function(ctr, pos)
39897 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39898 var size = this.config.initialSize || this.config.height;
39899 if(this.el && typeof size != "undefined"){
39900 this.el.setHeight(size);
39905 getBox : function(){
39906 if(this.collapsed){
39907 return this.collapsedEl.getBox();
39909 var box = this.el.getBox();
39911 var sh = this.split.el.getHeight();
39918 updateBox : function(box){
39919 if(this.split && !this.collapsed){
39920 var sh = this.split.el.getHeight();
39923 this.split.el.setLeft(box.x);
39924 this.split.el.setTop(box.y-sh);
39925 this.split.el.setWidth(box.width);
39927 if(this.collapsed){
39928 this.updateBody(box.width, null);
39930 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39934 Roo.bootstrap.layout.East = function(config){
39935 config.region = "east";
39936 config.cursor = "e-resize";
39937 Roo.bootstrap.layout.Split.call(this, config);
39939 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39940 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39941 this.split.el.addClass("roo-layout-split-h");
39945 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39946 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39948 onRender : function(ctr, pos)
39950 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39951 var size = this.config.initialSize || this.config.width;
39952 if(this.el && typeof size != "undefined"){
39953 this.el.setWidth(size);
39958 getBox : function(){
39959 if(this.collapsed){
39960 return this.collapsedEl.getBox();
39962 var box = this.el.getBox();
39964 var sw = this.split.el.getWidth();
39971 updateBox : function(box){
39972 if(this.split && !this.collapsed){
39973 var sw = this.split.el.getWidth();
39975 this.split.el.setLeft(box.x);
39976 this.split.el.setTop(box.y);
39977 this.split.el.setHeight(box.height);
39980 if(this.collapsed){
39981 this.updateBody(null, box.height);
39983 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39987 Roo.bootstrap.layout.West = function(config){
39988 config.region = "west";
39989 config.cursor = "w-resize";
39991 Roo.bootstrap.layout.Split.call(this, config);
39993 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39994 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39995 this.split.el.addClass("roo-layout-split-h");
39999 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40000 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40002 onRender: function(ctr, pos)
40004 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40005 var size = this.config.initialSize || this.config.width;
40006 if(typeof size != "undefined"){
40007 this.el.setWidth(size);
40011 getBox : function(){
40012 if(this.collapsed){
40013 return this.collapsedEl.getBox();
40015 var box = this.el.getBox();
40016 if (box.width == 0) {
40017 box.width = this.config.width; // kludge?
40020 box.width += this.split.el.getWidth();
40025 updateBox : function(box){
40026 if(this.split && !this.collapsed){
40027 var sw = this.split.el.getWidth();
40029 this.split.el.setLeft(box.x+box.width);
40030 this.split.el.setTop(box.y);
40031 this.split.el.setHeight(box.height);
40033 if(this.collapsed){
40034 this.updateBody(null, box.height);
40036 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40038 });Roo.namespace("Roo.bootstrap.panel");/*
40040 * Ext JS Library 1.1.1
40041 * Copyright(c) 2006-2007, Ext JS, LLC.
40043 * Originally Released Under LGPL - original licence link has changed is not relivant.
40046 * <script type="text/javascript">
40049 * @class Roo.ContentPanel
40050 * @extends Roo.util.Observable
40051 * A basic ContentPanel element.
40052 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
40053 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
40054 * @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
40055 * @cfg {Boolean} closable True if the panel can be closed/removed
40056 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
40057 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40058 * @cfg {Toolbar} toolbar A toolbar for this panel
40059 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
40060 * @cfg {String} title The title for this panel
40061 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40062 * @cfg {String} url Calls {@link #setUrl} with this value
40063 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40064 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
40065 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
40066 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
40067 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
40068 * @cfg {Boolean} badges render the badges
40069 * @cfg {String} cls extra classes to use
40070 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40073 * Create a new ContentPanel.
40074 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40075 * @param {String/Object} config A string to set only the title or a config object
40076 * @param {String} content (optional) Set the HTML content for this panel
40077 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40079 Roo.bootstrap.panel.Content = function( config){
40081 this.tpl = config.tpl || false;
40083 var el = config.el;
40084 var content = config.content;
40086 if(config.autoCreate){ // xtype is available if this is called from factory
40089 this.el = Roo.get(el);
40090 if(!this.el && config && config.autoCreate){
40091 if(typeof config.autoCreate == "object"){
40092 if(!config.autoCreate.id){
40093 config.autoCreate.id = config.id||el;
40095 this.el = Roo.DomHelper.append(document.body,
40096 config.autoCreate, true);
40100 cls: (config.cls || '') +
40101 (config.background ? ' bg-' + config.background : '') +
40102 " roo-layout-inactive-content",
40105 if (config.iframe) {
40109 style : 'border: 0px',
40110 src : 'about:blank'
40116 elcfg.html = config.html;
40120 this.el = Roo.DomHelper.append(document.body, elcfg , true);
40121 if (config.iframe) {
40122 this.iframeEl = this.el.select('iframe',true).first();
40127 this.closable = false;
40128 this.loaded = false;
40129 this.active = false;
40132 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40134 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40136 this.wrapEl = this.el; //this.el.wrap();
40138 if (config.toolbar.items) {
40139 ti = config.toolbar.items ;
40140 delete config.toolbar.items ;
40144 this.toolbar.render(this.wrapEl, 'before');
40145 for(var i =0;i < ti.length;i++) {
40146 // Roo.log(['add child', items[i]]);
40147 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40149 this.toolbar.items = nitems;
40150 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40151 delete config.toolbar;
40155 // xtype created footer. - not sure if will work as we normally have to render first..
40156 if (this.footer && !this.footer.el && this.footer.xtype) {
40157 if (!this.wrapEl) {
40158 this.wrapEl = this.el.wrap();
40161 this.footer.container = this.wrapEl.createChild();
40163 this.footer = Roo.factory(this.footer, Roo);
40168 if(typeof config == "string"){
40169 this.title = config;
40171 Roo.apply(this, config);
40175 this.resizeEl = Roo.get(this.resizeEl, true);
40177 this.resizeEl = this.el;
40179 // handle view.xtype
40187 * Fires when this panel is activated.
40188 * @param {Roo.ContentPanel} this
40192 * @event deactivate
40193 * Fires when this panel is activated.
40194 * @param {Roo.ContentPanel} this
40196 "deactivate" : true,
40200 * Fires when this panel is resized if fitToFrame is true.
40201 * @param {Roo.ContentPanel} this
40202 * @param {Number} width The width after any component adjustments
40203 * @param {Number} height The height after any component adjustments
40209 * Fires when this tab is created
40210 * @param {Roo.ContentPanel} this
40221 if(this.autoScroll && !this.iframe){
40222 this.resizeEl.setStyle("overflow", "auto");
40224 // fix randome scrolling
40225 //this.el.on('scroll', function() {
40226 // Roo.log('fix random scolling');
40227 // this.scrollTo('top',0);
40230 content = content || this.content;
40232 this.setContent(content);
40234 if(config && config.url){
40235 this.setUrl(this.url, this.params, this.loadOnce);
40240 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40242 if (this.view && typeof(this.view.xtype) != 'undefined') {
40243 this.view.el = this.el.appendChild(document.createElement("div"));
40244 this.view = Roo.factory(this.view);
40245 this.view.render && this.view.render(false, '');
40249 this.fireEvent('render', this);
40252 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40262 setRegion : function(region){
40263 this.region = region;
40264 this.setActiveClass(region && !this.background);
40268 setActiveClass: function(state)
40271 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40272 this.el.setStyle('position','relative');
40274 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40275 this.el.setStyle('position', 'absolute');
40280 * Returns the toolbar for this Panel if one was configured.
40281 * @return {Roo.Toolbar}
40283 getToolbar : function(){
40284 return this.toolbar;
40287 setActiveState : function(active)
40289 this.active = active;
40290 this.setActiveClass(active);
40292 if(this.fireEvent("deactivate", this) === false){
40297 this.fireEvent("activate", this);
40301 * Updates this panel's element (not for iframe)
40302 * @param {String} content The new content
40303 * @param {Boolean} loadScripts (optional) true to look for and process scripts
40305 setContent : function(content, loadScripts){
40310 this.el.update(content, loadScripts);
40313 ignoreResize : function(w, h){
40314 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40317 this.lastSize = {width: w, height: h};
40322 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40323 * @return {Roo.UpdateManager} The UpdateManager
40325 getUpdateManager : function(){
40329 return this.el.getUpdateManager();
40332 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40333 * Does not work with IFRAME contents
40334 * @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:
40337 url: "your-url.php",
40338 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40339 callback: yourFunction,
40340 scope: yourObject, //(optional scope)
40343 text: "Loading...",
40349 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40350 * 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.
40351 * @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}
40352 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40353 * @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.
40354 * @return {Roo.ContentPanel} this
40362 var um = this.el.getUpdateManager();
40363 um.update.apply(um, arguments);
40369 * 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.
40370 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40371 * @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)
40372 * @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)
40373 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40375 setUrl : function(url, params, loadOnce){
40377 this.iframeEl.dom.src = url;
40381 if(this.refreshDelegate){
40382 this.removeListener("activate", this.refreshDelegate);
40384 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40385 this.on("activate", this.refreshDelegate);
40386 return this.el.getUpdateManager();
40389 _handleRefresh : function(url, params, loadOnce){
40390 if(!loadOnce || !this.loaded){
40391 var updater = this.el.getUpdateManager();
40392 updater.update(url, params, this._setLoaded.createDelegate(this));
40396 _setLoaded : function(){
40397 this.loaded = true;
40401 * Returns this panel's id
40404 getId : function(){
40409 * Returns this panel's element - used by regiosn to add.
40410 * @return {Roo.Element}
40412 getEl : function(){
40413 return this.wrapEl || this.el;
40418 adjustForComponents : function(width, height)
40420 //Roo.log('adjustForComponents ');
40421 if(this.resizeEl != this.el){
40422 width -= this.el.getFrameWidth('lr');
40423 height -= this.el.getFrameWidth('tb');
40426 var te = this.toolbar.getEl();
40427 te.setWidth(width);
40428 height -= te.getHeight();
40431 var te = this.footer.getEl();
40432 te.setWidth(width);
40433 height -= te.getHeight();
40437 if(this.adjustments){
40438 width += this.adjustments[0];
40439 height += this.adjustments[1];
40441 return {"width": width, "height": height};
40444 setSize : function(width, height){
40445 if(this.fitToFrame && !this.ignoreResize(width, height)){
40446 if(this.fitContainer && this.resizeEl != this.el){
40447 this.el.setSize(width, height);
40449 var size = this.adjustForComponents(width, height);
40451 this.iframeEl.setSize(width,height);
40454 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40455 this.fireEvent('resize', this, size.width, size.height);
40462 * Returns this panel's title
40465 getTitle : function(){
40467 if (typeof(this.title) != 'object') {
40472 for (var k in this.title) {
40473 if (!this.title.hasOwnProperty(k)) {
40477 if (k.indexOf('-') >= 0) {
40478 var s = k.split('-');
40479 for (var i = 0; i<s.length; i++) {
40480 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40483 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40490 * Set this panel's title
40491 * @param {String} title
40493 setTitle : function(title){
40494 this.title = title;
40496 this.region.updatePanelTitle(this, title);
40501 * Returns true is this panel was configured to be closable
40502 * @return {Boolean}
40504 isClosable : function(){
40505 return this.closable;
40508 beforeSlide : function(){
40510 this.resizeEl.clip();
40513 afterSlide : function(){
40515 this.resizeEl.unclip();
40519 * Force a content refresh from the URL specified in the {@link #setUrl} method.
40520 * Will fail silently if the {@link #setUrl} method has not been called.
40521 * This does not activate the panel, just updates its content.
40523 refresh : function(){
40524 if(this.refreshDelegate){
40525 this.loaded = false;
40526 this.refreshDelegate();
40531 * Destroys this panel
40533 destroy : function(){
40534 this.el.removeAllListeners();
40535 var tempEl = document.createElement("span");
40536 tempEl.appendChild(this.el.dom);
40537 tempEl.innerHTML = "";
40543 * form - if the content panel contains a form - this is a reference to it.
40544 * @type {Roo.form.Form}
40548 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40549 * This contains a reference to it.
40555 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40565 * @param {Object} cfg Xtype definition of item to add.
40569 getChildContainer: function () {
40570 return this.getEl();
40575 var ret = new Roo.factory(cfg);
40580 if (cfg.xtype.match(/^Form$/)) {
40583 //if (this.footer) {
40584 // el = this.footer.container.insertSibling(false, 'before');
40586 el = this.el.createChild();
40589 this.form = new Roo.form.Form(cfg);
40592 if ( this.form.allItems.length) {
40593 this.form.render(el.dom);
40597 // should only have one of theses..
40598 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40599 // views.. should not be just added - used named prop 'view''
40601 cfg.el = this.el.appendChild(document.createElement("div"));
40604 var ret = new Roo.factory(cfg);
40606 ret.render && ret.render(false, ''); // render blank..
40616 * @class Roo.bootstrap.panel.Grid
40617 * @extends Roo.bootstrap.panel.Content
40619 * Create a new GridPanel.
40620 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40621 * @param {Object} config A the config object
40627 Roo.bootstrap.panel.Grid = function(config)
40631 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40632 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40634 config.el = this.wrapper;
40635 //this.el = this.wrapper;
40637 if (config.container) {
40638 // ctor'ed from a Border/panel.grid
40641 this.wrapper.setStyle("overflow", "hidden");
40642 this.wrapper.addClass('roo-grid-container');
40647 if(config.toolbar){
40648 var tool_el = this.wrapper.createChild();
40649 this.toolbar = Roo.factory(config.toolbar);
40651 if (config.toolbar.items) {
40652 ti = config.toolbar.items ;
40653 delete config.toolbar.items ;
40657 this.toolbar.render(tool_el);
40658 for(var i =0;i < ti.length;i++) {
40659 // Roo.log(['add child', items[i]]);
40660 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40662 this.toolbar.items = nitems;
40664 delete config.toolbar;
40667 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40668 config.grid.scrollBody = true;;
40669 config.grid.monitorWindowResize = false; // turn off autosizing
40670 config.grid.autoHeight = false;
40671 config.grid.autoWidth = false;
40673 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40675 if (config.background) {
40676 // render grid on panel activation (if panel background)
40677 this.on('activate', function(gp) {
40678 if (!gp.grid.rendered) {
40679 gp.grid.render(this.wrapper);
40680 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40685 this.grid.render(this.wrapper);
40686 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40689 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40690 // ??? needed ??? config.el = this.wrapper;
40695 // xtype created footer. - not sure if will work as we normally have to render first..
40696 if (this.footer && !this.footer.el && this.footer.xtype) {
40698 var ctr = this.grid.getView().getFooterPanel(true);
40699 this.footer.dataSource = this.grid.dataSource;
40700 this.footer = Roo.factory(this.footer, Roo);
40701 this.footer.render(ctr);
40711 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40712 getId : function(){
40713 return this.grid.id;
40717 * Returns the grid for this panel
40718 * @return {Roo.bootstrap.Table}
40720 getGrid : function(){
40724 setSize : function(width, height){
40725 if(!this.ignoreResize(width, height)){
40726 var grid = this.grid;
40727 var size = this.adjustForComponents(width, height);
40728 // tfoot is not a footer?
40731 var gridel = grid.getGridEl();
40732 gridel.setSize(size.width, size.height);
40734 var tbd = grid.getGridEl().select('tbody', true).first();
40735 var thd = grid.getGridEl().select('thead',true).first();
40736 var tbf= grid.getGridEl().select('tfoot', true).first();
40739 size.height -= tbf.getHeight();
40742 size.height -= thd.getHeight();
40745 tbd.setSize(size.width, size.height );
40746 // this is for the account management tab -seems to work there.
40747 var thd = grid.getGridEl().select('thead',true).first();
40749 // tbd.setSize(size.width, size.height - thd.getHeight());
40758 beforeSlide : function(){
40759 this.grid.getView().scroller.clip();
40762 afterSlide : function(){
40763 this.grid.getView().scroller.unclip();
40766 destroy : function(){
40767 this.grid.destroy();
40769 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
40774 * @class Roo.bootstrap.panel.Nest
40775 * @extends Roo.bootstrap.panel.Content
40777 * Create a new Panel, that can contain a layout.Border.
40780 * @param {Roo.BorderLayout} layout The layout for this panel
40781 * @param {String/Object} config A string to set only the title or a config object
40783 Roo.bootstrap.panel.Nest = function(config)
40785 // construct with only one argument..
40786 /* FIXME - implement nicer consturctors
40787 if (layout.layout) {
40789 layout = config.layout;
40790 delete config.layout;
40792 if (layout.xtype && !layout.getEl) {
40793 // then layout needs constructing..
40794 layout = Roo.factory(layout, Roo);
40798 config.el = config.layout.getEl();
40800 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40802 config.layout.monitorWindowResize = false; // turn off autosizing
40803 this.layout = config.layout;
40804 this.layout.getEl().addClass("roo-layout-nested-layout");
40805 this.layout.parent = this;
40812 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40814 setSize : function(width, height){
40815 if(!this.ignoreResize(width, height)){
40816 var size = this.adjustForComponents(width, height);
40817 var el = this.layout.getEl();
40818 if (size.height < 1) {
40819 el.setWidth(size.width);
40821 el.setSize(size.width, size.height);
40823 var touch = el.dom.offsetWidth;
40824 this.layout.layout();
40825 // ie requires a double layout on the first pass
40826 if(Roo.isIE && !this.initialized){
40827 this.initialized = true;
40828 this.layout.layout();
40833 // activate all subpanels if not currently active..
40835 setActiveState : function(active){
40836 this.active = active;
40837 this.setActiveClass(active);
40840 this.fireEvent("deactivate", this);
40844 this.fireEvent("activate", this);
40845 // not sure if this should happen before or after..
40846 if (!this.layout) {
40847 return; // should not happen..
40850 for (var r in this.layout.regions) {
40851 reg = this.layout.getRegion(r);
40852 if (reg.getActivePanel()) {
40853 //reg.showPanel(reg.getActivePanel()); // force it to activate..
40854 reg.setActivePanel(reg.getActivePanel());
40857 if (!reg.panels.length) {
40860 reg.showPanel(reg.getPanel(0));
40869 * Returns the nested BorderLayout for this panel
40870 * @return {Roo.BorderLayout}
40872 getLayout : function(){
40873 return this.layout;
40877 * Adds a xtype elements to the layout of the nested panel
40881 xtype : 'ContentPanel',
40888 xtype : 'NestedLayoutPanel',
40894 items : [ ... list of content panels or nested layout panels.. ]
40898 * @param {Object} cfg Xtype definition of item to add.
40900 addxtype : function(cfg) {
40901 return this.layout.addxtype(cfg);
40906 * Ext JS Library 1.1.1
40907 * Copyright(c) 2006-2007, Ext JS, LLC.
40909 * Originally Released Under LGPL - original licence link has changed is not relivant.
40912 * <script type="text/javascript">
40915 * @class Roo.TabPanel
40916 * @extends Roo.util.Observable
40917 * A lightweight tab container.
40921 // basic tabs 1, built from existing content
40922 var tabs = new Roo.TabPanel("tabs1");
40923 tabs.addTab("script", "View Script");
40924 tabs.addTab("markup", "View Markup");
40925 tabs.activate("script");
40927 // more advanced tabs, built from javascript
40928 var jtabs = new Roo.TabPanel("jtabs");
40929 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40931 // set up the UpdateManager
40932 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40933 var updater = tab2.getUpdateManager();
40934 updater.setDefaultUrl("ajax1.htm");
40935 tab2.on('activate', updater.refresh, updater, true);
40937 // Use setUrl for Ajax loading
40938 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40939 tab3.setUrl("ajax2.htm", null, true);
40942 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40945 jtabs.activate("jtabs-1");
40948 * Create a new TabPanel.
40949 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40950 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40952 Roo.bootstrap.panel.Tabs = function(config){
40954 * The container element for this TabPanel.
40955 * @type Roo.Element
40957 this.el = Roo.get(config.el);
40960 if(typeof config == "boolean"){
40961 this.tabPosition = config ? "bottom" : "top";
40963 Roo.apply(this, config);
40967 if(this.tabPosition == "bottom"){
40968 // if tabs are at the bottom = create the body first.
40969 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40970 this.el.addClass("roo-tabs-bottom");
40972 // next create the tabs holders
40974 if (this.tabPosition == "west"){
40976 var reg = this.region; // fake it..
40978 if (!reg.mgr.parent) {
40981 reg = reg.mgr.parent.region;
40983 Roo.log("got nest?");
40985 if (reg.mgr.getRegion('west')) {
40986 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40987 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40988 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40989 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40990 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40998 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40999 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41000 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41001 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41006 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41009 // finally - if tabs are at the top, then create the body last..
41010 if(this.tabPosition != "bottom"){
41011 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41012 * @type Roo.Element
41014 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41015 this.el.addClass("roo-tabs-top");
41019 this.bodyEl.setStyle("position", "relative");
41021 this.active = null;
41022 this.activateDelegate = this.activate.createDelegate(this);
41027 * Fires when the active tab changes
41028 * @param {Roo.TabPanel} this
41029 * @param {Roo.TabPanelItem} activePanel The new active tab
41033 * @event beforetabchange
41034 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41035 * @param {Roo.TabPanel} this
41036 * @param {Object} e Set cancel to true on this object to cancel the tab change
41037 * @param {Roo.TabPanelItem} tab The tab being changed to
41039 "beforetabchange" : true
41042 Roo.EventManager.onWindowResize(this.onResize, this);
41043 this.cpad = this.el.getPadding("lr");
41044 this.hiddenCount = 0;
41047 // toolbar on the tabbar support...
41048 if (this.toolbar) {
41049 alert("no toolbar support yet");
41050 this.toolbar = false;
41052 var tcfg = this.toolbar;
41053 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
41054 this.toolbar = new Roo.Toolbar(tcfg);
41055 if (Roo.isSafari) {
41056 var tbl = tcfg.container.child('table', true);
41057 tbl.setAttribute('width', '100%');
41065 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41068 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41070 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41072 tabPosition : "top",
41074 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41076 currentTabWidth : 0,
41078 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41082 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41086 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41088 preferredTabWidth : 175,
41090 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41092 resizeTabs : false,
41094 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41096 monitorResize : true,
41098 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
41100 toolbar : false, // set by caller..
41102 region : false, /// set by caller
41104 disableTooltips : true, // not used yet...
41107 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41108 * @param {String} id The id of the div to use <b>or create</b>
41109 * @param {String} text The text for the tab
41110 * @param {String} content (optional) Content to put in the TabPanelItem body
41111 * @param {Boolean} closable (optional) True to create a close icon on the tab
41112 * @return {Roo.TabPanelItem} The created TabPanelItem
41114 addTab : function(id, text, content, closable, tpl)
41116 var item = new Roo.bootstrap.panel.TabItem({
41120 closable : closable,
41123 this.addTabItem(item);
41125 item.setContent(content);
41131 * Returns the {@link Roo.TabPanelItem} with the specified id/index
41132 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41133 * @return {Roo.TabPanelItem}
41135 getTab : function(id){
41136 return this.items[id];
41140 * Hides the {@link Roo.TabPanelItem} with the specified id/index
41141 * @param {String/Number} id The id or index of the TabPanelItem to hide.
41143 hideTab : function(id){
41144 var t = this.items[id];
41147 this.hiddenCount++;
41148 this.autoSizeTabs();
41153 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41154 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41156 unhideTab : function(id){
41157 var t = this.items[id];
41159 t.setHidden(false);
41160 this.hiddenCount--;
41161 this.autoSizeTabs();
41166 * Adds an existing {@link Roo.TabPanelItem}.
41167 * @param {Roo.TabPanelItem} item The TabPanelItem to add
41169 addTabItem : function(item)
41171 this.items[item.id] = item;
41172 this.items.push(item);
41173 this.autoSizeTabs();
41174 // if(this.resizeTabs){
41175 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41176 // this.autoSizeTabs();
41178 // item.autoSize();
41183 * Removes a {@link Roo.TabPanelItem}.
41184 * @param {String/Number} id The id or index of the TabPanelItem to remove.
41186 removeTab : function(id){
41187 var items = this.items;
41188 var tab = items[id];
41189 if(!tab) { return; }
41190 var index = items.indexOf(tab);
41191 if(this.active == tab && items.length > 1){
41192 var newTab = this.getNextAvailable(index);
41197 this.stripEl.dom.removeChild(tab.pnode.dom);
41198 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41199 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41201 items.splice(index, 1);
41202 delete this.items[tab.id];
41203 tab.fireEvent("close", tab);
41204 tab.purgeListeners();
41205 this.autoSizeTabs();
41208 getNextAvailable : function(start){
41209 var items = this.items;
41211 // look for a next tab that will slide over to
41212 // replace the one being removed
41213 while(index < items.length){
41214 var item = items[++index];
41215 if(item && !item.isHidden()){
41219 // if one isn't found select the previous tab (on the left)
41222 var item = items[--index];
41223 if(item && !item.isHidden()){
41231 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41232 * @param {String/Number} id The id or index of the TabPanelItem to disable.
41234 disableTab : function(id){
41235 var tab = this.items[id];
41236 if(tab && this.active != tab){
41242 * Enables a {@link Roo.TabPanelItem} that is disabled.
41243 * @param {String/Number} id The id or index of the TabPanelItem to enable.
41245 enableTab : function(id){
41246 var tab = this.items[id];
41251 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41252 * @param {String/Number} id The id or index of the TabPanelItem to activate.
41253 * @return {Roo.TabPanelItem} The TabPanelItem.
41255 activate : function(id)
41257 //Roo.log('activite:' + id);
41259 var tab = this.items[id];
41263 if(tab == this.active || tab.disabled){
41267 this.fireEvent("beforetabchange", this, e, tab);
41268 if(e.cancel !== true && !tab.disabled){
41270 this.active.hide();
41272 this.active = this.items[id];
41273 this.active.show();
41274 this.fireEvent("tabchange", this, this.active);
41280 * Gets the active {@link Roo.TabPanelItem}.
41281 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41283 getActiveTab : function(){
41284 return this.active;
41288 * Updates the tab body element to fit the height of the container element
41289 * for overflow scrolling
41290 * @param {Number} targetHeight (optional) Override the starting height from the elements height
41292 syncHeight : function(targetHeight){
41293 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41294 var bm = this.bodyEl.getMargins();
41295 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41296 this.bodyEl.setHeight(newHeight);
41300 onResize : function(){
41301 if(this.monitorResize){
41302 this.autoSizeTabs();
41307 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41309 beginUpdate : function(){
41310 this.updating = true;
41314 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41316 endUpdate : function(){
41317 this.updating = false;
41318 this.autoSizeTabs();
41322 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41324 autoSizeTabs : function()
41326 var count = this.items.length;
41327 var vcount = count - this.hiddenCount;
41330 this.stripEl.hide();
41332 this.stripEl.show();
41335 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41340 var w = Math.max(this.el.getWidth() - this.cpad, 10);
41341 var availWidth = Math.floor(w / vcount);
41342 var b = this.stripBody;
41343 if(b.getWidth() > w){
41344 var tabs = this.items;
41345 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41346 if(availWidth < this.minTabWidth){
41347 /*if(!this.sleft){ // incomplete scrolling code
41348 this.createScrollButtons();
41351 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41354 if(this.currentTabWidth < this.preferredTabWidth){
41355 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41361 * Returns the number of tabs in this TabPanel.
41364 getCount : function(){
41365 return this.items.length;
41369 * Resizes all the tabs to the passed width
41370 * @param {Number} The new width
41372 setTabWidth : function(width){
41373 this.currentTabWidth = width;
41374 for(var i = 0, len = this.items.length; i < len; i++) {
41375 if(!this.items[i].isHidden()) {
41376 this.items[i].setWidth(width);
41382 * Destroys this TabPanel
41383 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41385 destroy : function(removeEl){
41386 Roo.EventManager.removeResizeListener(this.onResize, this);
41387 for(var i = 0, len = this.items.length; i < len; i++){
41388 this.items[i].purgeListeners();
41390 if(removeEl === true){
41391 this.el.update("");
41396 createStrip : function(container)
41398 var strip = document.createElement("nav");
41399 strip.className = Roo.bootstrap.version == 4 ?
41400 "navbar-light bg-light" :
41401 "navbar navbar-default"; //"x-tabs-wrap";
41402 container.appendChild(strip);
41406 createStripList : function(strip)
41408 // div wrapper for retard IE
41409 // returns the "tr" element.
41410 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41411 //'<div class="x-tabs-strip-wrap">'+
41412 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41413 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41414 return strip.firstChild; //.firstChild.firstChild.firstChild;
41416 createBody : function(container)
41418 var body = document.createElement("div");
41419 Roo.id(body, "tab-body");
41420 //Roo.fly(body).addClass("x-tabs-body");
41421 Roo.fly(body).addClass("tab-content");
41422 container.appendChild(body);
41425 createItemBody :function(bodyEl, id){
41426 var body = Roo.getDom(id);
41428 body = document.createElement("div");
41431 //Roo.fly(body).addClass("x-tabs-item-body");
41432 Roo.fly(body).addClass("tab-pane");
41433 bodyEl.insertBefore(body, bodyEl.firstChild);
41437 createStripElements : function(stripEl, text, closable, tpl)
41439 var td = document.createElement("li"); // was td..
41440 td.className = 'nav-item';
41442 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41445 stripEl.appendChild(td);
41447 td.className = "x-tabs-closable";
41448 if(!this.closeTpl){
41449 this.closeTpl = new Roo.Template(
41450 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41451 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41452 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41455 var el = this.closeTpl.overwrite(td, {"text": text});
41456 var close = el.getElementsByTagName("div")[0];
41457 var inner = el.getElementsByTagName("em")[0];
41458 return {"el": el, "close": close, "inner": inner};
41461 // not sure what this is..
41462 // if(!this.tabTpl){
41463 //this.tabTpl = new Roo.Template(
41464 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41465 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41467 // this.tabTpl = new Roo.Template(
41468 // '<a href="#">' +
41469 // '<span unselectable="on"' +
41470 // (this.disableTooltips ? '' : ' title="{text}"') +
41471 // ' >{text}</span></a>'
41477 var template = tpl || this.tabTpl || false;
41480 template = new Roo.Template(
41481 Roo.bootstrap.version == 4 ?
41483 '<a class="nav-link" href="#" unselectable="on"' +
41484 (this.disableTooltips ? '' : ' title="{text}"') +
41487 '<a class="nav-link" href="#">' +
41488 '<span unselectable="on"' +
41489 (this.disableTooltips ? '' : ' title="{text}"') +
41490 ' >{text}</span></a>'
41495 switch (typeof(template)) {
41499 template = new Roo.Template(template);
41505 var el = template.overwrite(td, {"text": text});
41507 var inner = el.getElementsByTagName("span")[0];
41509 return {"el": el, "inner": inner};
41517 * @class Roo.TabPanelItem
41518 * @extends Roo.util.Observable
41519 * Represents an individual item (tab plus body) in a TabPanel.
41520 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41521 * @param {String} id The id of this TabPanelItem
41522 * @param {String} text The text for the tab of this TabPanelItem
41523 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41525 Roo.bootstrap.panel.TabItem = function(config){
41527 * The {@link Roo.TabPanel} this TabPanelItem belongs to
41528 * @type Roo.TabPanel
41530 this.tabPanel = config.panel;
41532 * The id for this TabPanelItem
41535 this.id = config.id;
41537 this.disabled = false;
41539 this.text = config.text;
41541 this.loaded = false;
41542 this.closable = config.closable;
41545 * The body element for this TabPanelItem.
41546 * @type Roo.Element
41548 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41549 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41550 this.bodyEl.setStyle("display", "block");
41551 this.bodyEl.setStyle("zoom", "1");
41552 //this.hideAction();
41554 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41556 this.el = Roo.get(els.el);
41557 this.inner = Roo.get(els.inner, true);
41558 this.textEl = Roo.bootstrap.version == 4 ?
41559 this.el : Roo.get(this.el.dom.firstChild, true);
41561 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41562 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41565 // this.el.on("mousedown", this.onTabMouseDown, this);
41566 this.el.on("click", this.onTabClick, this);
41568 if(config.closable){
41569 var c = Roo.get(els.close, true);
41570 c.dom.title = this.closeText;
41571 c.addClassOnOver("close-over");
41572 c.on("click", this.closeClick, this);
41578 * Fires when this tab becomes the active tab.
41579 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41580 * @param {Roo.TabPanelItem} this
41584 * @event beforeclose
41585 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41586 * @param {Roo.TabPanelItem} this
41587 * @param {Object} e Set cancel to true on this object to cancel the close.
41589 "beforeclose": true,
41592 * Fires when this tab is closed.
41593 * @param {Roo.TabPanelItem} this
41597 * @event deactivate
41598 * Fires when this tab is no longer the active tab.
41599 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41600 * @param {Roo.TabPanelItem} this
41602 "deactivate" : true
41604 this.hidden = false;
41606 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41609 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41611 purgeListeners : function(){
41612 Roo.util.Observable.prototype.purgeListeners.call(this);
41613 this.el.removeAllListeners();
41616 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41619 this.status_node.addClass("active");
41622 this.tabPanel.stripWrap.repaint();
41624 this.fireEvent("activate", this.tabPanel, this);
41628 * Returns true if this tab is the active tab.
41629 * @return {Boolean}
41631 isActive : function(){
41632 return this.tabPanel.getActiveTab() == this;
41636 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41639 this.status_node.removeClass("active");
41641 this.fireEvent("deactivate", this.tabPanel, this);
41644 hideAction : function(){
41645 this.bodyEl.hide();
41646 this.bodyEl.setStyle("position", "absolute");
41647 this.bodyEl.setLeft("-20000px");
41648 this.bodyEl.setTop("-20000px");
41651 showAction : function(){
41652 this.bodyEl.setStyle("position", "relative");
41653 this.bodyEl.setTop("");
41654 this.bodyEl.setLeft("");
41655 this.bodyEl.show();
41659 * Set the tooltip for the tab.
41660 * @param {String} tooltip The tab's tooltip
41662 setTooltip : function(text){
41663 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41664 this.textEl.dom.qtip = text;
41665 this.textEl.dom.removeAttribute('title');
41667 this.textEl.dom.title = text;
41671 onTabClick : function(e){
41672 e.preventDefault();
41673 this.tabPanel.activate(this.id);
41676 onTabMouseDown : function(e){
41677 e.preventDefault();
41678 this.tabPanel.activate(this.id);
41681 getWidth : function(){
41682 return this.inner.getWidth();
41685 setWidth : function(width){
41686 var iwidth = width - this.linode.getPadding("lr");
41687 this.inner.setWidth(iwidth);
41688 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41689 this.linode.setWidth(width);
41693 * Show or hide the tab
41694 * @param {Boolean} hidden True to hide or false to show.
41696 setHidden : function(hidden){
41697 this.hidden = hidden;
41698 this.linode.setStyle("display", hidden ? "none" : "");
41702 * Returns true if this tab is "hidden"
41703 * @return {Boolean}
41705 isHidden : function(){
41706 return this.hidden;
41710 * Returns the text for this tab
41713 getText : function(){
41717 autoSize : function(){
41718 //this.el.beginMeasure();
41719 this.textEl.setWidth(1);
41721 * #2804 [new] Tabs in Roojs
41722 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41724 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41725 //this.el.endMeasure();
41729 * Sets the text for the tab (Note: this also sets the tooltip text)
41730 * @param {String} text The tab's text and tooltip
41732 setText : function(text){
41734 this.textEl.update(text);
41735 this.setTooltip(text);
41736 //if(!this.tabPanel.resizeTabs){
41737 // this.autoSize();
41741 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41743 activate : function(){
41744 this.tabPanel.activate(this.id);
41748 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41750 disable : function(){
41751 if(this.tabPanel.active != this){
41752 this.disabled = true;
41753 this.status_node.addClass("disabled");
41758 * Enables this TabPanelItem if it was previously disabled.
41760 enable : function(){
41761 this.disabled = false;
41762 this.status_node.removeClass("disabled");
41766 * Sets the content for this TabPanelItem.
41767 * @param {String} content The content
41768 * @param {Boolean} loadScripts true to look for and load scripts
41770 setContent : function(content, loadScripts){
41771 this.bodyEl.update(content, loadScripts);
41775 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41776 * @return {Roo.UpdateManager} The UpdateManager
41778 getUpdateManager : function(){
41779 return this.bodyEl.getUpdateManager();
41783 * Set a URL to be used to load the content for this TabPanelItem.
41784 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41785 * @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)
41786 * @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)
41787 * @return {Roo.UpdateManager} The UpdateManager
41789 setUrl : function(url, params, loadOnce){
41790 if(this.refreshDelegate){
41791 this.un('activate', this.refreshDelegate);
41793 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41794 this.on("activate", this.refreshDelegate);
41795 return this.bodyEl.getUpdateManager();
41799 _handleRefresh : function(url, params, loadOnce){
41800 if(!loadOnce || !this.loaded){
41801 var updater = this.bodyEl.getUpdateManager();
41802 updater.update(url, params, this._setLoaded.createDelegate(this));
41807 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
41808 * Will fail silently if the setUrl method has not been called.
41809 * This does not activate the panel, just updates its content.
41811 refresh : function(){
41812 if(this.refreshDelegate){
41813 this.loaded = false;
41814 this.refreshDelegate();
41819 _setLoaded : function(){
41820 this.loaded = true;
41824 closeClick : function(e){
41827 this.fireEvent("beforeclose", this, o);
41828 if(o.cancel !== true){
41829 this.tabPanel.removeTab(this.id);
41833 * The text displayed in the tooltip for the close icon.
41836 closeText : "Close this tab"
41839 * This script refer to:
41840 * Title: International Telephone Input
41841 * Author: Jack O'Connor
41842 * Code version: v12.1.12
41843 * Availability: https://github.com/jackocnr/intl-tel-input.git
41846 Roo.bootstrap.PhoneInputData = function() {
41849 "Afghanistan (افغانستان)",
41854 "Albania (Shqipëri)",
41859 "Algeria (الجزائر)",
41884 "Antigua and Barbuda",
41894 "Armenia (Հայաստան)",
41910 "Austria (Österreich)",
41915 "Azerbaijan (Azərbaycan)",
41925 "Bahrain (البحرين)",
41930 "Bangladesh (বাংলাদেশ)",
41940 "Belarus (Беларусь)",
41945 "Belgium (België)",
41975 "Bosnia and Herzegovina (Босна и Херцеговина)",
41990 "British Indian Ocean Territory",
41995 "British Virgin Islands",
42005 "Bulgaria (България)",
42015 "Burundi (Uburundi)",
42020 "Cambodia (កម្ពុជា)",
42025 "Cameroon (Cameroun)",
42034 ["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"]
42037 "Cape Verde (Kabu Verdi)",
42042 "Caribbean Netherlands",
42053 "Central African Republic (République centrafricaine)",
42073 "Christmas Island",
42079 "Cocos (Keeling) Islands",
42090 "Comoros (جزر القمر)",
42095 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42100 "Congo (Republic) (Congo-Brazzaville)",
42120 "Croatia (Hrvatska)",
42141 "Czech Republic (Česká republika)",
42146 "Denmark (Danmark)",
42161 "Dominican Republic (República Dominicana)",
42165 ["809", "829", "849"]
42183 "Equatorial Guinea (Guinea Ecuatorial)",
42203 "Falkland Islands (Islas Malvinas)",
42208 "Faroe Islands (Føroyar)",
42229 "French Guiana (Guyane française)",
42234 "French Polynesia (Polynésie française)",
42249 "Georgia (საქართველო)",
42254 "Germany (Deutschland)",
42274 "Greenland (Kalaallit Nunaat)",
42311 "Guinea-Bissau (Guiné Bissau)",
42336 "Hungary (Magyarország)",
42341 "Iceland (Ísland)",
42361 "Iraq (العراق)",
42377 "Israel (ישראל)",
42404 "Jordan (الأردن)",
42409 "Kazakhstan (Казахстан)",
42430 "Kuwait (الكويت)",
42435 "Kyrgyzstan (Кыргызстан)",
42445 "Latvia (Latvija)",
42450 "Lebanon (لبنان)",
42465 "Libya (ليبيا)",
42475 "Lithuania (Lietuva)",
42490 "Macedonia (FYROM) (Македонија)",
42495 "Madagascar (Madagasikara)",
42525 "Marshall Islands",
42535 "Mauritania (موريتانيا)",
42540 "Mauritius (Moris)",
42561 "Moldova (Republica Moldova)",
42571 "Mongolia (Монгол)",
42576 "Montenegro (Crna Gora)",
42586 "Morocco (المغرب)",
42592 "Mozambique (Moçambique)",
42597 "Myanmar (Burma) (မြန်မာ)",
42602 "Namibia (Namibië)",
42617 "Netherlands (Nederland)",
42622 "New Caledonia (Nouvelle-Calédonie)",
42657 "North Korea (조선 민주주의 인민 공화국)",
42662 "Northern Mariana Islands",
42678 "Pakistan (پاکستان)",
42688 "Palestine (فلسطين)",
42698 "Papua New Guinea",
42740 "Réunion (La Réunion)",
42746 "Romania (România)",
42762 "Saint Barthélemy",
42773 "Saint Kitts and Nevis",
42783 "Saint Martin (Saint-Martin (partie française))",
42789 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42794 "Saint Vincent and the Grenadines",
42809 "São Tomé and Príncipe (São Tomé e Príncipe)",
42814 "Saudi Arabia (المملكة العربية السعودية)",
42819 "Senegal (Sénégal)",
42849 "Slovakia (Slovensko)",
42854 "Slovenia (Slovenija)",
42864 "Somalia (Soomaaliya)",
42874 "South Korea (대한민국)",
42879 "South Sudan (جنوب السودان)",
42889 "Sri Lanka (ශ්රී ලංකාව)",
42894 "Sudan (السودان)",
42904 "Svalbard and Jan Mayen",
42915 "Sweden (Sverige)",
42920 "Switzerland (Schweiz)",
42925 "Syria (سوريا)",
42970 "Trinidad and Tobago",
42975 "Tunisia (تونس)",
42980 "Turkey (Türkiye)",
42990 "Turks and Caicos Islands",
43000 "U.S. Virgin Islands",
43010 "Ukraine (Україна)",
43015 "United Arab Emirates (الإمارات العربية المتحدة)",
43037 "Uzbekistan (Oʻzbekiston)",
43047 "Vatican City (Città del Vaticano)",
43058 "Vietnam (Việt Nam)",
43063 "Wallis and Futuna (Wallis-et-Futuna)",
43068 "Western Sahara (الصحراء الغربية)",
43074 "Yemen (اليمن)",
43098 * This script refer to:
43099 * Title: International Telephone Input
43100 * Author: Jack O'Connor
43101 * Code version: v12.1.12
43102 * Availability: https://github.com/jackocnr/intl-tel-input.git
43106 * @class Roo.bootstrap.PhoneInput
43107 * @extends Roo.bootstrap.TriggerField
43108 * An input with International dial-code selection
43110 * @cfg {String} defaultDialCode default '+852'
43111 * @cfg {Array} preferedCountries default []
43114 * Create a new PhoneInput.
43115 * @param {Object} config Configuration options
43118 Roo.bootstrap.PhoneInput = function(config) {
43119 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43122 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43124 listWidth: undefined,
43126 selectedClass: 'active',
43128 invalidClass : "has-warning",
43130 validClass: 'has-success',
43132 allowed: '0123456789',
43137 * @cfg {String} defaultDialCode The default dial code when initializing the input
43139 defaultDialCode: '+852',
43142 * @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
43144 preferedCountries: false,
43146 getAutoCreate : function()
43148 var data = Roo.bootstrap.PhoneInputData();
43149 var align = this.labelAlign || this.parentLabelAlign();
43152 this.allCountries = [];
43153 this.dialCodeMapping = [];
43155 for (var i = 0; i < data.length; i++) {
43157 this.allCountries[i] = {
43161 priority: c[3] || 0,
43162 areaCodes: c[4] || null
43164 this.dialCodeMapping[c[2]] = {
43167 priority: c[3] || 0,
43168 areaCodes: c[4] || null
43180 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43181 maxlength: this.max_length,
43182 cls : 'form-control tel-input',
43183 autocomplete: 'new-password'
43186 var hiddenInput = {
43189 cls: 'hidden-tel-input'
43193 hiddenInput.name = this.name;
43196 if (this.disabled) {
43197 input.disabled = true;
43200 var flag_container = {
43217 cls: this.hasFeedback ? 'has-feedback' : '',
43223 cls: 'dial-code-holder',
43230 cls: 'roo-select2-container input-group',
43237 if (this.fieldLabel.length) {
43240 tooltip: 'This field is required'
43246 cls: 'control-label',
43252 html: this.fieldLabel
43255 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43261 if(this.indicatorpos == 'right') {
43262 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43269 if(align == 'left') {
43277 if(this.labelWidth > 12){
43278 label.style = "width: " + this.labelWidth + 'px';
43280 if(this.labelWidth < 13 && this.labelmd == 0){
43281 this.labelmd = this.labelWidth;
43283 if(this.labellg > 0){
43284 label.cls += ' col-lg-' + this.labellg;
43285 input.cls += ' col-lg-' + (12 - this.labellg);
43287 if(this.labelmd > 0){
43288 label.cls += ' col-md-' + this.labelmd;
43289 container.cls += ' col-md-' + (12 - this.labelmd);
43291 if(this.labelsm > 0){
43292 label.cls += ' col-sm-' + this.labelsm;
43293 container.cls += ' col-sm-' + (12 - this.labelsm);
43295 if(this.labelxs > 0){
43296 label.cls += ' col-xs-' + this.labelxs;
43297 container.cls += ' col-xs-' + (12 - this.labelxs);
43307 var settings = this;
43309 ['xs','sm','md','lg'].map(function(size){
43310 if (settings[size]) {
43311 cfg.cls += ' col-' + size + '-' + settings[size];
43315 this.store = new Roo.data.Store({
43316 proxy : new Roo.data.MemoryProxy({}),
43317 reader : new Roo.data.JsonReader({
43328 'name' : 'dialCode',
43332 'name' : 'priority',
43336 'name' : 'areaCodes',
43343 if(!this.preferedCountries) {
43344 this.preferedCountries = [
43351 var p = this.preferedCountries.reverse();
43354 for (var i = 0; i < p.length; i++) {
43355 for (var j = 0; j < this.allCountries.length; j++) {
43356 if(this.allCountries[j].iso2 == p[i]) {
43357 var t = this.allCountries[j];
43358 this.allCountries.splice(j,1);
43359 this.allCountries.unshift(t);
43365 this.store.proxy.data = {
43367 data: this.allCountries
43373 initEvents : function()
43376 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43378 this.indicator = this.indicatorEl();
43379 this.flag = this.flagEl();
43380 this.dialCodeHolder = this.dialCodeHolderEl();
43382 this.trigger = this.el.select('div.flag-box',true).first();
43383 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43388 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43389 _this.list.setWidth(lw);
43392 this.list.on('mouseover', this.onViewOver, this);
43393 this.list.on('mousemove', this.onViewMove, this);
43394 this.inputEl().on("keyup", this.onKeyUp, this);
43395 this.inputEl().on("keypress", this.onKeyPress, this);
43397 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43399 this.view = new Roo.View(this.list, this.tpl, {
43400 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43403 this.view.on('click', this.onViewClick, this);
43404 this.setValue(this.defaultDialCode);
43407 onTriggerClick : function(e)
43409 Roo.log('trigger click');
43414 if(this.isExpanded()){
43416 this.hasFocus = false;
43418 this.store.load({});
43419 this.hasFocus = true;
43424 isExpanded : function()
43426 return this.list.isVisible();
43429 collapse : function()
43431 if(!this.isExpanded()){
43435 Roo.get(document).un('mousedown', this.collapseIf, this);
43436 Roo.get(document).un('mousewheel', this.collapseIf, this);
43437 this.fireEvent('collapse', this);
43441 expand : function()
43445 if(this.isExpanded() || !this.hasFocus){
43449 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43450 this.list.setWidth(lw);
43453 this.restrictHeight();
43455 Roo.get(document).on('mousedown', this.collapseIf, this);
43456 Roo.get(document).on('mousewheel', this.collapseIf, this);
43458 this.fireEvent('expand', this);
43461 restrictHeight : function()
43463 this.list.alignTo(this.inputEl(), this.listAlign);
43464 this.list.alignTo(this.inputEl(), this.listAlign);
43467 onViewOver : function(e, t)
43469 if(this.inKeyMode){
43472 var item = this.view.findItemFromChild(t);
43475 var index = this.view.indexOf(item);
43476 this.select(index, false);
43481 onViewClick : function(view, doFocus, el, e)
43483 var index = this.view.getSelectedIndexes()[0];
43485 var r = this.store.getAt(index);
43488 this.onSelect(r, index);
43490 if(doFocus !== false && !this.blockFocus){
43491 this.inputEl().focus();
43495 onViewMove : function(e, t)
43497 this.inKeyMode = false;
43500 select : function(index, scrollIntoView)
43502 this.selectedIndex = index;
43503 this.view.select(index);
43504 if(scrollIntoView !== false){
43505 var el = this.view.getNode(index);
43507 this.list.scrollChildIntoView(el, false);
43512 createList : function()
43514 this.list = Roo.get(document.body).createChild({
43516 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43517 style: 'display:none'
43520 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43523 collapseIf : function(e)
43525 var in_combo = e.within(this.el);
43526 var in_list = e.within(this.list);
43527 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43529 if (in_combo || in_list || is_list) {
43535 onSelect : function(record, index)
43537 if(this.fireEvent('beforeselect', this, record, index) !== false){
43539 this.setFlagClass(record.data.iso2);
43540 this.setDialCode(record.data.dialCode);
43541 this.hasFocus = false;
43543 this.fireEvent('select', this, record, index);
43547 flagEl : function()
43549 var flag = this.el.select('div.flag',true).first();
43556 dialCodeHolderEl : function()
43558 var d = this.el.select('input.dial-code-holder',true).first();
43565 setDialCode : function(v)
43567 this.dialCodeHolder.dom.value = '+'+v;
43570 setFlagClass : function(n)
43572 this.flag.dom.className = 'flag '+n;
43575 getValue : function()
43577 var v = this.inputEl().getValue();
43578 if(this.dialCodeHolder) {
43579 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43584 setValue : function(v)
43586 var d = this.getDialCode(v);
43588 //invalid dial code
43589 if(v.length == 0 || !d || d.length == 0) {
43591 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43592 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43598 this.setFlagClass(this.dialCodeMapping[d].iso2);
43599 this.setDialCode(d);
43600 this.inputEl().dom.value = v.replace('+'+d,'');
43601 this.hiddenEl().dom.value = this.getValue();
43606 getDialCode : function(v)
43610 if (v.length == 0) {
43611 return this.dialCodeHolder.dom.value;
43615 if (v.charAt(0) != "+") {
43618 var numericChars = "";
43619 for (var i = 1; i < v.length; i++) {
43620 var c = v.charAt(i);
43623 if (this.dialCodeMapping[numericChars]) {
43624 dialCode = v.substr(1, i);
43626 if (numericChars.length == 4) {
43636 this.setValue(this.defaultDialCode);
43640 hiddenEl : function()
43642 return this.el.select('input.hidden-tel-input',true).first();
43645 // after setting val
43646 onKeyUp : function(e){
43647 this.setValue(this.getValue());
43650 onKeyPress : function(e){
43651 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43658 * @class Roo.bootstrap.MoneyField
43659 * @extends Roo.bootstrap.ComboBox
43660 * Bootstrap MoneyField class
43663 * Create a new MoneyField.
43664 * @param {Object} config Configuration options
43667 Roo.bootstrap.MoneyField = function(config) {
43669 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43673 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43676 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43678 allowDecimals : true,
43680 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43682 decimalSeparator : ".",
43684 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43686 decimalPrecision : 0,
43688 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43690 allowNegative : true,
43692 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43696 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43698 minValue : Number.NEGATIVE_INFINITY,
43700 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43702 maxValue : Number.MAX_VALUE,
43704 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43706 minText : "The minimum value for this field is {0}",
43708 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43710 maxText : "The maximum value for this field is {0}",
43712 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
43713 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43715 nanText : "{0} is not a valid number",
43717 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43721 * @cfg {String} defaults currency of the MoneyField
43722 * value should be in lkey
43724 defaultCurrency : false,
43726 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43728 thousandsDelimiter : false,
43730 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43741 getAutoCreate : function()
43743 var align = this.labelAlign || this.parentLabelAlign();
43755 cls : 'form-control roo-money-amount-input',
43756 autocomplete: 'new-password'
43759 var hiddenInput = {
43763 cls: 'hidden-number-input'
43766 if(this.max_length) {
43767 input.maxlength = this.max_length;
43771 hiddenInput.name = this.name;
43774 if (this.disabled) {
43775 input.disabled = true;
43778 var clg = 12 - this.inputlg;
43779 var cmd = 12 - this.inputmd;
43780 var csm = 12 - this.inputsm;
43781 var cxs = 12 - this.inputxs;
43785 cls : 'row roo-money-field',
43789 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43793 cls: 'roo-select2-container input-group',
43797 cls : 'form-control roo-money-currency-input',
43798 autocomplete: 'new-password',
43800 name : this.currencyName
43804 cls : 'input-group-addon',
43818 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43822 cls: this.hasFeedback ? 'has-feedback' : '',
43833 if (this.fieldLabel.length) {
43836 tooltip: 'This field is required'
43842 cls: 'control-label',
43848 html: this.fieldLabel
43851 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43857 if(this.indicatorpos == 'right') {
43858 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43865 if(align == 'left') {
43873 if(this.labelWidth > 12){
43874 label.style = "width: " + this.labelWidth + 'px';
43876 if(this.labelWidth < 13 && this.labelmd == 0){
43877 this.labelmd = this.labelWidth;
43879 if(this.labellg > 0){
43880 label.cls += ' col-lg-' + this.labellg;
43881 input.cls += ' col-lg-' + (12 - this.labellg);
43883 if(this.labelmd > 0){
43884 label.cls += ' col-md-' + this.labelmd;
43885 container.cls += ' col-md-' + (12 - this.labelmd);
43887 if(this.labelsm > 0){
43888 label.cls += ' col-sm-' + this.labelsm;
43889 container.cls += ' col-sm-' + (12 - this.labelsm);
43891 if(this.labelxs > 0){
43892 label.cls += ' col-xs-' + this.labelxs;
43893 container.cls += ' col-xs-' + (12 - this.labelxs);
43904 var settings = this;
43906 ['xs','sm','md','lg'].map(function(size){
43907 if (settings[size]) {
43908 cfg.cls += ' col-' + size + '-' + settings[size];
43915 initEvents : function()
43917 this.indicator = this.indicatorEl();
43919 this.initCurrencyEvent();
43921 this.initNumberEvent();
43924 initCurrencyEvent : function()
43927 throw "can not find store for combo";
43930 this.store = Roo.factory(this.store, Roo.data);
43931 this.store.parent = this;
43935 this.triggerEl = this.el.select('.input-group-addon', true).first();
43937 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43942 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43943 _this.list.setWidth(lw);
43946 this.list.on('mouseover', this.onViewOver, this);
43947 this.list.on('mousemove', this.onViewMove, this);
43948 this.list.on('scroll', this.onViewScroll, this);
43951 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43954 this.view = new Roo.View(this.list, this.tpl, {
43955 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43958 this.view.on('click', this.onViewClick, this);
43960 this.store.on('beforeload', this.onBeforeLoad, this);
43961 this.store.on('load', this.onLoad, this);
43962 this.store.on('loadexception', this.onLoadException, this);
43964 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43965 "up" : function(e){
43966 this.inKeyMode = true;
43970 "down" : function(e){
43971 if(!this.isExpanded()){
43972 this.onTriggerClick();
43974 this.inKeyMode = true;
43979 "enter" : function(e){
43982 if(this.fireEvent("specialkey", this, e)){
43983 this.onViewClick(false);
43989 "esc" : function(e){
43993 "tab" : function(e){
43996 if(this.fireEvent("specialkey", this, e)){
43997 this.onViewClick(false);
44005 doRelay : function(foo, bar, hname){
44006 if(hname == 'down' || this.scope.isExpanded()){
44007 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44015 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44019 initNumberEvent : function(e)
44021 this.inputEl().on("keydown" , this.fireKey, this);
44022 this.inputEl().on("focus", this.onFocus, this);
44023 this.inputEl().on("blur", this.onBlur, this);
44025 this.inputEl().relayEvent('keyup', this);
44027 if(this.indicator){
44028 this.indicator.addClass('invisible');
44031 this.originalValue = this.getValue();
44033 if(this.validationEvent == 'keyup'){
44034 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44035 this.inputEl().on('keyup', this.filterValidation, this);
44037 else if(this.validationEvent !== false){
44038 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44041 if(this.selectOnFocus){
44042 this.on("focus", this.preFocus, this);
44045 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44046 this.inputEl().on("keypress", this.filterKeys, this);
44048 this.inputEl().relayEvent('keypress', this);
44051 var allowed = "0123456789";
44053 if(this.allowDecimals){
44054 allowed += this.decimalSeparator;
44057 if(this.allowNegative){
44061 if(this.thousandsDelimiter) {
44065 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44067 var keyPress = function(e){
44069 var k = e.getKey();
44071 var c = e.getCharCode();
44074 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44075 allowed.indexOf(String.fromCharCode(c)) === -1
44081 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44085 if(allowed.indexOf(String.fromCharCode(c)) === -1){
44090 this.inputEl().on("keypress", keyPress, this);
44094 onTriggerClick : function(e)
44101 this.loadNext = false;
44103 if(this.isExpanded()){
44108 this.hasFocus = true;
44110 if(this.triggerAction == 'all') {
44111 this.doQuery(this.allQuery, true);
44115 this.doQuery(this.getRawValue());
44118 getCurrency : function()
44120 var v = this.currencyEl().getValue();
44125 restrictHeight : function()
44127 this.list.alignTo(this.currencyEl(), this.listAlign);
44128 this.list.alignTo(this.currencyEl(), this.listAlign);
44131 onViewClick : function(view, doFocus, el, e)
44133 var index = this.view.getSelectedIndexes()[0];
44135 var r = this.store.getAt(index);
44138 this.onSelect(r, index);
44142 onSelect : function(record, index){
44144 if(this.fireEvent('beforeselect', this, record, index) !== false){
44146 this.setFromCurrencyData(index > -1 ? record.data : false);
44150 this.fireEvent('select', this, record, index);
44154 setFromCurrencyData : function(o)
44158 this.lastCurrency = o;
44160 if (this.currencyField) {
44161 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44163 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
44166 this.lastSelectionText = currency;
44168 //setting default currency
44169 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44170 this.setCurrency(this.defaultCurrency);
44174 this.setCurrency(currency);
44177 setFromData : function(o)
44181 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44183 this.setFromCurrencyData(c);
44188 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44190 Roo.log('no value set for '+ (this.name ? this.name : this.id));
44193 this.setValue(value);
44197 setCurrency : function(v)
44199 this.currencyValue = v;
44202 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44207 setValue : function(v)
44209 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44215 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44217 this.inputEl().dom.value = (v == '') ? '' :
44218 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44220 if(!this.allowZero && v === '0') {
44221 this.hiddenEl().dom.value = '';
44222 this.inputEl().dom.value = '';
44229 getRawValue : function()
44231 var v = this.inputEl().getValue();
44236 getValue : function()
44238 return this.fixPrecision(this.parseValue(this.getRawValue()));
44241 parseValue : function(value)
44243 if(this.thousandsDelimiter) {
44245 r = new RegExp(",", "g");
44246 value = value.replace(r, "");
44249 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44250 return isNaN(value) ? '' : value;
44254 fixPrecision : function(value)
44256 if(this.thousandsDelimiter) {
44258 r = new RegExp(",", "g");
44259 value = value.replace(r, "");
44262 var nan = isNaN(value);
44264 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44265 return nan ? '' : value;
44267 return parseFloat(value).toFixed(this.decimalPrecision);
44270 decimalPrecisionFcn : function(v)
44272 return Math.floor(v);
44275 validateValue : function(value)
44277 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44281 var num = this.parseValue(value);
44284 this.markInvalid(String.format(this.nanText, value));
44288 if(num < this.minValue){
44289 this.markInvalid(String.format(this.minText, this.minValue));
44293 if(num > this.maxValue){
44294 this.markInvalid(String.format(this.maxText, this.maxValue));
44301 validate : function()
44303 if(this.disabled || this.allowBlank){
44308 var currency = this.getCurrency();
44310 if(this.validateValue(this.getRawValue()) && currency.length){
44315 this.markInvalid();
44319 getName: function()
44324 beforeBlur : function()
44330 var v = this.parseValue(this.getRawValue());
44337 onBlur : function()
44341 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44342 //this.el.removeClass(this.focusClass);
44345 this.hasFocus = false;
44347 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44351 var v = this.getValue();
44353 if(String(v) !== String(this.startValue)){
44354 this.fireEvent('change', this, v, this.startValue);
44357 this.fireEvent("blur", this);
44360 inputEl : function()
44362 return this.el.select('.roo-money-amount-input', true).first();
44365 currencyEl : function()
44367 return this.el.select('.roo-money-currency-input', true).first();
44370 hiddenEl : function()
44372 return this.el.select('input.hidden-number-input',true).first();
44376 * @class Roo.bootstrap.BezierSignature
44377 * @extends Roo.bootstrap.Component
44378 * Bootstrap BezierSignature class
44379 * This script refer to:
44380 * Title: Signature Pad
44382 * Availability: https://github.com/szimek/signature_pad
44385 * Create a new BezierSignature
44386 * @param {Object} config The config object
44389 Roo.bootstrap.BezierSignature = function(config){
44390 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44396 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44403 mouse_btn_down: true,
44406 * @cfg {int} canvas height
44408 canvas_height: '200px',
44411 * @cfg {float|function} Radius of a single dot.
44416 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44421 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44426 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44431 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44436 * @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.
44438 bg_color: 'rgba(0, 0, 0, 0)',
44441 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44443 dot_color: 'black',
44446 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44448 velocity_filter_weight: 0.7,
44451 * @cfg {function} Callback when stroke begin.
44456 * @cfg {function} Callback when stroke end.
44460 getAutoCreate : function()
44462 var cls = 'roo-signature column';
44465 cls += ' ' + this.cls;
44475 for(var i = 0; i < col_sizes.length; i++) {
44476 if(this[col_sizes[i]]) {
44477 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44487 cls: 'roo-signature-body',
44491 cls: 'roo-signature-body-canvas',
44492 height: this.canvas_height,
44493 width: this.canvas_width
44500 style: 'display: none'
44508 initEvents: function()
44510 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44512 var canvas = this.canvasEl();
44514 // mouse && touch event swapping...
44515 canvas.dom.style.touchAction = 'none';
44516 canvas.dom.style.msTouchAction = 'none';
44518 this.mouse_btn_down = false;
44519 canvas.on('mousedown', this._handleMouseDown, this);
44520 canvas.on('mousemove', this._handleMouseMove, this);
44521 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44523 if (window.PointerEvent) {
44524 canvas.on('pointerdown', this._handleMouseDown, this);
44525 canvas.on('pointermove', this._handleMouseMove, this);
44526 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44529 if ('ontouchstart' in window) {
44530 canvas.on('touchstart', this._handleTouchStart, this);
44531 canvas.on('touchmove', this._handleTouchMove, this);
44532 canvas.on('touchend', this._handleTouchEnd, this);
44535 Roo.EventManager.onWindowResize(this.resize, this, true);
44537 // file input event
44538 this.fileEl().on('change', this.uploadImage, this);
44545 resize: function(){
44547 var canvas = this.canvasEl().dom;
44548 var ctx = this.canvasElCtx();
44549 var img_data = false;
44551 if(canvas.width > 0) {
44552 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44554 // setting canvas width will clean img data
44557 var style = window.getComputedStyle ?
44558 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44560 var padding_left = parseInt(style.paddingLeft) || 0;
44561 var padding_right = parseInt(style.paddingRight) || 0;
44563 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44566 ctx.putImageData(img_data, 0, 0);
44570 _handleMouseDown: function(e)
44572 if (e.browserEvent.which === 1) {
44573 this.mouse_btn_down = true;
44574 this.strokeBegin(e);
44578 _handleMouseMove: function (e)
44580 if (this.mouse_btn_down) {
44581 this.strokeMoveUpdate(e);
44585 _handleMouseUp: function (e)
44587 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44588 this.mouse_btn_down = false;
44593 _handleTouchStart: function (e) {
44595 e.preventDefault();
44596 if (e.browserEvent.targetTouches.length === 1) {
44597 // var touch = e.browserEvent.changedTouches[0];
44598 // this.strokeBegin(touch);
44600 this.strokeBegin(e); // assume e catching the correct xy...
44604 _handleTouchMove: function (e) {
44605 e.preventDefault();
44606 // var touch = event.targetTouches[0];
44607 // _this._strokeMoveUpdate(touch);
44608 this.strokeMoveUpdate(e);
44611 _handleTouchEnd: function (e) {
44612 var wasCanvasTouched = e.target === this.canvasEl().dom;
44613 if (wasCanvasTouched) {
44614 e.preventDefault();
44615 // var touch = event.changedTouches[0];
44616 // _this._strokeEnd(touch);
44621 reset: function () {
44622 this._lastPoints = [];
44623 this._lastVelocity = 0;
44624 this._lastWidth = (this.min_width + this.max_width) / 2;
44625 this.canvasElCtx().fillStyle = this.dot_color;
44628 strokeMoveUpdate: function(e)
44630 this.strokeUpdate(e);
44632 if (this.throttle) {
44633 this.throttleStroke(this.strokeUpdate, this.throttle);
44636 this.strokeUpdate(e);
44640 strokeBegin: function(e)
44642 var newPointGroup = {
44643 color: this.dot_color,
44647 if (typeof this.onBegin === 'function') {
44651 this.curve_data.push(newPointGroup);
44653 this.strokeUpdate(e);
44656 strokeUpdate: function(e)
44658 var rect = this.canvasEl().dom.getBoundingClientRect();
44659 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44660 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44661 var lastPoints = lastPointGroup.points;
44662 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44663 var isLastPointTooClose = lastPoint
44664 ? point.distanceTo(lastPoint) <= this.min_distance
44666 var color = lastPointGroup.color;
44667 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44668 var curve = this.addPoint(point);
44670 this.drawDot({color: color, point: point});
44673 this.drawCurve({color: color, curve: curve});
44683 strokeEnd: function(e)
44685 this.strokeUpdate(e);
44686 if (typeof this.onEnd === 'function') {
44691 addPoint: function (point) {
44692 var _lastPoints = this._lastPoints;
44693 _lastPoints.push(point);
44694 if (_lastPoints.length > 2) {
44695 if (_lastPoints.length === 3) {
44696 _lastPoints.unshift(_lastPoints[0]);
44698 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44699 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44700 _lastPoints.shift();
44706 calculateCurveWidths: function (startPoint, endPoint) {
44707 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44708 (1 - this.velocity_filter_weight) * this._lastVelocity;
44710 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44713 start: this._lastWidth
44716 this._lastVelocity = velocity;
44717 this._lastWidth = newWidth;
44721 drawDot: function (_a) {
44722 var color = _a.color, point = _a.point;
44723 var ctx = this.canvasElCtx();
44724 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44726 this.drawCurveSegment(point.x, point.y, width);
44728 ctx.fillStyle = color;
44732 drawCurve: function (_a) {
44733 var color = _a.color, curve = _a.curve;
44734 var ctx = this.canvasElCtx();
44735 var widthDelta = curve.endWidth - curve.startWidth;
44736 var drawSteps = Math.floor(curve.length()) * 2;
44738 ctx.fillStyle = color;
44739 for (var i = 0; i < drawSteps; i += 1) {
44740 var t = i / drawSteps;
44746 var x = uuu * curve.startPoint.x;
44747 x += 3 * uu * t * curve.control1.x;
44748 x += 3 * u * tt * curve.control2.x;
44749 x += ttt * curve.endPoint.x;
44750 var y = uuu * curve.startPoint.y;
44751 y += 3 * uu * t * curve.control1.y;
44752 y += 3 * u * tt * curve.control2.y;
44753 y += ttt * curve.endPoint.y;
44754 var width = curve.startWidth + ttt * widthDelta;
44755 this.drawCurveSegment(x, y, width);
44761 drawCurveSegment: function (x, y, width) {
44762 var ctx = this.canvasElCtx();
44764 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44765 this.is_empty = false;
44770 var ctx = this.canvasElCtx();
44771 var canvas = this.canvasEl().dom;
44772 ctx.fillStyle = this.bg_color;
44773 ctx.clearRect(0, 0, canvas.width, canvas.height);
44774 ctx.fillRect(0, 0, canvas.width, canvas.height);
44775 this.curve_data = [];
44777 this.is_empty = true;
44782 return this.el.select('input',true).first();
44785 canvasEl: function()
44787 return this.el.select('canvas',true).first();
44790 canvasElCtx: function()
44792 return this.el.select('canvas',true).first().dom.getContext('2d');
44795 getImage: function(type)
44797 if(this.is_empty) {
44802 return this.canvasEl().dom.toDataURL('image/'+type, 1);
44805 drawFromImage: function(img_src)
44807 var img = new Image();
44809 img.onload = function(){
44810 this.canvasElCtx().drawImage(img, 0, 0);
44815 this.is_empty = false;
44818 selectImage: function()
44820 this.fileEl().dom.click();
44823 uploadImage: function(e)
44825 var reader = new FileReader();
44827 reader.onload = function(e){
44828 var img = new Image();
44829 img.onload = function(){
44831 this.canvasElCtx().drawImage(img, 0, 0);
44833 img.src = e.target.result;
44836 reader.readAsDataURL(e.target.files[0]);
44839 // Bezier Point Constructor
44840 Point: (function () {
44841 function Point(x, y, time) {
44844 this.time = time || Date.now();
44846 Point.prototype.distanceTo = function (start) {
44847 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44849 Point.prototype.equals = function (other) {
44850 return this.x === other.x && this.y === other.y && this.time === other.time;
44852 Point.prototype.velocityFrom = function (start) {
44853 return this.time !== start.time
44854 ? this.distanceTo(start) / (this.time - start.time)
44861 // Bezier Constructor
44862 Bezier: (function () {
44863 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44864 this.startPoint = startPoint;
44865 this.control2 = control2;
44866 this.control1 = control1;
44867 this.endPoint = endPoint;
44868 this.startWidth = startWidth;
44869 this.endWidth = endWidth;
44871 Bezier.fromPoints = function (points, widths, scope) {
44872 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44873 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44874 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44876 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44877 var dx1 = s1.x - s2.x;
44878 var dy1 = s1.y - s2.y;
44879 var dx2 = s2.x - s3.x;
44880 var dy2 = s2.y - s3.y;
44881 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44882 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44883 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44884 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44885 var dxm = m1.x - m2.x;
44886 var dym = m1.y - m2.y;
44887 var k = l2 / (l1 + l2);
44888 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44889 var tx = s2.x - cm.x;
44890 var ty = s2.y - cm.y;
44892 c1: new scope.Point(m1.x + tx, m1.y + ty),
44893 c2: new scope.Point(m2.x + tx, m2.y + ty)
44896 Bezier.prototype.length = function () {
44901 for (var i = 0; i <= steps; i += 1) {
44903 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44904 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44906 var xdiff = cx - px;
44907 var ydiff = cy - py;
44908 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44915 Bezier.prototype.point = function (t, start, c1, c2, end) {
44916 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44917 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44918 + (3.0 * c2 * (1.0 - t) * t * t)
44919 + (end * t * t * t);
44924 throttleStroke: function(fn, wait) {
44925 if (wait === void 0) { wait = 250; }
44927 var timeout = null;
44931 var later = function () {
44932 previous = Date.now();
44934 result = fn.apply(storedContext, storedArgs);
44936 storedContext = null;
44940 return function wrapper() {
44942 for (var _i = 0; _i < arguments.length; _i++) {
44943 args[_i] = arguments[_i];
44945 var now = Date.now();
44946 var remaining = wait - (now - previous);
44947 storedContext = this;
44949 if (remaining <= 0 || remaining > wait) {
44951 clearTimeout(timeout);
44955 result = fn.apply(storedContext, storedArgs);
44957 storedContext = null;
44961 else if (!timeout) {
44962 timeout = window.setTimeout(later, remaining);