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 = 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
3048 * Create a new Input
3049 * @param {Object} config The config object
3052 Roo.bootstrap.Img = function(config){
3053 Roo.bootstrap.Img.superclass.constructor.call(this, config);
3059 * The img click event for the img.
3060 * @param {Roo.EventObject} e
3065 * The when any image loads
3066 * @param {Roo.EventObject} e
3072 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
3074 imgResponsive: true,
3084 getAutoCreate : function()
3086 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3087 return this.createSingleImg();
3092 cls: 'roo-image-responsive-group',
3097 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3099 if(!_this[size + 'Url']){
3105 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3106 html: _this.html || cfg.html,
3107 src: _this[size + 'Url']
3110 img.cls += ' roo-image-responsive-' + size;
3112 var s = ['xs', 'sm', 'md', 'lg'];
3114 s.splice(s.indexOf(size), 1);
3116 Roo.each(s, function(ss){
3117 img.cls += ' hidden-' + ss;
3120 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3121 cfg.cls += ' img-' + _this.border;
3125 cfg.alt = _this.alt;
3138 a.target = _this.target;
3142 cfg.cn.push((_this.href) ? a : img);
3149 createSingleImg : function()
3153 cls: (this.imgResponsive) ? 'img-responsive' : '',
3155 src : 'about:blank' // just incase src get's set to undefined?!?
3158 cfg.html = this.html || cfg.html;
3160 cfg.src = this.src || cfg.src;
3162 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3163 cfg.cls += ' img-' + this.border;
3180 a.target = this.target;
3185 return (this.href) ? a : cfg;
3188 initEvents: function()
3191 this.el.on('click', this.onClick, this);
3193 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3194 this.el.on('loac', this.onImageLoad, this);
3197 // not sure if this works.. not tested
3198 this.el.select('img', true).on('load', this.onImageLoad, this);
3203 onClick : function(e)
3205 Roo.log('img onclick');
3206 this.fireEvent('click', this, e);
3208 onImageLoad: function(e)
3210 Roo.log('img load');
3211 this.fireEvent('load', this, e);
3215 * Sets the url of the image - used to update it
3216 * @param {String} url the url of the image
3219 setSrc : function(url)
3223 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3224 this.el.dom.src = url;
3228 this.el.select('img', true).first().dom.src = url;
3244 * @class Roo.bootstrap.Link
3245 * @extends Roo.bootstrap.Component
3246 * Bootstrap Link Class
3247 * @cfg {String} alt image alternative text
3248 * @cfg {String} href a tag href
3249 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3250 * @cfg {String} html the content of the link.
3251 * @cfg {String} anchor name for the anchor link
3252 * @cfg {String} fa - favicon
3254 * @cfg {Boolean} preventDefault (true | false) default false
3258 * Create a new Input
3259 * @param {Object} config The config object
3262 Roo.bootstrap.Link = function(config){
3263 Roo.bootstrap.Link.superclass.constructor.call(this, config);
3269 * The img click event for the img.
3270 * @param {Roo.EventObject} e
3276 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
3280 preventDefault: false,
3286 getAutoCreate : function()
3288 var html = this.html || '';
3290 if (this.fa !== false) {
3291 html = '<i class="fa fa-' + this.fa + '"></i>';
3296 // anchor's do not require html/href...
3297 if (this.anchor === false) {
3299 cfg.href = this.href || '#';
3301 cfg.name = this.anchor;
3302 if (this.html !== false || this.fa !== false) {
3305 if (this.href !== false) {
3306 cfg.href = this.href;
3310 if(this.alt !== false){
3315 if(this.target !== false) {
3316 cfg.target = this.target;
3322 initEvents: function() {
3324 if(!this.href || this.preventDefault){
3325 this.el.on('click', this.onClick, this);
3329 onClick : function(e)
3331 if(this.preventDefault){
3334 //Roo.log('img onclick');
3335 this.fireEvent('click', this, e);
3348 * @class Roo.bootstrap.Header
3349 * @extends Roo.bootstrap.Component
3350 * Bootstrap Header class
3351 * @cfg {String} html content of header
3352 * @cfg {Number} level (1|2|3|4|5|6) default 1
3355 * Create a new Header
3356 * @param {Object} config The config object
3360 Roo.bootstrap.Header = function(config){
3361 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3364 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3372 getAutoCreate : function(){
3377 tag: 'h' + (1 *this.level),
3378 html: this.html || ''
3390 * Ext JS Library 1.1.1
3391 * Copyright(c) 2006-2007, Ext JS, LLC.
3393 * Originally Released Under LGPL - original licence link has changed is not relivant.
3396 * <script type="text/javascript">
3400 * @class Roo.bootstrap.MenuMgr
3401 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3404 Roo.bootstrap.MenuMgr = function(){
3405 var menus, active, groups = {}, attached = false, lastShow = new Date();
3407 // private - called when first menu is created
3410 active = new Roo.util.MixedCollection();
3411 Roo.get(document).addKeyListener(27, function(){
3412 if(active.length > 0){
3420 if(active && active.length > 0){
3421 var c = active.clone();
3431 if(active.length < 1){
3432 Roo.get(document).un("mouseup", onMouseDown);
3440 var last = active.last();
3441 lastShow = new Date();
3444 Roo.get(document).on("mouseup", onMouseDown);
3449 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3450 m.parentMenu.activeChild = m;
3451 }else if(last && last.isVisible()){
3452 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3457 function onBeforeHide(m){
3459 m.activeChild.hide();
3461 if(m.autoHideTimer){
3462 clearTimeout(m.autoHideTimer);
3463 delete m.autoHideTimer;
3468 function onBeforeShow(m){
3469 var pm = m.parentMenu;
3470 if(!pm && !m.allowOtherMenus){
3472 }else if(pm && pm.activeChild && active != m){
3473 pm.activeChild.hide();
3477 // private this should really trigger on mouseup..
3478 function onMouseDown(e){
3479 Roo.log("on Mouse Up");
3481 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3482 Roo.log("MenuManager hideAll");
3491 function onBeforeCheck(mi, state){
3493 var g = groups[mi.group];
3494 for(var i = 0, l = g.length; i < l; i++){
3496 g[i].setChecked(false);
3505 * Hides all menus that are currently visible
3507 hideAll : function(){
3512 register : function(menu){
3516 menus[menu.id] = menu;
3517 menu.on("beforehide", onBeforeHide);
3518 menu.on("hide", onHide);
3519 menu.on("beforeshow", onBeforeShow);
3520 menu.on("show", onShow);
3522 if(g && menu.events["checkchange"]){
3526 groups[g].push(menu);
3527 menu.on("checkchange", onCheck);
3532 * Returns a {@link Roo.menu.Menu} object
3533 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3534 * be used to generate and return a new Menu instance.
3536 get : function(menu){
3537 if(typeof menu == "string"){ // menu id
3539 }else if(menu.events){ // menu instance
3542 /*else if(typeof menu.length == 'number'){ // array of menu items?
3543 return new Roo.bootstrap.Menu({items:menu});
3544 }else{ // otherwise, must be a config
3545 return new Roo.bootstrap.Menu(menu);
3552 unregister : function(menu){
3553 delete menus[menu.id];
3554 menu.un("beforehide", onBeforeHide);
3555 menu.un("hide", onHide);
3556 menu.un("beforeshow", onBeforeShow);
3557 menu.un("show", onShow);
3559 if(g && menu.events["checkchange"]){
3560 groups[g].remove(menu);
3561 menu.un("checkchange", onCheck);
3566 registerCheckable : function(menuItem){
3567 var g = menuItem.group;
3572 groups[g].push(menuItem);
3573 menuItem.on("beforecheckchange", onBeforeCheck);
3578 unregisterCheckable : function(menuItem){
3579 var g = menuItem.group;
3581 groups[g].remove(menuItem);
3582 menuItem.un("beforecheckchange", onBeforeCheck);
3594 * @class Roo.bootstrap.Menu
3595 * @extends Roo.bootstrap.Component
3596 * Bootstrap Menu class - container for MenuItems
3597 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3598 * @cfg {bool} hidden if the menu should be hidden when rendered.
3599 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3600 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3601 * @cfg {bool} hideTrigger (true|false) default false - hide the carret for trigger.
3602 * @cfg {String} align default tl-bl? == below - how the menu should be aligned.
3606 * @param {Object} config The config object
3610 Roo.bootstrap.Menu = function(config){
3612 if (config.type == 'treeview') {
3613 // normally menu's are drawn attached to the document to handle layering etc..
3614 // however treeview (used by the docs menu is drawn into the parent element)
3615 this.container_method = 'getChildContainer';
3618 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3619 if (this.registerMenu && this.type != 'treeview') {
3620 Roo.bootstrap.MenuMgr.register(this);
3627 * Fires before this menu is displayed (return false to block)
3628 * @param {Roo.menu.Menu} this
3633 * Fires before this menu is hidden (return false to block)
3634 * @param {Roo.menu.Menu} this
3639 * Fires after this menu is displayed
3640 * @param {Roo.menu.Menu} this
3645 * Fires after this menu is hidden
3646 * @param {Roo.menu.Menu} this
3651 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3652 * @param {Roo.menu.Menu} this
3653 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3654 * @param {Roo.EventObject} e
3659 * Fires when the mouse is hovering over this menu
3660 * @param {Roo.menu.Menu} this
3661 * @param {Roo.EventObject} e
3662 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3667 * Fires when the mouse exits this menu
3668 * @param {Roo.menu.Menu} this
3669 * @param {Roo.EventObject} e
3670 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3675 * Fires when a menu item contained in this menu is clicked
3676 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3677 * @param {Roo.EventObject} e
3681 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3684 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
3688 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3691 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3693 registerMenu : true,
3695 menuItems :false, // stores the menu items..
3705 container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3707 hideTrigger : false,
3712 getChildContainer : function() {
3716 getAutoCreate : function(){
3718 //if (['right'].indexOf(this.align)!==-1) {
3719 // cfg.cn[1].cls += ' pull-right'
3724 cls : 'dropdown-menu shadow' ,
3725 style : 'z-index:1000'
3729 if (this.type === 'submenu') {
3730 cfg.cls = 'submenu active';
3732 if (this.type === 'treeview') {
3733 cfg.cls = 'treeview-menu';
3738 initEvents : function() {
3740 // Roo.log("ADD event");
3741 // Roo.log(this.triggerEl.dom);
3742 if (this.triggerEl) {
3744 this.triggerEl.on('click', this.onTriggerClick, this);
3746 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3748 if (!this.hideTrigger) {
3749 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3750 // dropdown toggle on the 'a' in BS4?
3751 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3753 this.triggerEl.addClass('dropdown-toggle');
3759 this.el.on('touchstart' , this.onTouch, this);
3761 this.el.on('click' , this.onClick, this);
3763 this.el.on("mouseover", this.onMouseOver, this);
3764 this.el.on("mouseout", this.onMouseOut, this);
3768 findTargetItem : function(e)
3770 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3774 //Roo.log(t); Roo.log(t.id);
3776 //Roo.log(this.menuitems);
3777 return this.menuitems.get(t.id);
3779 //return this.items.get(t.menuItemId);
3785 onTouch : function(e)
3787 Roo.log("menu.onTouch");
3788 //e.stopEvent(); this make the user popdown broken
3792 onClick : function(e)
3794 Roo.log("menu.onClick");
3796 var t = this.findTargetItem(e);
3797 if(!t || t.isContainer){
3802 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3803 if(t == this.activeItem && t.shouldDeactivate(e)){
3804 this.activeItem.deactivate();
3805 delete this.activeItem;
3809 this.setActiveItem(t, true);
3817 Roo.log('pass click event');
3821 this.fireEvent("click", this, t, e);
3825 if(!t.href.length || t.href == '#'){
3826 (function() { _this.hide(); }).defer(100);
3831 onMouseOver : function(e){
3832 var t = this.findTargetItem(e);
3835 // if(t.canActivate && !t.disabled){
3836 // this.setActiveItem(t, true);
3840 this.fireEvent("mouseover", this, e, t);
3842 isVisible : function(){
3843 return !this.hidden;
3845 onMouseOut : function(e){
3846 var t = this.findTargetItem(e);
3849 // if(t == this.activeItem && t.shouldDeactivate(e)){
3850 // this.activeItem.deactivate();
3851 // delete this.activeItem;
3854 this.fireEvent("mouseout", this, e, t);
3859 * Displays this menu relative to another element
3860 * @param {String/HTMLElement/Roo.Element} element The element to align to
3861 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3862 * the element (defaults to this.defaultAlign)
3863 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3865 show : function(el, pos, parentMenu)
3867 if (false === this.fireEvent("beforeshow", this)) {
3868 Roo.log("show canceled");
3871 this.parentMenu = parentMenu;
3875 this.el.addClass('show'); // show otherwise we do not know how big we are..
3877 var xy = this.el.getAlignToXY(el, pos);
3879 // bl-tl << left align below
3880 // tl-bl << left align
3882 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3883 // if it goes to far to the right.. -> align left.
3884 xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3887 // was left align - go right?
3888 xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3891 // goes down the bottom
3892 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3894 var a = this.align.replace('?', '').split('-');
3895 xy = this.el.getAlignToXY(el, a[1] + '-' + a[0] + '?')
3899 this.showAt( xy , parentMenu, false);
3902 * Displays this menu at a specific xy position
3903 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3904 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3906 showAt : function(xy, parentMenu, /* private: */_e){
3907 this.parentMenu = parentMenu;
3912 this.fireEvent("beforeshow", this);
3913 //xy = this.el.adjustForConstraints(xy);
3917 this.hideMenuItems();
3918 this.hidden = false;
3919 if (this.triggerEl) {
3920 this.triggerEl.addClass('open');
3923 this.el.addClass('show');
3927 // reassign x when hitting right
3929 // reassign y when hitting bottom
3931 // but the list may align on trigger left or trigger top... should it be a properity?
3933 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3938 this.fireEvent("show", this);
3944 this.doFocus.defer(50, this);
3948 doFocus : function(){
3950 this.focusEl.focus();
3955 * Hides this menu and optionally all parent menus
3956 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3958 hide : function(deep)
3960 if (false === this.fireEvent("beforehide", this)) {
3961 Roo.log("hide canceled");
3964 this.hideMenuItems();
3965 if(this.el && this.isVisible()){
3967 if(this.activeItem){
3968 this.activeItem.deactivate();
3969 this.activeItem = null;
3971 if (this.triggerEl) {
3972 this.triggerEl.removeClass('open');
3975 this.el.removeClass('show');
3977 this.fireEvent("hide", this);
3979 if(deep === true && this.parentMenu){
3980 this.parentMenu.hide(true);
3984 onTriggerClick : function(e)
3986 Roo.log('trigger click');
3988 var target = e.getTarget();
3990 Roo.log(target.nodeName.toLowerCase());
3992 if(target.nodeName.toLowerCase() === 'i'){
3998 onTriggerPress : function(e)
4000 Roo.log('trigger press');
4001 //Roo.log(e.getTarget());
4002 // Roo.log(this.triggerEl.dom);
4004 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4005 var pel = Roo.get(e.getTarget());
4006 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4007 Roo.log('is treeview or dropdown?');
4011 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4015 if (this.isVisible()) {
4021 this.show(this.triggerEl, this.align, false);
4024 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4031 hideMenuItems : function()
4033 Roo.log("hide Menu Items");
4038 this.el.select('.open',true).each(function(aa) {
4040 aa.removeClass('open');
4044 addxtypeChild : function (tree, cntr) {
4045 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4047 this.menuitems.add(comp);
4059 this.getEl().dom.innerHTML = '';
4060 this.menuitems.clear();
4074 * @class Roo.bootstrap.MenuItem
4075 * @extends Roo.bootstrap.Component
4076 * Bootstrap MenuItem class
4077 * @cfg {String} html the menu label
4078 * @cfg {String} href the link
4079 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4080 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4081 * @cfg {Boolean} active used on sidebars to highlight active itesm
4082 * @cfg {String} fa favicon to show on left of menu item.
4083 * @cfg {Roo.bootsrap.Menu} menu the child menu.
4087 * Create a new MenuItem
4088 * @param {Object} config The config object
4092 Roo.bootstrap.MenuItem = function(config){
4093 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4098 * The raw click event for the entire grid.
4099 * @param {Roo.bootstrap.MenuItem} this
4100 * @param {Roo.EventObject} e
4106 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
4110 preventDefault: false,
4111 isContainer : false,
4115 getAutoCreate : function(){
4117 if(this.isContainer){
4120 cls: 'dropdown-menu-item '
4130 cls : 'dropdown-item',
4135 if (this.fa !== false) {
4138 cls : 'fa fa-' + this.fa
4147 cls: 'dropdown-menu-item',
4150 if (this.parent().type == 'treeview') {
4151 cfg.cls = 'treeview-menu';
4154 cfg.cls += ' active';
4159 anc.href = this.href || cfg.cn[0].href ;
4160 ctag.html = this.html || cfg.cn[0].html ;
4164 initEvents: function()
4166 if (this.parent().type == 'treeview') {
4167 this.el.select('a').on('click', this.onClick, this);
4171 this.menu.parentType = this.xtype;
4172 this.menu.triggerEl = this.el;
4173 this.menu = this.addxtype(Roo.apply({}, this.menu));
4177 onClick : function(e)
4179 Roo.log('item on click ');
4181 if(this.preventDefault){
4184 //this.parent().hideMenuItems();
4186 this.fireEvent('click', this, e);
4205 * @class Roo.bootstrap.MenuSeparator
4206 * @extends Roo.bootstrap.Component
4207 * Bootstrap MenuSeparator class
4210 * Create a new MenuItem
4211 * @param {Object} config The config object
4215 Roo.bootstrap.MenuSeparator = function(config){
4216 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4219 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
4221 getAutoCreate : function(){
4240 * @class Roo.bootstrap.Modal
4241 * @extends Roo.bootstrap.Component
4242 * Bootstrap Modal class
4243 * @cfg {String} title Title of dialog
4244 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4245 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
4246 * @cfg {Boolean} specificTitle default false
4247 * @cfg {Array} buttons Array of buttons or standard button set..
4248 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4249 * @cfg {Boolean} animate default true
4250 * @cfg {Boolean} allow_close default true
4251 * @cfg {Boolean} fitwindow default false
4252 * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4253 * @cfg {Number} width fixed width - usefull for chrome extension only really.
4254 * @cfg {Number} height fixed height - usefull for chrome extension only really.
4255 * @cfg {String} size (sm|lg|xl) default empty
4256 * @cfg {Number} max_width set the max width of modal
4257 * @cfg {Boolean} editableTitle can the title be edited
4262 * Create a new Modal Dialog
4263 * @param {Object} config The config object
4266 Roo.bootstrap.Modal = function(config){
4267 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4272 * The raw btnclick event for the button
4273 * @param {Roo.EventObject} e
4278 * Fire when dialog resize
4279 * @param {Roo.bootstrap.Modal} this
4280 * @param {Roo.EventObject} e
4284 * @event titlechanged
4285 * Fire when the editable title has been changed
4286 * @param {Roo.bootstrap.Modal} this
4287 * @param {Roo.EventObject} value
4289 "titlechanged" : true
4292 this.buttons = this.buttons || [];
4295 this.tmpl = Roo.factory(this.tmpl);
4300 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
4302 title : 'test dialog',
4312 specificTitle: false,
4314 buttonPosition: 'right',
4336 editableTitle : false,
4338 onRender : function(ct, position)
4340 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4343 var cfg = Roo.apply({}, this.getAutoCreate());
4346 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4348 //if (!cfg.name.length) {
4352 cfg.cls += ' ' + this.cls;
4355 cfg.style = this.style;
4357 this.el = Roo.get(document.body).createChild(cfg, position);
4359 //var type = this.el.dom.type;
4362 if(this.tabIndex !== undefined){
4363 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4366 this.dialogEl = this.el.select('.modal-dialog',true).first();
4367 this.bodyEl = this.el.select('.modal-body',true).first();
4368 this.closeEl = this.el.select('.modal-header .close', true).first();
4369 this.headerEl = this.el.select('.modal-header',true).first();
4370 this.titleEl = this.el.select('.modal-title',true).first();
4371 this.footerEl = this.el.select('.modal-footer',true).first();
4373 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4375 //this.el.addClass("x-dlg-modal");
4377 if (this.buttons.length) {
4378 Roo.each(this.buttons, function(bb) {
4379 var b = Roo.apply({}, bb);
4380 b.xns = b.xns || Roo.bootstrap;
4381 b.xtype = b.xtype || 'Button';
4382 if (typeof(b.listeners) == 'undefined') {
4383 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4386 var btn = Roo.factory(b);
4388 btn.render(this.getButtonContainer());
4392 // render the children.
4395 if(typeof(this.items) != 'undefined'){
4396 var items = this.items;
4399 for(var i =0;i < items.length;i++) {
4400 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4404 this.items = nitems;
4406 // where are these used - they used to be body/close/footer
4410 //this.el.addClass([this.fieldClass, this.cls]);
4414 getAutoCreate : function()
4416 // we will default to modal-body-overflow - might need to remove or make optional later.
4418 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''),
4419 html : this.html || ''
4424 cls : 'modal-title',
4428 if(this.specificTitle){ // WTF is this?
4433 if (this.allow_close && Roo.bootstrap.version == 3) {
4443 if (this.editableTitle) {
4445 cls: 'form-control roo-editable-title d-none',
4451 if (this.allow_close && Roo.bootstrap.version == 4) {
4461 if(this.size.length){
4462 size = 'modal-' + this.size;
4465 var footer = Roo.bootstrap.version == 3 ?
4467 cls : 'modal-footer',
4471 cls: 'btn-' + this.buttonPosition
4476 { // BS4 uses mr-auto on left buttons....
4477 cls : 'modal-footer'
4488 cls: "modal-dialog " + size,
4491 cls : "modal-content",
4494 cls : 'modal-header',
4509 modal.cls += ' fade';
4515 getChildContainer : function() {
4520 getButtonContainer : function() {
4522 return Roo.bootstrap.version == 4 ?
4523 this.el.select('.modal-footer',true).first()
4524 : this.el.select('.modal-footer div',true).first();
4527 initEvents : function()
4529 if (this.allow_close) {
4530 this.closeEl.on('click', this.hide, this);
4532 Roo.EventManager.onWindowResize(this.resize, this, true);
4533 if (this.editableTitle) {
4534 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4535 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4536 this.headerEditEl.on('keyup', function(e) {
4537 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4538 this.toggleHeaderInput(false)
4541 this.headerEditEl.on('blur', function(e) {
4542 this.toggleHeaderInput(false)
4551 this.maskEl.setSize(
4552 Roo.lib.Dom.getViewWidth(true),
4553 Roo.lib.Dom.getViewHeight(true)
4556 if (this.fitwindow) {
4558 this.dialogEl.setStyle( { 'max-width' : '100%' });
4560 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4561 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4566 if(this.max_width !== 0) {
4568 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4571 this.setSize(w, this.height);
4575 if(this.max_height) {
4576 this.setSize(w,Math.min(
4578 Roo.lib.Dom.getViewportHeight(true) - 60
4584 if(!this.fit_content) {
4585 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4589 this.setSize(w, Math.min(
4591 this.headerEl.getHeight() +
4592 this.footerEl.getHeight() +
4593 this.getChildHeight(this.bodyEl.dom.childNodes),
4594 Roo.lib.Dom.getViewportHeight(true) - 60)
4600 setSize : function(w,h)
4611 if (!this.rendered) {
4614 this.toggleHeaderInput(false);
4615 //this.el.setStyle('display', 'block');
4616 this.el.removeClass('hideing');
4617 this.el.dom.style.display='block';
4619 Roo.get(document.body).addClass('modal-open');
4621 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4624 this.el.addClass('show');
4625 this.el.addClass('in');
4628 this.el.addClass('show');
4629 this.el.addClass('in');
4632 // not sure how we can show data in here..
4634 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4637 Roo.get(document.body).addClass("x-body-masked");
4639 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4640 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4641 this.maskEl.dom.style.display = 'block';
4642 this.maskEl.addClass('show');
4647 this.fireEvent('show', this);
4649 // set zindex here - otherwise it appears to be ignored...
4650 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4653 this.items.forEach( function(e) {
4654 e.layout ? e.layout() : false;
4662 if(this.fireEvent("beforehide", this) !== false){
4664 this.maskEl.removeClass('show');
4666 this.maskEl.dom.style.display = '';
4667 Roo.get(document.body).removeClass("x-body-masked");
4668 this.el.removeClass('in');
4669 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4671 if(this.animate){ // why
4672 this.el.addClass('hideing');
4673 this.el.removeClass('show');
4675 if (!this.el.hasClass('hideing')) {
4676 return; // it's been shown again...
4679 this.el.dom.style.display='';
4681 Roo.get(document.body).removeClass('modal-open');
4682 this.el.removeClass('hideing');
4686 this.el.removeClass('show');
4687 this.el.dom.style.display='';
4688 Roo.get(document.body).removeClass('modal-open');
4691 this.fireEvent('hide', this);
4694 isVisible : function()
4697 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4701 addButton : function(str, cb)
4705 var b = Roo.apply({}, { html : str } );
4706 b.xns = b.xns || Roo.bootstrap;
4707 b.xtype = b.xtype || 'Button';
4708 if (typeof(b.listeners) == 'undefined') {
4709 b.listeners = { click : cb.createDelegate(this) };
4712 var btn = Roo.factory(b);
4714 btn.render(this.getButtonContainer());
4720 setDefaultButton : function(btn)
4722 //this.el.select('.modal-footer').()
4725 resizeTo: function(w,h)
4727 this.dialogEl.setWidth(w);
4729 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4731 this.bodyEl.setHeight(h - diff);
4733 this.fireEvent('resize', this);
4736 setContentSize : function(w, h)
4740 onButtonClick: function(btn,e)
4743 this.fireEvent('btnclick', btn.name, e);
4746 * Set the title of the Dialog
4747 * @param {String} str new Title
4749 setTitle: function(str) {
4750 this.titleEl.dom.innerHTML = str;
4754 * Set the body of the Dialog
4755 * @param {String} str new Title
4757 setBody: function(str) {
4758 this.bodyEl.dom.innerHTML = str;
4761 * Set the body of the Dialog using the template
4762 * @param {Obj} data - apply this data to the template and replace the body contents.
4764 applyBody: function(obj)
4767 Roo.log("Error - using apply Body without a template");
4770 this.tmpl.overwrite(this.bodyEl, obj);
4773 getChildHeight : function(child_nodes)
4777 child_nodes.length == 0
4782 var child_height = 0;
4784 for(var i = 0; i < child_nodes.length; i++) {
4787 * for modal with tabs...
4788 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4790 var layout_childs = child_nodes[i].childNodes;
4792 for(var j = 0; j < layout_childs.length; j++) {
4794 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4796 var layout_body_childs = layout_childs[j].childNodes;
4798 for(var k = 0; k < layout_body_childs.length; k++) {
4800 if(layout_body_childs[k].classList.contains('navbar')) {
4801 child_height += layout_body_childs[k].offsetHeight;
4805 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4807 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4809 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4811 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4812 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4827 child_height += child_nodes[i].offsetHeight;
4828 // Roo.log(child_nodes[i].offsetHeight);
4831 return child_height;
4833 toggleHeaderInput : function(is_edit)
4835 if (!this.editableTitle) {
4836 return; // not editable.
4838 if (is_edit && this.is_header_editing) {
4839 return; // already editing..
4843 this.headerEditEl.dom.value = this.title;
4844 this.headerEditEl.removeClass('d-none');
4845 this.headerEditEl.dom.focus();
4846 this.titleEl.addClass('d-none');
4848 this.is_header_editing = true;
4851 // flip back to not editing.
4852 this.title = this.headerEditEl.dom.value;
4853 this.headerEditEl.addClass('d-none');
4854 this.titleEl.removeClass('d-none');
4855 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4856 this.is_header_editing = false;
4857 this.fireEvent('titlechanged', this, this.title);
4866 Roo.apply(Roo.bootstrap.Modal, {
4868 * Button config that displays a single OK button
4877 * Button config that displays Yes and No buttons
4893 * Button config that displays OK and Cancel buttons
4908 * Button config that displays Yes, No and Cancel buttons
4933 * messagebox - can be used as a replace
4937 * @class Roo.MessageBox
4938 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4942 Roo.Msg.alert('Status', 'Changes saved successfully.');
4944 // Prompt for user data:
4945 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4947 // process text value...
4951 // Show a dialog using config options:
4953 title:'Save Changes?',
4954 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4955 buttons: Roo.Msg.YESNOCANCEL,
4962 Roo.bootstrap.MessageBox = function(){
4963 var dlg, opt, mask, waitTimer;
4964 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4965 var buttons, activeTextEl, bwidth;
4969 var handleButton = function(button){
4971 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4975 var handleHide = function(){
4977 dlg.el.removeClass(opt.cls);
4980 // Roo.TaskMgr.stop(waitTimer);
4981 // waitTimer = null;
4986 var updateButtons = function(b){
4989 buttons["ok"].hide();
4990 buttons["cancel"].hide();
4991 buttons["yes"].hide();
4992 buttons["no"].hide();
4993 dlg.footerEl.hide();
4997 dlg.footerEl.show();
4998 for(var k in buttons){
4999 if(typeof buttons[k] != "function"){
5002 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5003 width += buttons[k].el.getWidth()+15;
5013 var handleEsc = function(d, k, e){
5014 if(opt && opt.closable !== false){
5024 * Returns a reference to the underlying {@link Roo.BasicDialog} element
5025 * @return {Roo.BasicDialog} The BasicDialog element
5027 getDialog : function(){
5029 dlg = new Roo.bootstrap.Modal( {
5032 //constraintoviewport:false,
5034 //collapsible : false,
5039 //buttonAlign:"center",
5040 closeClick : function(){
5041 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5044 handleButton("cancel");
5049 dlg.on("hide", handleHide);
5051 //dlg.addKeyListener(27, handleEsc);
5053 this.buttons = buttons;
5054 var bt = this.buttonText;
5055 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5056 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5057 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5058 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5060 bodyEl = dlg.bodyEl.createChild({
5062 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5063 '<textarea class="roo-mb-textarea"></textarea>' +
5064 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
5066 msgEl = bodyEl.dom.firstChild;
5067 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5068 textboxEl.enableDisplayMode();
5069 textboxEl.addKeyListener([10,13], function(){
5070 if(dlg.isVisible() && opt && opt.buttons){
5073 }else if(opt.buttons.yes){
5074 handleButton("yes");
5078 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5079 textareaEl.enableDisplayMode();
5080 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5081 progressEl.enableDisplayMode();
5083 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5084 var pf = progressEl.dom.firstChild;
5086 pp = Roo.get(pf.firstChild);
5087 pp.setHeight(pf.offsetHeight);
5095 * Updates the message box body text
5096 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5097 * the XHTML-compliant non-breaking space character '&#160;')
5098 * @return {Roo.MessageBox} This message box
5100 updateText : function(text)
5102 if(!dlg.isVisible() && !opt.width){
5103 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5104 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5106 msgEl.innerHTML = text || ' ';
5108 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5109 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5111 Math.min(opt.width || cw , this.maxWidth),
5112 Math.max(opt.minWidth || this.minWidth, bwidth)
5115 activeTextEl.setWidth(w);
5117 if(dlg.isVisible()){
5118 dlg.fixedcenter = false;
5120 // to big, make it scroll. = But as usual stupid IE does not support
5123 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5124 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5125 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5127 bodyEl.dom.style.height = '';
5128 bodyEl.dom.style.overflowY = '';
5131 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5133 bodyEl.dom.style.overflowX = '';
5136 dlg.setContentSize(w, bodyEl.getHeight());
5137 if(dlg.isVisible()){
5138 dlg.fixedcenter = true;
5144 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
5145 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5146 * @param {Number} value Any number between 0 and 1 (e.g., .5)
5147 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5148 * @return {Roo.MessageBox} This message box
5150 updateProgress : function(value, text){
5152 this.updateText(text);
5155 if (pp) { // weird bug on my firefox - for some reason this is not defined
5156 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5157 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5163 * Returns true if the message box is currently displayed
5164 * @return {Boolean} True if the message box is visible, else false
5166 isVisible : function(){
5167 return dlg && dlg.isVisible();
5171 * Hides the message box if it is displayed
5174 if(this.isVisible()){
5180 * Displays a new message box, or reinitializes an existing message box, based on the config options
5181 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5182 * The following config object properties are supported:
5184 Property Type Description
5185 ---------- --------------- ------------------------------------------------------------------------------------
5186 animEl String/Element An id or Element from which the message box should animate as it opens and
5187 closes (defaults to undefined)
5188 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5189 cancel:'Bar'}), or false to not show any buttons (defaults to false)
5190 closable Boolean False to hide the top-right close button (defaults to true). Note that
5191 progress and wait dialogs will ignore this property and always hide the
5192 close button as they can only be closed programmatically.
5193 cls String A custom CSS class to apply to the message box element
5194 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
5195 displayed (defaults to 75)
5196 fn Function A callback function to execute after closing the dialog. The arguments to the
5197 function will be btn (the name of the button that was clicked, if applicable,
5198 e.g. "ok"), and text (the value of the active text field, if applicable).
5199 Progress and wait dialogs will ignore this option since they do not respond to
5200 user actions and can only be closed programmatically, so any required function
5201 should be called by the same code after it closes the dialog.
5202 icon String A CSS class that provides a background image to be used as an icon for
5203 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5204 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
5205 minWidth Number The minimum width in pixels of the message box (defaults to 100)
5206 modal Boolean False to allow user interaction with the page while the message box is
5207 displayed (defaults to true)
5208 msg String A string that will replace the existing message box body text (defaults
5209 to the XHTML-compliant non-breaking space character ' ')
5210 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
5211 progress Boolean True to display a progress bar (defaults to false)
5212 progressText String The text to display inside the progress bar if progress = true (defaults to '')
5213 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
5214 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
5215 title String The title text
5216 value String The string value to set into the active textbox element if displayed
5217 wait Boolean True to display a progress bar (defaults to false)
5218 width Number The width of the dialog in pixels
5225 msg: 'Please enter your address:',
5227 buttons: Roo.MessageBox.OKCANCEL,
5230 animEl: 'addAddressBtn'
5233 * @param {Object} config Configuration options
5234 * @return {Roo.MessageBox} This message box
5236 show : function(options)
5239 // this causes nightmares if you show one dialog after another
5240 // especially on callbacks..
5242 if(this.isVisible()){
5245 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5246 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
5247 Roo.log("New Dialog Message:" + options.msg )
5248 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5249 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5252 var d = this.getDialog();
5254 d.setTitle(opt.title || " ");
5255 d.closeEl.setDisplayed(opt.closable !== false);
5256 activeTextEl = textboxEl;
5257 opt.prompt = opt.prompt || (opt.multiline ? true : false);
5262 textareaEl.setHeight(typeof opt.multiline == "number" ?
5263 opt.multiline : this.defaultTextHeight);
5264 activeTextEl = textareaEl;
5273 progressEl.setDisplayed(opt.progress === true);
5275 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5277 this.updateProgress(0);
5278 activeTextEl.dom.value = opt.value || "";
5280 dlg.setDefaultButton(activeTextEl);
5282 var bs = opt.buttons;
5286 }else if(bs && bs.yes){
5287 db = buttons["yes"];
5289 dlg.setDefaultButton(db);
5291 bwidth = updateButtons(opt.buttons);
5292 this.updateText(opt.msg);
5294 d.el.addClass(opt.cls);
5296 d.proxyDrag = opt.proxyDrag === true;
5297 d.modal = opt.modal !== false;
5298 d.mask = opt.modal !== false ? mask : false;
5300 // force it to the end of the z-index stack so it gets a cursor in FF
5301 document.body.appendChild(dlg.el.dom);
5302 d.animateTarget = null;
5303 d.show(options.animEl);
5309 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5310 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5311 * and closing the message box when the process is complete.
5312 * @param {String} title The title bar text
5313 * @param {String} msg The message box body text
5314 * @return {Roo.MessageBox} This message box
5316 progress : function(title, msg){
5323 minWidth: this.minProgressWidth,
5330 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5331 * If a callback function is passed it will be called after the user clicks the button, and the
5332 * id of the button that was clicked will be passed as the only parameter to the callback
5333 * (could also be the top-right close button).
5334 * @param {String} title The title bar text
5335 * @param {String} msg The message box body text
5336 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5337 * @param {Object} scope (optional) The scope of the callback function
5338 * @return {Roo.MessageBox} This message box
5340 alert : function(title, msg, fn, scope)
5355 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5356 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5357 * You are responsible for closing the message box when the process is complete.
5358 * @param {String} msg The message box body text
5359 * @param {String} title (optional) The title bar text
5360 * @return {Roo.MessageBox} This message box
5362 wait : function(msg, title){
5373 waitTimer = Roo.TaskMgr.start({
5375 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5383 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5384 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5385 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5386 * @param {String} title The title bar text
5387 * @param {String} msg The message box body text
5388 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5389 * @param {Object} scope (optional) The scope of the callback function
5390 * @return {Roo.MessageBox} This message box
5392 confirm : function(title, msg, fn, scope){
5396 buttons: this.YESNO,
5405 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5406 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5407 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5408 * (could also be the top-right close button) and the text that was entered will be passed as the two
5409 * parameters to the callback.
5410 * @param {String} title The title bar text
5411 * @param {String} msg The message box body text
5412 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5413 * @param {Object} scope (optional) The scope of the callback function
5414 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5415 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5416 * @return {Roo.MessageBox} This message box
5418 prompt : function(title, msg, fn, scope, multiline){
5422 buttons: this.OKCANCEL,
5427 multiline: multiline,
5434 * Button config that displays a single OK button
5439 * Button config that displays Yes and No buttons
5442 YESNO : {yes:true, no:true},
5444 * Button config that displays OK and Cancel buttons
5447 OKCANCEL : {ok:true, cancel:true},
5449 * Button config that displays Yes, No and Cancel buttons
5452 YESNOCANCEL : {yes:true, no:true, cancel:true},
5455 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5458 defaultTextHeight : 75,
5460 * The maximum width in pixels of the message box (defaults to 600)
5465 * The minimum width in pixels of the message box (defaults to 100)
5470 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5471 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5474 minProgressWidth : 250,
5476 * An object containing the default button text strings that can be overriden for localized language support.
5477 * Supported properties are: ok, cancel, yes and no.
5478 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5491 * Shorthand for {@link Roo.MessageBox}
5493 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5494 Roo.Msg = Roo.Msg || Roo.MessageBox;
5503 * @class Roo.bootstrap.Navbar
5504 * @extends Roo.bootstrap.Component
5505 * Bootstrap Navbar class
5508 * Create a new Navbar
5509 * @param {Object} config The config object
5513 Roo.bootstrap.Navbar = function(config){
5514 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5518 * @event beforetoggle
5519 * Fire before toggle the menu
5520 * @param {Roo.EventObject} e
5522 "beforetoggle" : true
5526 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
5535 getAutoCreate : function(){
5538 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5542 initEvents :function ()
5544 //Roo.log(this.el.select('.navbar-toggle',true));
5545 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5552 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5554 var size = this.el.getSize();
5555 this.maskEl.setSize(size.width, size.height);
5556 this.maskEl.enableDisplayMode("block");
5565 getChildContainer : function()
5567 if (this.el && this.el.select('.collapse').getCount()) {
5568 return this.el.select('.collapse',true).first();
5583 onToggle : function()
5586 if(this.fireEvent('beforetoggle', this) === false){
5589 var ce = this.el.select('.navbar-collapse',true).first();
5591 if (!ce.hasClass('show')) {
5601 * Expand the navbar pulldown
5603 expand : function ()
5606 var ce = this.el.select('.navbar-collapse',true).first();
5607 if (ce.hasClass('collapsing')) {
5610 ce.dom.style.height = '';
5612 ce.addClass('in'); // old...
5613 ce.removeClass('collapse');
5614 ce.addClass('show');
5615 var h = ce.getHeight();
5617 ce.removeClass('show');
5618 // at this point we should be able to see it..
5619 ce.addClass('collapsing');
5621 ce.setHeight(0); // resize it ...
5622 ce.on('transitionend', function() {
5623 //Roo.log('done transition');
5624 ce.removeClass('collapsing');
5625 ce.addClass('show');
5626 ce.removeClass('collapse');
5628 ce.dom.style.height = '';
5629 }, this, { single: true} );
5631 ce.dom.scrollTop = 0;
5634 * Collapse the navbar pulldown
5636 collapse : function()
5638 var ce = this.el.select('.navbar-collapse',true).first();
5640 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5641 // it's collapsed or collapsing..
5644 ce.removeClass('in'); // old...
5645 ce.setHeight(ce.getHeight());
5646 ce.removeClass('show');
5647 ce.addClass('collapsing');
5649 ce.on('transitionend', function() {
5650 ce.dom.style.height = '';
5651 ce.removeClass('collapsing');
5652 ce.addClass('collapse');
5653 }, this, { single: true} );
5673 * @class Roo.bootstrap.NavSimplebar
5674 * @extends Roo.bootstrap.Navbar
5675 * Bootstrap Sidebar class
5677 * @cfg {Boolean} inverse is inverted color
5679 * @cfg {String} type (nav | pills | tabs)
5680 * @cfg {Boolean} arrangement stacked | justified
5681 * @cfg {String} align (left | right) alignment
5683 * @cfg {Boolean} main (true|false) main nav bar? default false
5684 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5686 * @cfg {String} tag (header|footer|nav|div) default is nav
5688 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5692 * Create a new Sidebar
5693 * @param {Object} config The config object
5697 Roo.bootstrap.NavSimplebar = function(config){
5698 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5701 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5717 getAutoCreate : function(){
5721 tag : this.tag || 'div',
5722 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5724 if (['light','white'].indexOf(this.weight) > -1) {
5725 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5727 cfg.cls += ' bg-' + this.weight;
5730 cfg.cls += ' navbar-inverse';
5734 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5736 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5745 cls: 'nav nav-' + this.xtype,
5751 this.type = this.type || 'nav';
5752 if (['tabs','pills'].indexOf(this.type) != -1) {
5753 cfg.cn[0].cls += ' nav-' + this.type
5757 if (this.type!=='nav') {
5758 Roo.log('nav type must be nav/tabs/pills')
5760 cfg.cn[0].cls += ' navbar-nav'
5766 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5767 cfg.cn[0].cls += ' nav-' + this.arrangement;
5771 if (this.align === 'right') {
5772 cfg.cn[0].cls += ' navbar-right';
5797 * navbar-expand-md fixed-top
5801 * @class Roo.bootstrap.NavHeaderbar
5802 * @extends Roo.bootstrap.NavSimplebar
5803 * Bootstrap Sidebar class
5805 * @cfg {String} brand what is brand
5806 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5807 * @cfg {String} brand_href href of the brand
5808 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5809 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5810 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5811 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5814 * Create a new Sidebar
5815 * @param {Object} config The config object
5819 Roo.bootstrap.NavHeaderbar = function(config){
5820 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5824 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5831 desktopCenter : false,
5834 getAutoCreate : function(){
5837 tag: this.nav || 'nav',
5838 cls: 'navbar navbar-expand-md',
5844 if (this.desktopCenter) {
5845 cn.push({cls : 'container', cn : []});
5853 cls: 'navbar-toggle navbar-toggler',
5854 'data-toggle': 'collapse',
5859 html: 'Toggle navigation'
5863 cls: 'icon-bar navbar-toggler-icon'
5876 cn.push( Roo.bootstrap.version == 4 ? btn : {
5878 cls: 'navbar-header',
5887 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5891 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5893 if (['light','white'].indexOf(this.weight) > -1) {
5894 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5896 cfg.cls += ' bg-' + this.weight;
5899 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5900 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5902 // tag can override this..
5904 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5907 if (this.brand !== '') {
5908 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5909 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5911 href: this.brand_href ? this.brand_href : '#',
5912 cls: 'navbar-brand',
5920 cfg.cls += ' main-nav';
5928 getHeaderChildContainer : function()
5930 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5931 return this.el.select('.navbar-header',true).first();
5934 return this.getChildContainer();
5937 getChildContainer : function()
5940 return this.el.select('.roo-navbar-collapse',true).first();
5945 initEvents : function()
5947 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5949 if (this.autohide) {
5954 Roo.get(document).on('scroll',function(e) {
5955 var ns = Roo.get(document).getScroll().top;
5956 var os = prevScroll;
5960 ft.removeClass('slideDown');
5961 ft.addClass('slideUp');
5964 ft.removeClass('slideUp');
5965 ft.addClass('slideDown');
5986 * @class Roo.bootstrap.NavSidebar
5987 * @extends Roo.bootstrap.Navbar
5988 * Bootstrap Sidebar class
5991 * Create a new Sidebar
5992 * @param {Object} config The config object
5996 Roo.bootstrap.NavSidebar = function(config){
5997 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
6000 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
6002 sidebar : true, // used by Navbar Item and NavbarGroup at present...
6004 getAutoCreate : function(){
6009 cls: 'sidebar sidebar-nav'
6031 * @class Roo.bootstrap.NavGroup
6032 * @extends Roo.bootstrap.Component
6033 * Bootstrap NavGroup class
6034 * @cfg {String} align (left|right)
6035 * @cfg {Boolean} inverse
6036 * @cfg {String} type (nav|pills|tab) default nav
6037 * @cfg {String} navId - reference Id for navbar.
6038 * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6041 * Create a new nav group
6042 * @param {Object} config The config object
6045 Roo.bootstrap.NavGroup = function(config){
6046 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
6049 Roo.bootstrap.NavGroup.register(this);
6053 * Fires when the active item changes
6054 * @param {Roo.bootstrap.NavGroup} this
6055 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6056 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
6063 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
6075 getAutoCreate : function()
6077 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6083 if (Roo.bootstrap.version == 4) {
6084 if (['tabs','pills'].indexOf(this.type) != -1) {
6085 cfg.cls += ' nav-' + this.type;
6087 // trying to remove so header bar can right align top?
6088 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6089 // do not use on header bar...
6090 cfg.cls += ' navbar-nav';
6095 if (['tabs','pills'].indexOf(this.type) != -1) {
6096 cfg.cls += ' nav-' + this.type
6098 if (this.type !== 'nav') {
6099 Roo.log('nav type must be nav/tabs/pills')
6101 cfg.cls += ' navbar-nav'
6105 if (this.parent() && this.parent().sidebar) {
6108 cls: 'dashboard-menu sidebar-menu'
6114 if (this.form === true) {
6117 cls: 'navbar-form form-inline'
6119 //nav navbar-right ml-md-auto
6120 if (this.align === 'right') {
6121 cfg.cls += ' navbar-right ml-md-auto';
6123 cfg.cls += ' navbar-left';
6127 if (this.align === 'right') {
6128 cfg.cls += ' navbar-right ml-md-auto';
6130 cfg.cls += ' mr-auto';
6134 cfg.cls += ' navbar-inverse';
6142 * sets the active Navigation item
6143 * @param {Roo.bootstrap.NavItem} the new current navitem
6145 setActiveItem : function(item)
6148 Roo.each(this.navItems, function(v){
6153 v.setActive(false, true);
6160 item.setActive(true, true);
6161 this.fireEvent('changed', this, item, prev);
6166 * gets the active Navigation item
6167 * @return {Roo.bootstrap.NavItem} the current navitem
6169 getActive : function()
6173 Roo.each(this.navItems, function(v){
6184 indexOfNav : function()
6188 Roo.each(this.navItems, function(v,i){
6199 * adds a Navigation item
6200 * @param {Roo.bootstrap.NavItem} the navitem to add
6202 addItem : function(cfg)
6204 if (this.form && Roo.bootstrap.version == 4) {
6207 var cn = new Roo.bootstrap.NavItem(cfg);
6209 cn.parentId = this.id;
6210 cn.onRender(this.el, null);
6214 * register a Navigation item
6215 * @param {Roo.bootstrap.NavItem} the navitem to add
6217 register : function(item)
6219 this.navItems.push( item);
6220 item.navId = this.navId;
6225 * clear all the Navigation item
6228 clearAll : function()
6231 this.el.dom.innerHTML = '';
6234 getNavItem: function(tabId)
6237 Roo.each(this.navItems, function(e) {
6238 if (e.tabId == tabId) {
6248 setActiveNext : function()
6250 var i = this.indexOfNav(this.getActive());
6251 if (i > this.navItems.length) {
6254 this.setActiveItem(this.navItems[i+1]);
6256 setActivePrev : function()
6258 var i = this.indexOfNav(this.getActive());
6262 this.setActiveItem(this.navItems[i-1]);
6264 clearWasActive : function(except) {
6265 Roo.each(this.navItems, function(e) {
6266 if (e.tabId != except.tabId && e.was_active) {
6267 e.was_active = false;
6274 getWasActive : function ()
6277 Roo.each(this.navItems, function(e) {
6292 Roo.apply(Roo.bootstrap.NavGroup, {
6296 * register a Navigation Group
6297 * @param {Roo.bootstrap.NavGroup} the navgroup to add
6299 register : function(navgrp)
6301 this.groups[navgrp.navId] = navgrp;
6305 * fetch a Navigation Group based on the navigation ID
6306 * @param {string} the navgroup to add
6307 * @returns {Roo.bootstrap.NavGroup} the navgroup
6309 get: function(navId) {
6310 if (typeof(this.groups[navId]) == 'undefined') {
6312 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6314 return this.groups[navId] ;
6329 * @class Roo.bootstrap.NavItem
6330 * @extends Roo.bootstrap.Component
6331 * Bootstrap Navbar.NavItem class
6332 * @cfg {String} href link to
6333 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6334 * @cfg {Boolean} button_outline show and outlined button
6335 * @cfg {String} html content of button
6336 * @cfg {String} badge text inside badge
6337 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6338 * @cfg {String} glyphicon DEPRICATED - use fa
6339 * @cfg {String} icon DEPRICATED - use fa
6340 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6341 * @cfg {Boolean} active Is item active
6342 * @cfg {Boolean} disabled Is item disabled
6343 * @cfg {String} linkcls Link Class
6344 * @cfg {Boolean} preventDefault (true | false) default false
6345 * @cfg {String} tabId the tab that this item activates.
6346 * @cfg {String} tagtype (a|span) render as a href or span?
6347 * @cfg {Boolean} animateRef (true|false) link to element default false
6350 * Create a new Navbar Item
6351 * @param {Object} config The config object
6353 Roo.bootstrap.NavItem = function(config){
6354 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6359 * The raw click event for the entire grid.
6360 * @param {Roo.EventObject} e
6365 * Fires when the active item active state changes
6366 * @param {Roo.bootstrap.NavItem} this
6367 * @param {boolean} state the new state
6373 * Fires when scroll to element
6374 * @param {Roo.bootstrap.NavItem} this
6375 * @param {Object} options
6376 * @param {Roo.EventObject} e
6384 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
6393 preventDefault : false,
6401 button_outline : false,
6405 getAutoCreate : function(){
6412 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6415 cfg.cls += ' active' ;
6417 if (this.disabled) {
6418 cfg.cls += ' disabled';
6422 if (this.button_weight.length) {
6423 cfg.tag = this.href ? 'a' : 'button';
6424 cfg.html = this.html || '';
6425 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6427 cfg.href = this.href;
6430 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6432 cfg.cls += " nav-html";
6435 // menu .. should add dropdown-menu class - so no need for carat..
6437 if (this.badge !== '') {
6439 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6444 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6448 href : this.href || "#",
6449 html: this.html || '',
6453 if (this.tagtype == 'a') {
6454 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
6458 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6459 } else if (this.fa) {
6460 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6461 } else if(this.glyphicon) {
6462 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6464 cfg.cn[0].cls += " nav-html";
6468 cfg.cn[0].html += " <span class='caret'></span>";
6472 if (this.badge !== '') {
6473 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6481 onRender : function(ct, position)
6483 // Roo.log("Call onRender: " + this.xtype);
6484 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6488 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6489 this.navLink = this.el.select('.nav-link',true).first();
6490 this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6495 initEvents: function()
6497 if (typeof (this.menu) != 'undefined') {
6498 this.menu.parentType = this.xtype;
6499 this.menu.triggerEl = this.el;
6500 this.menu = this.addxtype(Roo.apply({}, this.menu));
6503 this.el.on('click', this.onClick, this);
6505 //if(this.tagtype == 'span'){
6506 // this.el.select('span',true).on('click', this.onClick, this);
6509 // at this point parent should be available..
6510 this.parent().register(this);
6513 onClick : function(e)
6515 if (e.getTarget('.dropdown-menu-item')) {
6516 // did you click on a menu itemm.... - then don't trigger onclick..
6521 this.preventDefault ||
6524 Roo.log("NavItem - prevent Default?");
6528 if (this.disabled) {
6532 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6533 if (tg && tg.transition) {
6534 Roo.log("waiting for the transitionend");
6540 //Roo.log("fire event clicked");
6541 if(this.fireEvent('click', this, e) === false){
6545 if(this.tagtype == 'span'){
6549 //Roo.log(this.href);
6550 var ael = this.el.select('a',true).first();
6553 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6554 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6555 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6556 return; // ignore... - it's a 'hash' to another page.
6558 Roo.log("NavItem - prevent Default?");
6560 this.scrollToElement(e);
6564 var p = this.parent();
6566 if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6567 if (typeof(p.setActiveItem) !== 'undefined') {
6568 p.setActiveItem(this);
6572 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6573 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6574 // remove the collapsed menu expand...
6575 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6579 isActive: function () {
6582 setActive : function(state, fire, is_was_active)
6584 if (this.active && !state && this.navId) {
6585 this.was_active = true;
6586 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6588 nv.clearWasActive(this);
6592 this.active = state;
6595 this.el.removeClass('active');
6596 this.navLink ? this.navLink.removeClass('active') : false;
6597 } else if (!this.el.hasClass('active')) {
6599 this.el.addClass('active');
6600 if (Roo.bootstrap.version == 4 && this.navLink ) {
6601 this.navLink.addClass('active');
6606 this.fireEvent('changed', this, state);
6609 // show a panel if it's registered and related..
6611 if (!this.navId || !this.tabId || !state || is_was_active) {
6615 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6619 var pan = tg.getPanelByName(this.tabId);
6623 // if we can not flip to new panel - go back to old nav highlight..
6624 if (false == tg.showPanel(pan)) {
6625 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6627 var onav = nv.getWasActive();
6629 onav.setActive(true, false, true);
6638 // this should not be here...
6639 setDisabled : function(state)
6641 this.disabled = state;
6643 this.el.removeClass('disabled');
6644 } else if (!this.el.hasClass('disabled')) {
6645 this.el.addClass('disabled');
6651 * Fetch the element to display the tooltip on.
6652 * @return {Roo.Element} defaults to this.el
6654 tooltipEl : function()
6656 return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
6659 scrollToElement : function(e)
6661 var c = document.body;
6664 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6666 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6667 c = document.documentElement;
6670 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6676 var o = target.calcOffsetsTo(c);
6683 this.fireEvent('scrollto', this, options, e);
6685 Roo.get(c).scrollTo('top', options.value, true);
6690 * Set the HTML (text content) of the item
6691 * @param {string} html content for the nav item
6693 setHtml : function(html)
6696 this.htmlEl.dom.innerHTML = html;
6708 * <span> icon </span>
6709 * <span> text </span>
6710 * <span>badge </span>
6714 * @class Roo.bootstrap.NavSidebarItem
6715 * @extends Roo.bootstrap.NavItem
6716 * Bootstrap Navbar.NavSidebarItem class
6717 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6718 * {Boolean} open is the menu open
6719 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6720 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6721 * {String} buttonSize (sm|md|lg)the extra classes for the button
6722 * {Boolean} showArrow show arrow next to the text (default true)
6724 * Create a new Navbar Button
6725 * @param {Object} config The config object
6727 Roo.bootstrap.NavSidebarItem = function(config){
6728 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6733 * The raw click event for the entire grid.
6734 * @param {Roo.EventObject} e
6739 * Fires when the active item active state changes
6740 * @param {Roo.bootstrap.NavSidebarItem} this
6741 * @param {boolean} state the new state
6749 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6751 badgeWeight : 'default',
6757 buttonWeight : 'default',
6763 getAutoCreate : function(){
6768 href : this.href || '#',
6774 if(this.buttonView){
6777 href : this.href || '#',
6778 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6791 cfg.cls += ' active';
6794 if (this.disabled) {
6795 cfg.cls += ' disabled';
6798 cfg.cls += ' open x-open';
6801 if (this.glyphicon || this.icon) {
6802 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6803 a.cn.push({ tag : 'i', cls : c }) ;
6806 if(!this.buttonView){
6809 html : this.html || ''
6816 if (this.badge !== '') {
6817 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6823 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6826 a.cls += ' dropdown-toggle treeview' ;
6832 initEvents : function()
6834 if (typeof (this.menu) != 'undefined') {
6835 this.menu.parentType = this.xtype;
6836 this.menu.triggerEl = this.el;
6837 this.menu = this.addxtype(Roo.apply({}, this.menu));
6840 this.el.on('click', this.onClick, this);
6842 if(this.badge !== ''){
6843 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6848 onClick : function(e)
6855 if(this.preventDefault){
6859 this.fireEvent('click', this, e);
6862 disable : function()
6864 this.setDisabled(true);
6869 this.setDisabled(false);
6872 setDisabled : function(state)
6874 if(this.disabled == state){
6878 this.disabled = state;
6881 this.el.addClass('disabled');
6885 this.el.removeClass('disabled');
6890 setActive : function(state)
6892 if(this.active == state){
6896 this.active = state;
6899 this.el.addClass('active');
6903 this.el.removeClass('active');
6908 isActive: function ()
6913 setBadge : function(str)
6919 this.badgeEl.dom.innerHTML = str;
6934 Roo.namespace('Roo.bootstrap.breadcrumb');
6938 * @class Roo.bootstrap.breadcrumb.Nav
6939 * @extends Roo.bootstrap.Component
6940 * Bootstrap Breadcrumb Nav Class
6942 * @children Roo.bootstrap.breadcrumb.Item
6945 * Create a new breadcrumb.Nav
6946 * @param {Object} config The config object
6950 Roo.bootstrap.breadcrumb.Nav = function(config){
6951 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6956 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
6958 getAutoCreate : function()
6975 initEvents: function()
6977 this.olEl = this.el.select('ol',true).first();
6979 getChildContainer : function()
6995 * @class Roo.bootstrap.breadcrumb.Nav
6996 * @extends Roo.bootstrap.Component
6997 * Bootstrap Breadcrumb Nav Class
6999 * @children Roo.bootstrap.breadcrumb.Component
7000 * @cfg {String} html the content of the link.
7001 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7002 * @cfg {Boolean} active is it active
7006 * Create a new breadcrumb.Nav
7007 * @param {Object} config The config object
7010 Roo.bootstrap.breadcrumb.Item = function(config){
7011 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7016 * The img click event for the img.
7017 * @param {Roo.EventObject} e
7024 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
7029 getAutoCreate : function()
7034 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7036 if (this.href !== false) {
7043 cfg.html = this.html;
7049 initEvents: function()
7052 this.el.select('a', true).first().on('click',this.onClick, this)
7056 onClick : function(e)
7059 this.fireEvent('click',this, e);
7072 * @class Roo.bootstrap.Row
7073 * @extends Roo.bootstrap.Component
7074 * Bootstrap Row class (contains columns...)
7078 * @param {Object} config The config object
7081 Roo.bootstrap.Row = function(config){
7082 Roo.bootstrap.Row.superclass.constructor.call(this, config);
7085 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
7087 getAutoCreate : function(){
7106 * @class Roo.bootstrap.Pagination
7107 * @extends Roo.bootstrap.Component
7108 * Bootstrap Pagination class
7109 * @cfg {String} size xs | sm | md | lg
7110 * @cfg {Boolean} inverse false | true
7113 * Create a new Pagination
7114 * @param {Object} config The config object
7117 Roo.bootstrap.Pagination = function(config){
7118 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7121 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
7127 getAutoCreate : function(){
7133 cfg.cls += ' inverse';
7139 cfg.cls += " " + this.cls;
7157 * @class Roo.bootstrap.PaginationItem
7158 * @extends Roo.bootstrap.Component
7159 * Bootstrap PaginationItem class
7160 * @cfg {String} html text
7161 * @cfg {String} href the link
7162 * @cfg {Boolean} preventDefault (true | false) default true
7163 * @cfg {Boolean} active (true | false) default false
7164 * @cfg {Boolean} disabled default false
7168 * Create a new PaginationItem
7169 * @param {Object} config The config object
7173 Roo.bootstrap.PaginationItem = function(config){
7174 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7179 * The raw click event for the entire grid.
7180 * @param {Roo.EventObject} e
7186 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
7190 preventDefault: true,
7195 getAutoCreate : function(){
7201 href : this.href ? this.href : '#',
7202 html : this.html ? this.html : ''
7212 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7216 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7222 initEvents: function() {
7224 this.el.on('click', this.onClick, this);
7227 onClick : function(e)
7229 Roo.log('PaginationItem on click ');
7230 if(this.preventDefault){
7238 this.fireEvent('click', this, e);
7254 * @class Roo.bootstrap.Slider
7255 * @extends Roo.bootstrap.Component
7256 * Bootstrap Slider class
7259 * Create a new Slider
7260 * @param {Object} config The config object
7263 Roo.bootstrap.Slider = function(config){
7264 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7267 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
7269 getAutoCreate : function(){
7273 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7277 cls: 'ui-slider-handle ui-state-default ui-corner-all'
7289 * Ext JS Library 1.1.1
7290 * Copyright(c) 2006-2007, Ext JS, LLC.
7292 * Originally Released Under LGPL - original licence link has changed is not relivant.
7295 * <script type="text/javascript">
7300 * @class Roo.grid.ColumnModel
7301 * @extends Roo.util.Observable
7302 * This is the default implementation of a ColumnModel used by the Grid. It defines
7303 * the columns in the grid.
7306 var colModel = new Roo.grid.ColumnModel([
7307 {header: "Ticker", width: 60, sortable: true, locked: true},
7308 {header: "Company Name", width: 150, sortable: true},
7309 {header: "Market Cap.", width: 100, sortable: true},
7310 {header: "$ Sales", width: 100, sortable: true, renderer: money},
7311 {header: "Employees", width: 100, sortable: true, resizable: false}
7316 * The config options listed for this class are options which may appear in each
7317 * individual column definition.
7318 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7320 * @param {Object} config An Array of column config objects. See this class's
7321 * config objects for details.
7323 Roo.grid.ColumnModel = function(config){
7325 * The config passed into the constructor
7327 this.config = []; //config;
7330 // if no id, create one
7331 // if the column does not have a dataIndex mapping,
7332 // map it to the order it is in the config
7333 for(var i = 0, len = config.length; i < len; i++){
7334 this.addColumn(config[i]);
7339 * The width of columns which have no width specified (defaults to 100)
7342 this.defaultWidth = 100;
7345 * Default sortable of columns which have no sortable specified (defaults to false)
7348 this.defaultSortable = false;
7352 * @event widthchange
7353 * Fires when the width of a column changes.
7354 * @param {ColumnModel} this
7355 * @param {Number} columnIndex The column index
7356 * @param {Number} newWidth The new width
7358 "widthchange": true,
7360 * @event headerchange
7361 * Fires when the text of a header changes.
7362 * @param {ColumnModel} this
7363 * @param {Number} columnIndex The column index
7364 * @param {Number} newText The new header text
7366 "headerchange": true,
7368 * @event hiddenchange
7369 * Fires when a column is hidden or "unhidden".
7370 * @param {ColumnModel} this
7371 * @param {Number} columnIndex The column index
7372 * @param {Boolean} hidden true if hidden, false otherwise
7374 "hiddenchange": true,
7376 * @event columnmoved
7377 * Fires when a column is moved.
7378 * @param {ColumnModel} this
7379 * @param {Number} oldIndex
7380 * @param {Number} newIndex
7382 "columnmoved" : true,
7384 * @event columlockchange
7385 * Fires when a column's locked state is changed
7386 * @param {ColumnModel} this
7387 * @param {Number} colIndex
7388 * @param {Boolean} locked true if locked
7390 "columnlockchange" : true
7392 Roo.grid.ColumnModel.superclass.constructor.call(this);
7394 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7396 * @cfg {String} header The header text to display in the Grid view.
7399 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7400 * {@link Roo.data.Record} definition from which to draw the column's value. If not
7401 * specified, the column's index is used as an index into the Record's data Array.
7404 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7405 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7408 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7409 * Defaults to the value of the {@link #defaultSortable} property.
7410 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7413 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
7416 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
7419 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7422 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7425 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7426 * given the cell's data value. See {@link #setRenderer}. If not specified, the
7427 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7428 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7431 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
7434 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
7437 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
7440 * @cfg {String} cursor (Optional)
7443 * @cfg {String} tooltip (Optional)
7446 * @cfg {Number} xs (Optional)
7449 * @cfg {Number} sm (Optional)
7452 * @cfg {Number} md (Optional)
7455 * @cfg {Number} lg (Optional)
7458 * Returns the id of the column at the specified index.
7459 * @param {Number} index The column index
7460 * @return {String} the id
7462 getColumnId : function(index){
7463 return this.config[index].id;
7467 * Returns the column for a specified id.
7468 * @param {String} id The column id
7469 * @return {Object} the column
7471 getColumnById : function(id){
7472 return this.lookup[id];
7477 * Returns the column Object for a specified dataIndex.
7478 * @param {String} dataIndex The column dataIndex
7479 * @return {Object|Boolean} the column or false if not found
7481 getColumnByDataIndex: function(dataIndex){
7482 var index = this.findColumnIndex(dataIndex);
7483 return index > -1 ? this.config[index] : false;
7487 * Returns the index for a specified column id.
7488 * @param {String} id The column id
7489 * @return {Number} the index, or -1 if not found
7491 getIndexById : function(id){
7492 for(var i = 0, len = this.config.length; i < len; i++){
7493 if(this.config[i].id == id){
7501 * Returns the index for a specified column dataIndex.
7502 * @param {String} dataIndex The column dataIndex
7503 * @return {Number} the index, or -1 if not found
7506 findColumnIndex : function(dataIndex){
7507 for(var i = 0, len = this.config.length; i < len; i++){
7508 if(this.config[i].dataIndex == dataIndex){
7516 moveColumn : function(oldIndex, newIndex){
7517 var c = this.config[oldIndex];
7518 this.config.splice(oldIndex, 1);
7519 this.config.splice(newIndex, 0, c);
7520 this.dataMap = null;
7521 this.fireEvent("columnmoved", this, oldIndex, newIndex);
7524 isLocked : function(colIndex){
7525 return this.config[colIndex].locked === true;
7528 setLocked : function(colIndex, value, suppressEvent){
7529 if(this.isLocked(colIndex) == value){
7532 this.config[colIndex].locked = value;
7534 this.fireEvent("columnlockchange", this, colIndex, value);
7538 getTotalLockedWidth : function(){
7540 for(var i = 0; i < this.config.length; i++){
7541 if(this.isLocked(i) && !this.isHidden(i)){
7542 this.totalWidth += this.getColumnWidth(i);
7548 getLockedCount : function(){
7549 for(var i = 0, len = this.config.length; i < len; i++){
7550 if(!this.isLocked(i)){
7555 return this.config.length;
7559 * Returns the number of columns.
7562 getColumnCount : function(visibleOnly){
7563 if(visibleOnly === true){
7565 for(var i = 0, len = this.config.length; i < len; i++){
7566 if(!this.isHidden(i)){
7572 return this.config.length;
7576 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7577 * @param {Function} fn
7578 * @param {Object} scope (optional)
7579 * @return {Array} result
7581 getColumnsBy : function(fn, scope){
7583 for(var i = 0, len = this.config.length; i < len; i++){
7584 var c = this.config[i];
7585 if(fn.call(scope||this, c, i) === true){
7593 * Returns true if the specified column is sortable.
7594 * @param {Number} col The column index
7597 isSortable : function(col){
7598 if(typeof this.config[col].sortable == "undefined"){
7599 return this.defaultSortable;
7601 return this.config[col].sortable;
7605 * Returns the rendering (formatting) function defined for the column.
7606 * @param {Number} col The column index.
7607 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7609 getRenderer : function(col){
7610 if(!this.config[col].renderer){
7611 return Roo.grid.ColumnModel.defaultRenderer;
7613 return this.config[col].renderer;
7617 * Sets the rendering (formatting) function for a column.
7618 * @param {Number} col The column index
7619 * @param {Function} fn The function to use to process the cell's raw data
7620 * to return HTML markup for the grid view. The render function is called with
7621 * the following parameters:<ul>
7622 * <li>Data value.</li>
7623 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7624 * <li>css A CSS style string to apply to the table cell.</li>
7625 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7626 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7627 * <li>Row index</li>
7628 * <li>Column index</li>
7629 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7631 setRenderer : function(col, fn){
7632 this.config[col].renderer = fn;
7636 * Returns the width for the specified column.
7637 * @param {Number} col The column index
7640 getColumnWidth : function(col){
7641 return this.config[col].width * 1 || this.defaultWidth;
7645 * Sets the width for a column.
7646 * @param {Number} col The column index
7647 * @param {Number} width The new width
7649 setColumnWidth : function(col, width, suppressEvent){
7650 this.config[col].width = width;
7651 this.totalWidth = null;
7653 this.fireEvent("widthchange", this, col, width);
7658 * Returns the total width of all columns.
7659 * @param {Boolean} includeHidden True to include hidden column widths
7662 getTotalWidth : function(includeHidden){
7663 if(!this.totalWidth){
7664 this.totalWidth = 0;
7665 for(var i = 0, len = this.config.length; i < len; i++){
7666 if(includeHidden || !this.isHidden(i)){
7667 this.totalWidth += this.getColumnWidth(i);
7671 return this.totalWidth;
7675 * Returns the header for the specified column.
7676 * @param {Number} col The column index
7679 getColumnHeader : function(col){
7680 return this.config[col].header;
7684 * Sets the header for a column.
7685 * @param {Number} col The column index
7686 * @param {String} header The new header
7688 setColumnHeader : function(col, header){
7689 this.config[col].header = header;
7690 this.fireEvent("headerchange", this, col, header);
7694 * Returns the tooltip for the specified column.
7695 * @param {Number} col The column index
7698 getColumnTooltip : function(col){
7699 return this.config[col].tooltip;
7702 * Sets the tooltip for a column.
7703 * @param {Number} col The column index
7704 * @param {String} tooltip The new tooltip
7706 setColumnTooltip : function(col, tooltip){
7707 this.config[col].tooltip = tooltip;
7711 * Returns the dataIndex for the specified column.
7712 * @param {Number} col The column index
7715 getDataIndex : function(col){
7716 return this.config[col].dataIndex;
7720 * Sets the dataIndex for a column.
7721 * @param {Number} col The column index
7722 * @param {Number} dataIndex The new dataIndex
7724 setDataIndex : function(col, dataIndex){
7725 this.config[col].dataIndex = dataIndex;
7731 * Returns true if the cell is editable.
7732 * @param {Number} colIndex The column index
7733 * @param {Number} rowIndex The row index - this is nto actually used..?
7736 isCellEditable : function(colIndex, rowIndex){
7737 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7741 * Returns the editor defined for the cell/column.
7742 * return false or null to disable editing.
7743 * @param {Number} colIndex The column index
7744 * @param {Number} rowIndex The row index
7747 getCellEditor : function(colIndex, rowIndex){
7748 return this.config[colIndex].editor;
7752 * Sets if a column is editable.
7753 * @param {Number} col The column index
7754 * @param {Boolean} editable True if the column is editable
7756 setEditable : function(col, editable){
7757 this.config[col].editable = editable;
7762 * Returns true if the column is hidden.
7763 * @param {Number} colIndex The column index
7766 isHidden : function(colIndex){
7767 return this.config[colIndex].hidden;
7772 * Returns true if the column width cannot be changed
7774 isFixed : function(colIndex){
7775 return this.config[colIndex].fixed;
7779 * Returns true if the column can be resized
7782 isResizable : function(colIndex){
7783 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7786 * Sets if a column is hidden.
7787 * @param {Number} colIndex The column index
7788 * @param {Boolean} hidden True if the column is hidden
7790 setHidden : function(colIndex, hidden){
7791 this.config[colIndex].hidden = hidden;
7792 this.totalWidth = null;
7793 this.fireEvent("hiddenchange", this, colIndex, hidden);
7797 * Sets the editor for a column.
7798 * @param {Number} col The column index
7799 * @param {Object} editor The editor object
7801 setEditor : function(col, editor){
7802 this.config[col].editor = editor;
7805 * Add a column (experimental...) - defaults to adding to the end..
7806 * @param {Object} config
7808 addColumn : function(c)
7811 var i = this.config.length;
7814 if(typeof c.dataIndex == "undefined"){
7817 if(typeof c.renderer == "string"){
7818 c.renderer = Roo.util.Format[c.renderer];
7820 if(typeof c.id == "undefined"){
7823 if(c.editor && c.editor.xtype){
7824 c.editor = Roo.factory(c.editor, Roo.grid);
7826 if(c.editor && c.editor.isFormField){
7827 c.editor = new Roo.grid.GridEditor(c.editor);
7829 this.lookup[c.id] = c;
7834 Roo.grid.ColumnModel.defaultRenderer = function(value)
7836 if(typeof value == "object") {
7839 if(typeof value == "string" && value.length < 1){
7843 return String.format("{0}", value);
7846 // Alias for backwards compatibility
7847 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7850 * Ext JS Library 1.1.1
7851 * Copyright(c) 2006-2007, Ext JS, LLC.
7853 * Originally Released Under LGPL - original licence link has changed is not relivant.
7856 * <script type="text/javascript">
7860 * @class Roo.LoadMask
7861 * A simple utility class for generically masking elements while loading data. If the element being masked has
7862 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7863 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
7864 * element's UpdateManager load indicator and will be destroyed after the initial load.
7866 * Create a new LoadMask
7867 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7868 * @param {Object} config The config object
7870 Roo.LoadMask = function(el, config){
7871 this.el = Roo.get(el);
7872 Roo.apply(this, config);
7874 this.store.on('beforeload', this.onBeforeLoad, this);
7875 this.store.on('load', this.onLoad, this);
7876 this.store.on('loadexception', this.onLoadException, this);
7877 this.removeMask = false;
7879 var um = this.el.getUpdateManager();
7880 um.showLoadIndicator = false; // disable the default indicator
7881 um.on('beforeupdate', this.onBeforeLoad, this);
7882 um.on('update', this.onLoad, this);
7883 um.on('failure', this.onLoad, this);
7884 this.removeMask = true;
7888 Roo.LoadMask.prototype = {
7890 * @cfg {Boolean} removeMask
7891 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7892 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
7896 * The text to display in a centered loading message box (defaults to 'Loading...')
7900 * @cfg {String} msgCls
7901 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7903 msgCls : 'x-mask-loading',
7906 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7912 * Disables the mask to prevent it from being displayed
7914 disable : function(){
7915 this.disabled = true;
7919 * Enables the mask so that it can be displayed
7921 enable : function(){
7922 this.disabled = false;
7925 onLoadException : function()
7929 if (typeof(arguments[3]) != 'undefined') {
7930 Roo.MessageBox.alert("Error loading",arguments[3]);
7934 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7935 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7942 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7947 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7951 onBeforeLoad : function(){
7953 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7958 destroy : function(){
7960 this.store.un('beforeload', this.onBeforeLoad, this);
7961 this.store.un('load', this.onLoad, this);
7962 this.store.un('loadexception', this.onLoadException, this);
7964 var um = this.el.getUpdateManager();
7965 um.un('beforeupdate', this.onBeforeLoad, this);
7966 um.un('update', this.onLoad, this);
7967 um.un('failure', this.onLoad, this);
7978 * @class Roo.bootstrap.Table
7979 * @extends Roo.bootstrap.Component
7980 * Bootstrap Table class
7981 * @cfg {String} cls table class
7982 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7983 * @cfg {String} bgcolor Specifies the background color for a table
7984 * @cfg {Number} border Specifies whether the table cells should have borders or not
7985 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7986 * @cfg {Number} cellspacing Specifies the space between cells
7987 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7988 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7989 * @cfg {String} sortable Specifies that the table should be sortable
7990 * @cfg {String} summary Specifies a summary of the content of a table
7991 * @cfg {Number} width Specifies the width of a table
7992 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7994 * @cfg {boolean} striped Should the rows be alternative striped
7995 * @cfg {boolean} bordered Add borders to the table
7996 * @cfg {boolean} hover Add hover highlighting
7997 * @cfg {boolean} condensed Format condensed
7998 * @cfg {boolean} responsive Format condensed
7999 * @cfg {Boolean} loadMask (true|false) default false
8000 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
8001 * @cfg {Boolean} headerShow (true|false) generate thead, default true
8002 * @cfg {Boolean} rowSelection (true|false) default false
8003 * @cfg {Boolean} cellSelection (true|false) default false
8004 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
8005 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
8006 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
8007 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
8011 * Create a new Table
8012 * @param {Object} config The config object
8015 Roo.bootstrap.Table = function(config){
8016 Roo.bootstrap.Table.superclass.constructor.call(this, config);
8021 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
8022 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
8023 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
8024 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
8026 this.sm = this.sm || {xtype: 'RowSelectionModel'};
8028 this.sm.grid = this;
8029 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
8030 this.sm = this.selModel;
8031 this.sm.xmodule = this.xmodule || false;
8034 if (this.cm && typeof(this.cm.config) == 'undefined') {
8035 this.colModel = new Roo.grid.ColumnModel(this.cm);
8036 this.cm = this.colModel;
8037 this.cm.xmodule = this.xmodule || false;
8040 this.store= Roo.factory(this.store, Roo.data);
8041 this.ds = this.store;
8042 this.ds.xmodule = this.xmodule || false;
8045 if (this.footer && this.store) {
8046 this.footer.dataSource = this.ds;
8047 this.footer = Roo.factory(this.footer);
8054 * Fires when a cell is clicked
8055 * @param {Roo.bootstrap.Table} this
8056 * @param {Roo.Element} el
8057 * @param {Number} rowIndex
8058 * @param {Number} columnIndex
8059 * @param {Roo.EventObject} e
8063 * @event celldblclick
8064 * Fires when a cell is double clicked
8065 * @param {Roo.bootstrap.Table} this
8066 * @param {Roo.Element} el
8067 * @param {Number} rowIndex
8068 * @param {Number} columnIndex
8069 * @param {Roo.EventObject} e
8071 "celldblclick" : true,
8074 * Fires when a row is clicked
8075 * @param {Roo.bootstrap.Table} this
8076 * @param {Roo.Element} el
8077 * @param {Number} rowIndex
8078 * @param {Roo.EventObject} e
8082 * @event rowdblclick
8083 * Fires when a row is double clicked
8084 * @param {Roo.bootstrap.Table} this
8085 * @param {Roo.Element} el
8086 * @param {Number} rowIndex
8087 * @param {Roo.EventObject} e
8089 "rowdblclick" : true,
8092 * Fires when a mouseover occur
8093 * @param {Roo.bootstrap.Table} this
8094 * @param {Roo.Element} el
8095 * @param {Number} rowIndex
8096 * @param {Number} columnIndex
8097 * @param {Roo.EventObject} e
8102 * Fires when a mouseout occur
8103 * @param {Roo.bootstrap.Table} this
8104 * @param {Roo.Element} el
8105 * @param {Number} rowIndex
8106 * @param {Number} columnIndex
8107 * @param {Roo.EventObject} e
8112 * Fires when a row is rendered, so you can change add a style to it.
8113 * @param {Roo.bootstrap.Table} this
8114 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
8118 * @event rowsrendered
8119 * Fires when all the rows have been rendered
8120 * @param {Roo.bootstrap.Table} this
8122 'rowsrendered' : true,
8124 * @event contextmenu
8125 * The raw contextmenu event for the entire grid.
8126 * @param {Roo.EventObject} e
8128 "contextmenu" : true,
8130 * @event rowcontextmenu
8131 * Fires when a row is right clicked
8132 * @param {Roo.bootstrap.Table} this
8133 * @param {Number} rowIndex
8134 * @param {Roo.EventObject} e
8136 "rowcontextmenu" : true,
8138 * @event cellcontextmenu
8139 * Fires when a cell is right clicked
8140 * @param {Roo.bootstrap.Table} this
8141 * @param {Number} rowIndex
8142 * @param {Number} cellIndex
8143 * @param {Roo.EventObject} e
8145 "cellcontextmenu" : true,
8147 * @event headercontextmenu
8148 * Fires when a header is right clicked
8149 * @param {Roo.bootstrap.Table} this
8150 * @param {Number} columnIndex
8151 * @param {Roo.EventObject} e
8153 "headercontextmenu" : true
8157 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
8183 rowSelection : false,
8184 cellSelection : false,
8187 // Roo.Element - the tbody
8189 // Roo.Element - thead element
8192 container: false, // used by gridpanel...
8198 auto_hide_footer : false,
8200 getAutoCreate : function()
8202 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8209 if (this.scrollBody) {
8210 cfg.cls += ' table-body-fixed';
8213 cfg.cls += ' table-striped';
8217 cfg.cls += ' table-hover';
8219 if (this.bordered) {
8220 cfg.cls += ' table-bordered';
8222 if (this.condensed) {
8223 cfg.cls += ' table-condensed';
8225 if (this.responsive) {
8226 cfg.cls += ' table-responsive';
8230 cfg.cls+= ' ' +this.cls;
8233 // this lot should be simplifed...
8246 ].forEach(function(k) {
8254 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8257 if(this.store || this.cm){
8258 if(this.headerShow){
8259 cfg.cn.push(this.renderHeader());
8262 cfg.cn.push(this.renderBody());
8264 if(this.footerShow){
8265 cfg.cn.push(this.renderFooter());
8267 // where does this come from?
8268 //cfg.cls+= ' TableGrid';
8271 return { cn : [ cfg ] };
8274 initEvents : function()
8276 if(!this.store || !this.cm){
8279 if (this.selModel) {
8280 this.selModel.initEvents();
8284 //Roo.log('initEvents with ds!!!!');
8286 this.mainBody = this.el.select('tbody', true).first();
8287 this.mainHead = this.el.select('thead', true).first();
8288 this.mainFoot = this.el.select('tfoot', true).first();
8293 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8294 e.on('click', this.sort, this);
8297 this.mainBody.on("click", this.onClick, this);
8298 this.mainBody.on("dblclick", this.onDblClick, this);
8300 // why is this done????? = it breaks dialogs??
8301 //this.parent().el.setStyle('position', 'relative');
8305 this.footer.parentId = this.id;
8306 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8309 this.el.select('tfoot tr td').first().addClass('hide');
8314 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8317 this.store.on('load', this.onLoad, this);
8318 this.store.on('beforeload', this.onBeforeLoad, this);
8319 this.store.on('update', this.onUpdate, this);
8320 this.store.on('add', this.onAdd, this);
8321 this.store.on("clear", this.clear, this);
8323 this.el.on("contextmenu", this.onContextMenu, this);
8325 this.mainBody.on('scroll', this.onBodyScroll, this);
8327 this.cm.on("headerchange", this.onHeaderChange, this);
8329 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8333 onContextMenu : function(e, t)
8335 this.processEvent("contextmenu", e);
8338 processEvent : function(name, e)
8340 if (name != 'touchstart' ) {
8341 this.fireEvent(name, e);
8344 var t = e.getTarget();
8346 var cell = Roo.get(t);
8352 if(cell.findParent('tfoot', false, true)){
8356 if(cell.findParent('thead', false, true)){
8358 if(e.getTarget().nodeName.toLowerCase() != 'th'){
8359 cell = Roo.get(t).findParent('th', false, true);
8361 Roo.log("failed to find th in thead?");
8362 Roo.log(e.getTarget());
8367 var cellIndex = cell.dom.cellIndex;
8369 var ename = name == 'touchstart' ? 'click' : name;
8370 this.fireEvent("header" + ename, this, cellIndex, e);
8375 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8376 cell = Roo.get(t).findParent('td', false, true);
8378 Roo.log("failed to find th in tbody?");
8379 Roo.log(e.getTarget());
8384 var row = cell.findParent('tr', false, true);
8385 var cellIndex = cell.dom.cellIndex;
8386 var rowIndex = row.dom.rowIndex - 1;
8390 this.fireEvent("row" + name, this, rowIndex, e);
8394 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8400 onMouseover : function(e, el)
8402 var cell = Roo.get(el);
8408 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8409 cell = cell.findParent('td', false, true);
8412 var row = cell.findParent('tr', false, true);
8413 var cellIndex = cell.dom.cellIndex;
8414 var rowIndex = row.dom.rowIndex - 1; // start from 0
8416 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8420 onMouseout : function(e, el)
8422 var cell = Roo.get(el);
8428 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8429 cell = cell.findParent('td', false, true);
8432 var row = cell.findParent('tr', false, true);
8433 var cellIndex = cell.dom.cellIndex;
8434 var rowIndex = row.dom.rowIndex - 1; // start from 0
8436 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8440 onClick : function(e, el)
8442 var cell = Roo.get(el);
8444 if(!cell || (!this.cellSelection && !this.rowSelection)){
8448 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8449 cell = cell.findParent('td', false, true);
8452 if(!cell || typeof(cell) == 'undefined'){
8456 var row = cell.findParent('tr', false, true);
8458 if(!row || typeof(row) == 'undefined'){
8462 var cellIndex = cell.dom.cellIndex;
8463 var rowIndex = this.getRowIndex(row);
8465 // why??? - should these not be based on SelectionModel?
8466 //if(this.cellSelection){
8467 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8470 //if(this.rowSelection){
8471 this.fireEvent('rowclick', this, row, rowIndex, e);
8476 onDblClick : function(e,el)
8478 var cell = Roo.get(el);
8480 if(!cell || (!this.cellSelection && !this.rowSelection)){
8484 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8485 cell = cell.findParent('td', false, true);
8488 if(!cell || typeof(cell) == 'undefined'){
8492 var row = cell.findParent('tr', false, true);
8494 if(!row || typeof(row) == 'undefined'){
8498 var cellIndex = cell.dom.cellIndex;
8499 var rowIndex = this.getRowIndex(row);
8501 if(this.cellSelection){
8502 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8505 if(this.rowSelection){
8506 this.fireEvent('rowdblclick', this, row, rowIndex, e);
8510 sort : function(e,el)
8512 var col = Roo.get(el);
8514 if(!col.hasClass('sortable')){
8518 var sort = col.attr('sort');
8521 if(col.select('i', true).first().hasClass('fa-arrow-up')){
8525 this.store.sortInfo = {field : sort, direction : dir};
8528 Roo.log("calling footer first");
8529 this.footer.onClick('first');
8532 this.store.load({ params : { start : 0 } });
8536 renderHeader : function()
8544 this.totalWidth = 0;
8546 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8548 var config = cm.config[i];
8552 cls : 'x-hcol-' + i,
8555 html: cm.getColumnHeader(i)
8558 var tooltip = cm.getColumnTooltip(i);
8560 c.tooltip = tooltip;
8566 if(typeof(config.sortable) != 'undefined' && config.sortable){
8568 c.html = '<i class="fa"></i>' + c.html;
8571 // could use BS4 hidden-..-down
8573 if(typeof(config.lgHeader) != 'undefined'){
8574 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8577 if(typeof(config.mdHeader) != 'undefined'){
8578 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8581 if(typeof(config.smHeader) != 'undefined'){
8582 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8585 if(typeof(config.xsHeader) != 'undefined'){
8586 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8593 if(typeof(config.tooltip) != 'undefined'){
8594 c.tooltip = config.tooltip;
8597 if(typeof(config.colspan) != 'undefined'){
8598 c.colspan = config.colspan;
8601 if(typeof(config.hidden) != 'undefined' && config.hidden){
8602 c.style += ' display:none;';
8605 if(typeof(config.dataIndex) != 'undefined'){
8606 c.sort = config.dataIndex;
8611 if(typeof(config.align) != 'undefined' && config.align.length){
8612 c.style += ' text-align:' + config.align + ';';
8615 if(typeof(config.width) != 'undefined'){
8616 c.style += ' width:' + config.width + 'px;';
8617 this.totalWidth += config.width;
8619 this.totalWidth += 100; // assume minimum of 100 per column?
8622 if(typeof(config.cls) != 'undefined'){
8623 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8626 ['xs','sm','md','lg'].map(function(size){
8628 if(typeof(config[size]) == 'undefined'){
8632 if (!config[size]) { // 0 = hidden
8633 // BS 4 '0' is treated as hide that column and below.
8634 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8638 c.cls += ' col-' + size + '-' + config[size] + (
8639 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8651 renderBody : function()
8661 colspan : this.cm.getColumnCount()
8671 renderFooter : function()
8681 colspan : this.cm.getColumnCount()
8695 // Roo.log('ds onload');
8700 var ds = this.store;
8702 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8703 e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
8704 if (_this.store.sortInfo) {
8706 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8707 e.select('i', true).addClass(['fa-arrow-up']);
8710 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8711 e.select('i', true).addClass(['fa-arrow-down']);
8716 var tbody = this.mainBody;
8718 if(ds.getCount() > 0){
8719 ds.data.each(function(d,rowIndex){
8720 var row = this.renderRow(cm, ds, rowIndex);
8722 tbody.createChild(row);
8726 if(row.cellObjects.length){
8727 Roo.each(row.cellObjects, function(r){
8728 _this.renderCellObject(r);
8735 var tfoot = this.el.select('tfoot', true).first();
8737 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8739 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8741 var total = this.ds.getTotalCount();
8743 if(this.footer.pageSize < total){
8744 this.mainFoot.show();
8748 Roo.each(this.el.select('tbody td', true).elements, function(e){
8749 e.on('mouseover', _this.onMouseover, _this);
8752 Roo.each(this.el.select('tbody td', true).elements, function(e){
8753 e.on('mouseout', _this.onMouseout, _this);
8755 this.fireEvent('rowsrendered', this);
8761 onUpdate : function(ds,record)
8763 this.refreshRow(record);
8767 onRemove : function(ds, record, index, isUpdate){
8768 if(isUpdate !== true){
8769 this.fireEvent("beforerowremoved", this, index, record);
8771 var bt = this.mainBody.dom;
8773 var rows = this.el.select('tbody > tr', true).elements;
8775 if(typeof(rows[index]) != 'undefined'){
8776 bt.removeChild(rows[index].dom);
8779 // if(bt.rows[index]){
8780 // bt.removeChild(bt.rows[index]);
8783 if(isUpdate !== true){
8784 //this.stripeRows(index);
8785 //this.syncRowHeights(index, index);
8787 this.fireEvent("rowremoved", this, index, record);
8791 onAdd : function(ds, records, rowIndex)
8793 //Roo.log('on Add called');
8794 // - note this does not handle multiple adding very well..
8795 var bt = this.mainBody.dom;
8796 for (var i =0 ; i < records.length;i++) {
8797 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8798 //Roo.log(records[i]);
8799 //Roo.log(this.store.getAt(rowIndex+i));
8800 this.insertRow(this.store, rowIndex + i, false);
8807 refreshRow : function(record){
8808 var ds = this.store, index;
8809 if(typeof record == 'number'){
8811 record = ds.getAt(index);
8813 index = ds.indexOf(record);
8815 return; // should not happen - but seems to
8818 this.insertRow(ds, index, true);
8820 this.onRemove(ds, record, index+1, true);
8822 //this.syncRowHeights(index, index);
8824 this.fireEvent("rowupdated", this, index, record);
8827 insertRow : function(dm, rowIndex, isUpdate){
8830 this.fireEvent("beforerowsinserted", this, rowIndex);
8832 //var s = this.getScrollState();
8833 var row = this.renderRow(this.cm, this.store, rowIndex);
8834 // insert before rowIndex..
8835 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8839 if(row.cellObjects.length){
8840 Roo.each(row.cellObjects, function(r){
8841 _this.renderCellObject(r);
8846 this.fireEvent("rowsinserted", this, rowIndex);
8847 //this.syncRowHeights(firstRow, lastRow);
8848 //this.stripeRows(firstRow);
8855 getRowDom : function(rowIndex)
8857 var rows = this.el.select('tbody > tr', true).elements;
8859 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8862 // returns the object tree for a tr..
8865 renderRow : function(cm, ds, rowIndex)
8867 var d = ds.getAt(rowIndex);
8871 cls : 'x-row-' + rowIndex,
8875 var cellObjects = [];
8877 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8878 var config = cm.config[i];
8880 var renderer = cm.getRenderer(i);
8884 if(typeof(renderer) !== 'undefined'){
8885 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8887 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8888 // and are rendered into the cells after the row is rendered - using the id for the element.
8890 if(typeof(value) === 'object'){
8900 rowIndex : rowIndex,
8905 this.fireEvent('rowclass', this, rowcfg);
8909 // this might end up displaying HTML?
8910 // this is too messy... - better to only do it on columsn you know are going to be too long
8911 //tooltip : (typeof(value) === 'object') ? '' : value,
8912 cls : rowcfg.rowClass + ' x-col-' + i,
8914 html: (typeof(value) === 'object') ? '' : value
8921 if(typeof(config.colspan) != 'undefined'){
8922 td.colspan = config.colspan;
8925 if(typeof(config.hidden) != 'undefined' && config.hidden){
8926 td.style += ' display:none;';
8929 if(typeof(config.align) != 'undefined' && config.align.length){
8930 td.style += ' text-align:' + config.align + ';';
8932 if(typeof(config.valign) != 'undefined' && config.valign.length){
8933 td.style += ' vertical-align:' + config.valign + ';';
8936 if(typeof(config.width) != 'undefined'){
8937 td.style += ' width:' + config.width + 'px;';
8940 if(typeof(config.cursor) != 'undefined'){
8941 td.style += ' cursor:' + config.cursor + ';';
8944 if(typeof(config.cls) != 'undefined'){
8945 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8948 ['xs','sm','md','lg'].map(function(size){
8950 if(typeof(config[size]) == 'undefined'){
8956 if (!config[size]) { // 0 = hidden
8957 // BS 4 '0' is treated as hide that column and below.
8958 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8962 td.cls += ' col-' + size + '-' + config[size] + (
8963 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8973 row.cellObjects = cellObjects;
8981 onBeforeLoad : function()
8990 this.el.select('tbody', true).first().dom.innerHTML = '';
8993 * Show or hide a row.
8994 * @param {Number} rowIndex to show or hide
8995 * @param {Boolean} state hide
8997 setRowVisibility : function(rowIndex, state)
8999 var bt = this.mainBody.dom;
9001 var rows = this.el.select('tbody > tr', true).elements;
9003 if(typeof(rows[rowIndex]) == 'undefined'){
9006 rows[rowIndex].dom.style.display = state ? '' : 'none';
9010 getSelectionModel : function(){
9012 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
9014 return this.selModel;
9017 * Render the Roo.bootstrap object from renderder
9019 renderCellObject : function(r)
9023 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
9025 var t = r.cfg.render(r.container);
9028 Roo.each(r.cfg.cn, function(c){
9030 container: t.getChildContainer(),
9033 _this.renderCellObject(child);
9038 getRowIndex : function(row)
9042 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
9053 * Returns the grid's underlying element = used by panel.Grid
9054 * @return {Element} The element
9056 getGridEl : function(){
9060 * Forces a resize - used by panel.Grid
9061 * @return {Element} The element
9063 autoSize : function()
9065 //var ctr = Roo.get(this.container.dom.parentElement);
9066 var ctr = Roo.get(this.el.dom);
9068 var thd = this.getGridEl().select('thead',true).first();
9069 var tbd = this.getGridEl().select('tbody', true).first();
9070 var tfd = this.getGridEl().select('tfoot', true).first();
9072 var cw = ctr.getWidth();
9073 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
9077 tbd.setWidth(ctr.getWidth());
9078 // if the body has a max height - and then scrolls - we should perhaps set up the height here
9079 // this needs fixing for various usage - currently only hydra job advers I think..
9081 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
9083 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
9086 cw = Math.max(cw, this.totalWidth);
9087 this.getGridEl().select('tbody tr',true).setWidth(cw);
9089 // resize 'expandable coloumn?
9091 return; // we doe not have a view in this design..
9094 onBodyScroll: function()
9096 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
9098 this.mainHead.setStyle({
9099 'position' : 'relative',
9100 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
9106 var scrollHeight = this.mainBody.dom.scrollHeight;
9108 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
9110 var height = this.mainBody.getHeight();
9112 if(scrollHeight - height == scrollTop) {
9114 var total = this.ds.getTotalCount();
9116 if(this.footer.cursor + this.footer.pageSize < total){
9118 this.footer.ds.load({
9120 start : this.footer.cursor + this.footer.pageSize,
9121 limit : this.footer.pageSize
9131 onHeaderChange : function()
9133 var header = this.renderHeader();
9134 var table = this.el.select('table', true).first();
9136 this.mainHead.remove();
9137 this.mainHead = table.createChild(header, this.mainBody, false);
9139 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9140 e.on('click', this.sort, this);
9146 onHiddenChange : function(colModel, colIndex, hidden)
9148 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
9149 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
9151 this.CSS.updateRule(thSelector, "display", "");
9152 this.CSS.updateRule(tdSelector, "display", "");
9155 this.CSS.updateRule(thSelector, "display", "none");
9156 this.CSS.updateRule(tdSelector, "display", "none");
9159 this.onHeaderChange();
9163 setColumnWidth: function(col_index, width)
9165 // width = "md-2 xs-2..."
9166 if(!this.colModel.config[col_index]) {
9170 var w = width.split(" ");
9172 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
9174 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
9177 for(var j = 0; j < w.length; j++) {
9183 var size_cls = w[j].split("-");
9185 if(!Number.isInteger(size_cls[1] * 1)) {
9189 if(!this.colModel.config[col_index][size_cls[0]]) {
9193 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9197 h_row[0].classList.replace(
9198 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9199 "col-"+size_cls[0]+"-"+size_cls[1]
9202 for(var i = 0; i < rows.length; i++) {
9204 var size_cls = w[j].split("-");
9206 if(!Number.isInteger(size_cls[1] * 1)) {
9210 if(!this.colModel.config[col_index][size_cls[0]]) {
9214 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9218 rows[i].classList.replace(
9219 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9220 "col-"+size_cls[0]+"-"+size_cls[1]
9224 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
9239 * @class Roo.bootstrap.TableCell
9240 * @extends Roo.bootstrap.Component
9241 * Bootstrap TableCell class
9242 * @cfg {String} html cell contain text
9243 * @cfg {String} cls cell class
9244 * @cfg {String} tag cell tag (td|th) default td
9245 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
9246 * @cfg {String} align Aligns the content in a cell
9247 * @cfg {String} axis Categorizes cells
9248 * @cfg {String} bgcolor Specifies the background color of a cell
9249 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9250 * @cfg {Number} colspan Specifies the number of columns a cell should span
9251 * @cfg {String} headers Specifies one or more header cells a cell is related to
9252 * @cfg {Number} height Sets the height of a cell
9253 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
9254 * @cfg {Number} rowspan Sets the number of rows a cell should span
9255 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
9256 * @cfg {String} valign Vertical aligns the content in a cell
9257 * @cfg {Number} width Specifies the width of a cell
9260 * Create a new TableCell
9261 * @param {Object} config The config object
9264 Roo.bootstrap.TableCell = function(config){
9265 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
9268 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
9288 getAutoCreate : function(){
9289 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
9309 cfg.align=this.align
9315 cfg.bgcolor=this.bgcolor
9318 cfg.charoff=this.charoff
9321 cfg.colspan=this.colspan
9324 cfg.headers=this.headers
9327 cfg.height=this.height
9330 cfg.nowrap=this.nowrap
9333 cfg.rowspan=this.rowspan
9336 cfg.scope=this.scope
9339 cfg.valign=this.valign
9342 cfg.width=this.width
9361 * @class Roo.bootstrap.TableRow
9362 * @extends Roo.bootstrap.Component
9363 * Bootstrap TableRow class
9364 * @cfg {String} cls row class
9365 * @cfg {String} align Aligns the content in a table row
9366 * @cfg {String} bgcolor Specifies a background color for a table row
9367 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9368 * @cfg {String} valign Vertical aligns the content in a table row
9371 * Create a new TableRow
9372 * @param {Object} config The config object
9375 Roo.bootstrap.TableRow = function(config){
9376 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9379 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
9387 getAutoCreate : function(){
9388 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9398 cfg.align = this.align;
9401 cfg.bgcolor = this.bgcolor;
9404 cfg.charoff = this.charoff;
9407 cfg.valign = this.valign;
9425 * @class Roo.bootstrap.TableBody
9426 * @extends Roo.bootstrap.Component
9427 * Bootstrap TableBody class
9428 * @cfg {String} cls element class
9429 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9430 * @cfg {String} align Aligns the content inside the element
9431 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9432 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9435 * Create a new TableBody
9436 * @param {Object} config The config object
9439 Roo.bootstrap.TableBody = function(config){
9440 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9443 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
9451 getAutoCreate : function(){
9452 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9466 cfg.align = this.align;
9469 cfg.charoff = this.charoff;
9472 cfg.valign = this.valign;
9479 // initEvents : function()
9486 // this.store = Roo.factory(this.store, Roo.data);
9487 // this.store.on('load', this.onLoad, this);
9489 // this.store.load();
9493 // onLoad: function ()
9495 // this.fireEvent('load', this);
9505 * Ext JS Library 1.1.1
9506 * Copyright(c) 2006-2007, Ext JS, LLC.
9508 * Originally Released Under LGPL - original licence link has changed is not relivant.
9511 * <script type="text/javascript">
9514 // as we use this in bootstrap.
9515 Roo.namespace('Roo.form');
9517 * @class Roo.form.Action
9518 * Internal Class used to handle form actions
9520 * @param {Roo.form.BasicForm} el The form element or its id
9521 * @param {Object} config Configuration options
9526 // define the action interface
9527 Roo.form.Action = function(form, options){
9529 this.options = options || {};
9532 * Client Validation Failed
9535 Roo.form.Action.CLIENT_INVALID = 'client';
9537 * Server Validation Failed
9540 Roo.form.Action.SERVER_INVALID = 'server';
9542 * Connect to Server Failed
9545 Roo.form.Action.CONNECT_FAILURE = 'connect';
9547 * Reading Data from Server Failed
9550 Roo.form.Action.LOAD_FAILURE = 'load';
9552 Roo.form.Action.prototype = {
9554 failureType : undefined,
9555 response : undefined,
9559 run : function(options){
9564 success : function(response){
9569 handleResponse : function(response){
9573 // default connection failure
9574 failure : function(response){
9576 this.response = response;
9577 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9578 this.form.afterAction(this, false);
9581 processResponse : function(response){
9582 this.response = response;
9583 if(!response.responseText){
9586 this.result = this.handleResponse(response);
9590 // utility functions used internally
9591 getUrl : function(appendParams){
9592 var url = this.options.url || this.form.url || this.form.el.dom.action;
9594 var p = this.getParams();
9596 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9602 getMethod : function(){
9603 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9606 getParams : function(){
9607 var bp = this.form.baseParams;
9608 var p = this.options.params;
9610 if(typeof p == "object"){
9611 p = Roo.urlEncode(Roo.applyIf(p, bp));
9612 }else if(typeof p == 'string' && bp){
9613 p += '&' + Roo.urlEncode(bp);
9616 p = Roo.urlEncode(bp);
9621 createCallback : function(){
9623 success: this.success,
9624 failure: this.failure,
9626 timeout: (this.form.timeout*1000),
9627 upload: this.form.fileUpload ? this.success : undefined
9632 Roo.form.Action.Submit = function(form, options){
9633 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9636 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9639 haveProgress : false,
9640 uploadComplete : false,
9642 // uploadProgress indicator.
9643 uploadProgress : function()
9645 if (!this.form.progressUrl) {
9649 if (!this.haveProgress) {
9650 Roo.MessageBox.progress("Uploading", "Uploading");
9652 if (this.uploadComplete) {
9653 Roo.MessageBox.hide();
9657 this.haveProgress = true;
9659 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9661 var c = new Roo.data.Connection();
9663 url : this.form.progressUrl,
9668 success : function(req){
9669 //console.log(data);
9673 rdata = Roo.decode(req.responseText)
9675 Roo.log("Invalid data from server..");
9679 if (!rdata || !rdata.success) {
9681 Roo.MessageBox.alert(Roo.encode(rdata));
9684 var data = rdata.data;
9686 if (this.uploadComplete) {
9687 Roo.MessageBox.hide();
9692 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9693 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9696 this.uploadProgress.defer(2000,this);
9699 failure: function(data) {
9700 Roo.log('progress url failed ');
9711 // run get Values on the form, so it syncs any secondary forms.
9712 this.form.getValues();
9714 var o = this.options;
9715 var method = this.getMethod();
9716 var isPost = method == 'POST';
9717 if(o.clientValidation === false || this.form.isValid()){
9719 if (this.form.progressUrl) {
9720 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9721 (new Date() * 1) + '' + Math.random());
9726 Roo.Ajax.request(Roo.apply(this.createCallback(), {
9727 form:this.form.el.dom,
9728 url:this.getUrl(!isPost),
9730 params:isPost ? this.getParams() : null,
9731 isUpload: this.form.fileUpload,
9732 formData : this.form.formData
9735 this.uploadProgress();
9737 }else if (o.clientValidation !== false){ // client validation failed
9738 this.failureType = Roo.form.Action.CLIENT_INVALID;
9739 this.form.afterAction(this, false);
9743 success : function(response)
9745 this.uploadComplete= true;
9746 if (this.haveProgress) {
9747 Roo.MessageBox.hide();
9751 var result = this.processResponse(response);
9752 if(result === true || result.success){
9753 this.form.afterAction(this, true);
9757 this.form.markInvalid(result.errors);
9758 this.failureType = Roo.form.Action.SERVER_INVALID;
9760 this.form.afterAction(this, false);
9762 failure : function(response)
9764 this.uploadComplete= true;
9765 if (this.haveProgress) {
9766 Roo.MessageBox.hide();
9769 this.response = response;
9770 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9771 this.form.afterAction(this, false);
9774 handleResponse : function(response){
9775 if(this.form.errorReader){
9776 var rs = this.form.errorReader.read(response);
9779 for(var i = 0, len = rs.records.length; i < len; i++) {
9780 var r = rs.records[i];
9784 if(errors.length < 1){
9788 success : rs.success,
9794 ret = Roo.decode(response.responseText);
9798 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9808 Roo.form.Action.Load = function(form, options){
9809 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9810 this.reader = this.form.reader;
9813 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9818 Roo.Ajax.request(Roo.apply(
9819 this.createCallback(), {
9820 method:this.getMethod(),
9821 url:this.getUrl(false),
9822 params:this.getParams()
9826 success : function(response){
9828 var result = this.processResponse(response);
9829 if(result === true || !result.success || !result.data){
9830 this.failureType = Roo.form.Action.LOAD_FAILURE;
9831 this.form.afterAction(this, false);
9834 this.form.clearInvalid();
9835 this.form.setValues(result.data);
9836 this.form.afterAction(this, true);
9839 handleResponse : function(response){
9840 if(this.form.reader){
9841 var rs = this.form.reader.read(response);
9842 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9844 success : rs.success,
9848 return Roo.decode(response.responseText);
9852 Roo.form.Action.ACTION_TYPES = {
9853 'load' : Roo.form.Action.Load,
9854 'submit' : Roo.form.Action.Submit
9863 * @class Roo.bootstrap.Form
9864 * @extends Roo.bootstrap.Component
9865 * Bootstrap Form class
9866 * @cfg {String} method GET | POST (default POST)
9867 * @cfg {String} labelAlign top | left (default top)
9868 * @cfg {String} align left | right - for navbars
9869 * @cfg {Boolean} loadMask load mask when submit (default true)
9874 * @param {Object} config The config object
9878 Roo.bootstrap.Form = function(config){
9880 Roo.bootstrap.Form.superclass.constructor.call(this, config);
9882 Roo.bootstrap.Form.popover.apply();
9886 * @event clientvalidation
9887 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9888 * @param {Form} this
9889 * @param {Boolean} valid true if the form has passed client-side validation
9891 clientvalidation: true,
9893 * @event beforeaction
9894 * Fires before any action is performed. Return false to cancel the action.
9895 * @param {Form} this
9896 * @param {Action} action The action to be performed
9900 * @event actionfailed
9901 * Fires when an action fails.
9902 * @param {Form} this
9903 * @param {Action} action The action that failed
9905 actionfailed : true,
9907 * @event actioncomplete
9908 * Fires when an action is completed.
9909 * @param {Form} this
9910 * @param {Action} action The action that completed
9912 actioncomplete : true
9916 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
9919 * @cfg {String} method
9920 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9925 * The URL to use for form actions if one isn't supplied in the action options.
9928 * @cfg {Boolean} fileUpload
9929 * Set to true if this form is a file upload.
9933 * @cfg {Object} baseParams
9934 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9938 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9942 * @cfg {Sting} align (left|right) for navbar forms
9947 activeAction : null,
9950 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9951 * element by passing it or its id or mask the form itself by passing in true.
9954 waitMsgTarget : false,
9959 * @cfg {Boolean} errorMask (true|false) default false
9964 * @cfg {Number} maskOffset Default 100
9969 * @cfg {Boolean} maskBody
9973 getAutoCreate : function(){
9977 method : this.method || 'POST',
9978 id : this.id || Roo.id(),
9981 if (this.parent().xtype.match(/^Nav/)) {
9982 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9986 if (this.labelAlign == 'left' ) {
9987 cfg.cls += ' form-horizontal';
9993 initEvents : function()
9995 this.el.on('submit', this.onSubmit, this);
9996 // this was added as random key presses on the form where triggering form submit.
9997 this.el.on('keypress', function(e) {
9998 if (e.getCharCode() != 13) {
10001 // we might need to allow it for textareas.. and some other items.
10002 // check e.getTarget().
10004 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
10008 Roo.log("keypress blocked");
10010 e.preventDefault();
10016 onSubmit : function(e){
10021 * Returns true if client-side validation on the form is successful.
10024 isValid : function(){
10025 var items = this.getItems();
10027 var target = false;
10029 items.each(function(f){
10035 Roo.log('invalid field: ' + f.name);
10039 if(!target && f.el.isVisible(true)){
10045 if(this.errorMask && !valid){
10046 Roo.bootstrap.Form.popover.mask(this, target);
10053 * Returns true if any fields in this form have changed since their original load.
10056 isDirty : function(){
10058 var items = this.getItems();
10059 items.each(function(f){
10069 * Performs a predefined action (submit or load) or custom actions you define on this form.
10070 * @param {String} actionName The name of the action type
10071 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
10072 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
10073 * accept other config options):
10075 Property Type Description
10076 ---------------- --------------- ----------------------------------------------------------------------------------
10077 url String The url for the action (defaults to the form's url)
10078 method String The form method to use (defaults to the form's method, or POST if not defined)
10079 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
10080 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
10081 validate the form on the client (defaults to false)
10083 * @return {BasicForm} this
10085 doAction : function(action, options){
10086 if(typeof action == 'string'){
10087 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
10089 if(this.fireEvent('beforeaction', this, action) !== false){
10090 this.beforeAction(action);
10091 action.run.defer(100, action);
10097 beforeAction : function(action){
10098 var o = action.options;
10103 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
10105 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10108 // not really supported yet.. ??
10110 //if(this.waitMsgTarget === true){
10111 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10112 //}else if(this.waitMsgTarget){
10113 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
10114 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
10116 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
10122 afterAction : function(action, success){
10123 this.activeAction = null;
10124 var o = action.options;
10129 Roo.get(document.body).unmask();
10135 //if(this.waitMsgTarget === true){
10136 // this.el.unmask();
10137 //}else if(this.waitMsgTarget){
10138 // this.waitMsgTarget.unmask();
10140 // Roo.MessageBox.updateProgress(1);
10141 // Roo.MessageBox.hide();
10148 Roo.callback(o.success, o.scope, [this, action]);
10149 this.fireEvent('actioncomplete', this, action);
10153 // failure condition..
10154 // we have a scenario where updates need confirming.
10155 // eg. if a locking scenario exists..
10156 // we look for { errors : { needs_confirm : true }} in the response.
10158 (typeof(action.result) != 'undefined') &&
10159 (typeof(action.result.errors) != 'undefined') &&
10160 (typeof(action.result.errors.needs_confirm) != 'undefined')
10163 Roo.log("not supported yet");
10166 Roo.MessageBox.confirm(
10167 "Change requires confirmation",
10168 action.result.errorMsg,
10173 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
10183 Roo.callback(o.failure, o.scope, [this, action]);
10184 // show an error message if no failed handler is set..
10185 if (!this.hasListener('actionfailed')) {
10186 Roo.log("need to add dialog support");
10188 Roo.MessageBox.alert("Error",
10189 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
10190 action.result.errorMsg :
10191 "Saving Failed, please check your entries or try again"
10196 this.fireEvent('actionfailed', this, action);
10201 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
10202 * @param {String} id The value to search for
10205 findField : function(id){
10206 var items = this.getItems();
10207 var field = items.get(id);
10209 items.each(function(f){
10210 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
10217 return field || null;
10220 * Mark fields in this form invalid in bulk.
10221 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
10222 * @return {BasicForm} this
10224 markInvalid : function(errors){
10225 if(errors instanceof Array){
10226 for(var i = 0, len = errors.length; i < len; i++){
10227 var fieldError = errors[i];
10228 var f = this.findField(fieldError.id);
10230 f.markInvalid(fieldError.msg);
10236 if(typeof errors[id] != 'function' && (field = this.findField(id))){
10237 field.markInvalid(errors[id]);
10241 //Roo.each(this.childForms || [], function (f) {
10242 // f.markInvalid(errors);
10249 * Set values for fields in this form in bulk.
10250 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
10251 * @return {BasicForm} this
10253 setValues : function(values){
10254 if(values instanceof Array){ // array of objects
10255 for(var i = 0, len = values.length; i < len; i++){
10257 var f = this.findField(v.id);
10259 f.setValue(v.value);
10260 if(this.trackResetOnLoad){
10261 f.originalValue = f.getValue();
10265 }else{ // object hash
10268 if(typeof values[id] != 'function' && (field = this.findField(id))){
10270 if (field.setFromData &&
10271 field.valueField &&
10272 field.displayField &&
10273 // combos' with local stores can
10274 // be queried via setValue()
10275 // to set their value..
10276 (field.store && !field.store.isLocal)
10280 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
10281 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
10282 field.setFromData(sd);
10284 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
10286 field.setFromData(values);
10289 field.setValue(values[id]);
10293 if(this.trackResetOnLoad){
10294 field.originalValue = field.getValue();
10300 //Roo.each(this.childForms || [], function (f) {
10301 // f.setValues(values);
10308 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
10309 * they are returned as an array.
10310 * @param {Boolean} asString
10313 getValues : function(asString){
10314 //if (this.childForms) {
10315 // copy values from the child forms
10316 // Roo.each(this.childForms, function (f) {
10317 // this.setValues(f.getValues());
10323 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10324 if(asString === true){
10327 return Roo.urlDecode(fs);
10331 * Returns the fields in this form as an object with key/value pairs.
10332 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10335 getFieldValues : function(with_hidden)
10337 var items = this.getItems();
10339 items.each(function(f){
10341 if (!f.getName()) {
10345 var v = f.getValue();
10347 if (f.inputType =='radio') {
10348 if (typeof(ret[f.getName()]) == 'undefined') {
10349 ret[f.getName()] = ''; // empty..
10352 if (!f.el.dom.checked) {
10356 v = f.el.dom.value;
10360 if(f.xtype == 'MoneyField'){
10361 ret[f.currencyName] = f.getCurrency();
10364 // not sure if this supported any more..
10365 if ((typeof(v) == 'object') && f.getRawValue) {
10366 v = f.getRawValue() ; // dates..
10368 // combo boxes where name != hiddenName...
10369 if (f.name !== false && f.name != '' && f.name != f.getName()) {
10370 ret[f.name] = f.getRawValue();
10372 ret[f.getName()] = v;
10379 * Clears all invalid messages in this form.
10380 * @return {BasicForm} this
10382 clearInvalid : function(){
10383 var items = this.getItems();
10385 items.each(function(f){
10393 * Resets this form.
10394 * @return {BasicForm} this
10396 reset : function(){
10397 var items = this.getItems();
10398 items.each(function(f){
10402 Roo.each(this.childForms || [], function (f) {
10410 getItems : function()
10412 var r=new Roo.util.MixedCollection(false, function(o){
10413 return o.id || (o.id = Roo.id());
10415 var iter = function(el) {
10422 Roo.each(el.items,function(e) {
10431 hideFields : function(items)
10433 Roo.each(items, function(i){
10435 var f = this.findField(i);
10446 showFields : function(items)
10448 Roo.each(items, function(i){
10450 var f = this.findField(i);
10463 Roo.apply(Roo.bootstrap.Form, {
10479 intervalID : false,
10485 if(this.isApplied){
10490 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10491 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10492 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10493 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10496 this.maskEl.top.enableDisplayMode("block");
10497 this.maskEl.left.enableDisplayMode("block");
10498 this.maskEl.bottom.enableDisplayMode("block");
10499 this.maskEl.right.enableDisplayMode("block");
10501 this.toolTip = new Roo.bootstrap.Tooltip({
10502 cls : 'roo-form-error-popover',
10504 'left' : ['r-l', [-2,0], 'right'],
10505 'right' : ['l-r', [2,0], 'left'],
10506 'bottom' : ['tl-bl', [0,2], 'top'],
10507 'top' : [ 'bl-tl', [0,-2], 'bottom']
10511 this.toolTip.render(Roo.get(document.body));
10513 this.toolTip.el.enableDisplayMode("block");
10515 Roo.get(document.body).on('click', function(){
10519 Roo.get(document.body).on('touchstart', function(){
10523 this.isApplied = true
10526 mask : function(form, target)
10530 this.target = target;
10532 if(!this.form.errorMask || !target.el){
10536 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10538 Roo.log(scrollable);
10540 var ot = this.target.el.calcOffsetsTo(scrollable);
10542 var scrollTo = ot[1] - this.form.maskOffset;
10544 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10546 scrollable.scrollTo('top', scrollTo);
10548 var box = this.target.el.getBox();
10550 var zIndex = Roo.bootstrap.Modal.zIndex++;
10553 this.maskEl.top.setStyle('position', 'absolute');
10554 this.maskEl.top.setStyle('z-index', zIndex);
10555 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10556 this.maskEl.top.setLeft(0);
10557 this.maskEl.top.setTop(0);
10558 this.maskEl.top.show();
10560 this.maskEl.left.setStyle('position', 'absolute');
10561 this.maskEl.left.setStyle('z-index', zIndex);
10562 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10563 this.maskEl.left.setLeft(0);
10564 this.maskEl.left.setTop(box.y - this.padding);
10565 this.maskEl.left.show();
10567 this.maskEl.bottom.setStyle('position', 'absolute');
10568 this.maskEl.bottom.setStyle('z-index', zIndex);
10569 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10570 this.maskEl.bottom.setLeft(0);
10571 this.maskEl.bottom.setTop(box.bottom + this.padding);
10572 this.maskEl.bottom.show();
10574 this.maskEl.right.setStyle('position', 'absolute');
10575 this.maskEl.right.setStyle('z-index', zIndex);
10576 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10577 this.maskEl.right.setLeft(box.right + this.padding);
10578 this.maskEl.right.setTop(box.y - this.padding);
10579 this.maskEl.right.show();
10581 this.toolTip.bindEl = this.target.el;
10583 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10585 var tip = this.target.blankText;
10587 if(this.target.getValue() !== '' ) {
10589 if (this.target.invalidText.length) {
10590 tip = this.target.invalidText;
10591 } else if (this.target.regexText.length){
10592 tip = this.target.regexText;
10596 this.toolTip.show(tip);
10598 this.intervalID = window.setInterval(function() {
10599 Roo.bootstrap.Form.popover.unmask();
10602 window.onwheel = function(){ return false;};
10604 (function(){ this.isMasked = true; }).defer(500, this);
10608 unmask : function()
10610 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10614 this.maskEl.top.setStyle('position', 'absolute');
10615 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10616 this.maskEl.top.hide();
10618 this.maskEl.left.setStyle('position', 'absolute');
10619 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10620 this.maskEl.left.hide();
10622 this.maskEl.bottom.setStyle('position', 'absolute');
10623 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10624 this.maskEl.bottom.hide();
10626 this.maskEl.right.setStyle('position', 'absolute');
10627 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10628 this.maskEl.right.hide();
10630 this.toolTip.hide();
10632 this.toolTip.el.hide();
10634 window.onwheel = function(){ return true;};
10636 if(this.intervalID){
10637 window.clearInterval(this.intervalID);
10638 this.intervalID = false;
10641 this.isMasked = false;
10651 * Ext JS Library 1.1.1
10652 * Copyright(c) 2006-2007, Ext JS, LLC.
10654 * Originally Released Under LGPL - original licence link has changed is not relivant.
10657 * <script type="text/javascript">
10660 * @class Roo.form.VTypes
10661 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10664 Roo.form.VTypes = function(){
10665 // closure these in so they are only created once.
10666 var alpha = /^[a-zA-Z_]+$/;
10667 var alphanum = /^[a-zA-Z0-9_]+$/;
10668 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10669 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10671 // All these messages and functions are configurable
10674 * The function used to validate email addresses
10675 * @param {String} value The email address
10677 'email' : function(v){
10678 return email.test(v);
10681 * The error text to display when the email validation function returns false
10684 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10686 * The keystroke filter mask to be applied on email input
10689 'emailMask' : /[a-z0-9_\.\-@]/i,
10692 * The function used to validate URLs
10693 * @param {String} value The URL
10695 'url' : function(v){
10696 return url.test(v);
10699 * The error text to display when the url validation function returns false
10702 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10705 * The function used to validate alpha values
10706 * @param {String} value The value
10708 'alpha' : function(v){
10709 return alpha.test(v);
10712 * The error text to display when the alpha validation function returns false
10715 'alphaText' : 'This field should only contain letters and _',
10717 * The keystroke filter mask to be applied on alpha input
10720 'alphaMask' : /[a-z_]/i,
10723 * The function used to validate alphanumeric values
10724 * @param {String} value The value
10726 'alphanum' : function(v){
10727 return alphanum.test(v);
10730 * The error text to display when the alphanumeric validation function returns false
10733 'alphanumText' : 'This field should only contain letters, numbers and _',
10735 * The keystroke filter mask to be applied on alphanumeric input
10738 'alphanumMask' : /[a-z0-9_]/i
10748 * @class Roo.bootstrap.Input
10749 * @extends Roo.bootstrap.Component
10750 * Bootstrap Input class
10751 * @cfg {Boolean} disabled is it disabled
10752 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
10753 * @cfg {String} name name of the input
10754 * @cfg {string} fieldLabel - the label associated
10755 * @cfg {string} placeholder - placeholder to put in text.
10756 * @cfg {string} before - input group add on before
10757 * @cfg {string} after - input group add on after
10758 * @cfg {string} size - (lg|sm) or leave empty..
10759 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10760 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10761 * @cfg {Number} md colspan out of 12 for computer-sized screens
10762 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10763 * @cfg {string} value default value of the input
10764 * @cfg {Number} labelWidth set the width of label
10765 * @cfg {Number} labellg set the width of label (1-12)
10766 * @cfg {Number} labelmd set the width of label (1-12)
10767 * @cfg {Number} labelsm set the width of label (1-12)
10768 * @cfg {Number} labelxs set the width of label (1-12)
10769 * @cfg {String} labelAlign (top|left)
10770 * @cfg {Boolean} readOnly Specifies that the field should be read-only
10771 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10772 * @cfg {String} indicatorpos (left|right) default left
10773 * @cfg {String} capture (user|camera) use for file input only. (default empty)
10774 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10775 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10777 * @cfg {String} align (left|center|right) Default left
10778 * @cfg {Boolean} forceFeedback (true|false) Default false
10781 * Create a new Input
10782 * @param {Object} config The config object
10785 Roo.bootstrap.Input = function(config){
10787 Roo.bootstrap.Input.superclass.constructor.call(this, config);
10792 * Fires when this field receives input focus.
10793 * @param {Roo.form.Field} this
10798 * Fires when this field loses input focus.
10799 * @param {Roo.form.Field} this
10803 * @event specialkey
10804 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
10805 * {@link Roo.EventObject#getKey} to determine which key was pressed.
10806 * @param {Roo.form.Field} this
10807 * @param {Roo.EventObject} e The event object
10812 * Fires just before the field blurs if the field value has changed.
10813 * @param {Roo.form.Field} this
10814 * @param {Mixed} newValue The new value
10815 * @param {Mixed} oldValue The original value
10820 * Fires after the field has been marked as invalid.
10821 * @param {Roo.form.Field} this
10822 * @param {String} msg The validation message
10827 * Fires after the field has been validated with no errors.
10828 * @param {Roo.form.Field} this
10833 * Fires after the key up
10834 * @param {Roo.form.Field} this
10835 * @param {Roo.EventObject} e The event Object
10840 * Fires after the user pastes into input
10841 * @param {Roo.form.Field} this
10842 * @param {Roo.EventObject} e The event Object
10848 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
10850 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10851 automatic validation (defaults to "keyup").
10853 validationEvent : "keyup",
10855 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10857 validateOnBlur : true,
10859 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10861 validationDelay : 250,
10863 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10865 focusClass : "x-form-focus", // not needed???
10869 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10871 invalidClass : "has-warning",
10874 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10876 validClass : "has-success",
10879 * @cfg {Boolean} hasFeedback (true|false) default true
10881 hasFeedback : true,
10884 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10886 invalidFeedbackClass : "glyphicon-warning-sign",
10889 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10891 validFeedbackClass : "glyphicon-ok",
10894 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10896 selectOnFocus : false,
10899 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10903 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10908 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10910 disableKeyFilter : false,
10913 * @cfg {Boolean} disabled True to disable the field (defaults to false).
10917 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10921 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10923 blankText : "Please complete this mandatory field",
10926 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10930 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10932 maxLength : Number.MAX_VALUE,
10934 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10936 minLengthText : "The minimum length for this field is {0}",
10938 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10940 maxLengthText : "The maximum length for this field is {0}",
10944 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10945 * If available, this function will be called only after the basic validators all return true, and will be passed the
10946 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10950 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10951 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10952 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
10956 * @cfg {String} regexText -- Depricated - use Invalid Text
10961 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10967 autocomplete: false,
10971 inputType : 'text',
10974 placeholder: false,
10979 preventMark: false,
10980 isFormField : true,
10983 labelAlign : false,
10986 formatedValue : false,
10987 forceFeedback : false,
10989 indicatorpos : 'left',
10999 parentLabelAlign : function()
11002 while (parent.parent()) {
11003 parent = parent.parent();
11004 if (typeof(parent.labelAlign) !='undefined') {
11005 return parent.labelAlign;
11012 getAutoCreate : function()
11014 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11020 if(this.inputType != 'hidden'){
11021 cfg.cls = 'form-group' //input-group
11027 type : this.inputType,
11028 value : this.value,
11029 cls : 'form-control',
11030 placeholder : this.placeholder || '',
11031 autocomplete : this.autocomplete || 'new-password'
11033 if (this.inputType == 'file') {
11034 input.style = 'overflow:hidden'; // why not in CSS?
11037 if(this.capture.length){
11038 input.capture = this.capture;
11041 if(this.accept.length){
11042 input.accept = this.accept + "/*";
11046 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
11049 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11050 input.maxLength = this.maxLength;
11053 if (this.disabled) {
11054 input.disabled=true;
11057 if (this.readOnly) {
11058 input.readonly=true;
11062 input.name = this.name;
11066 input.cls += ' input-' + this.size;
11070 ['xs','sm','md','lg'].map(function(size){
11071 if (settings[size]) {
11072 cfg.cls += ' col-' + size + '-' + settings[size];
11076 var inputblock = input;
11080 cls: 'glyphicon form-control-feedback'
11083 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11086 cls : 'has-feedback',
11094 if (this.before || this.after) {
11097 cls : 'input-group',
11101 if (this.before && typeof(this.before) == 'string') {
11103 inputblock.cn.push({
11105 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
11109 if (this.before && typeof(this.before) == 'object') {
11110 this.before = Roo.factory(this.before);
11112 inputblock.cn.push({
11114 cls : 'roo-input-before input-group-prepend input-group-' +
11115 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
11119 inputblock.cn.push(input);
11121 if (this.after && typeof(this.after) == 'string') {
11122 inputblock.cn.push({
11124 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
11128 if (this.after && typeof(this.after) == 'object') {
11129 this.after = Roo.factory(this.after);
11131 inputblock.cn.push({
11133 cls : 'roo-input-after input-group-append input-group-' +
11134 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
11138 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11139 inputblock.cls += ' has-feedback';
11140 inputblock.cn.push(feedback);
11145 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11146 tooltip : 'This field is required'
11148 if (this.allowBlank ) {
11149 indicator.style = this.allowBlank ? ' display:none' : '';
11151 if (align ==='left' && this.fieldLabel.length) {
11153 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
11160 cls : 'control-label col-form-label',
11161 html : this.fieldLabel
11172 var labelCfg = cfg.cn[1];
11173 var contentCfg = cfg.cn[2];
11175 if(this.indicatorpos == 'right'){
11180 cls : 'control-label col-form-label',
11184 html : this.fieldLabel
11198 labelCfg = cfg.cn[0];
11199 contentCfg = cfg.cn[1];
11203 if(this.labelWidth > 12){
11204 labelCfg.style = "width: " + this.labelWidth + 'px';
11207 if(this.labelWidth < 13 && this.labelmd == 0){
11208 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
11211 if(this.labellg > 0){
11212 labelCfg.cls += ' col-lg-' + this.labellg;
11213 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11216 if(this.labelmd > 0){
11217 labelCfg.cls += ' col-md-' + this.labelmd;
11218 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11221 if(this.labelsm > 0){
11222 labelCfg.cls += ' col-sm-' + this.labelsm;
11223 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11226 if(this.labelxs > 0){
11227 labelCfg.cls += ' col-xs-' + this.labelxs;
11228 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11232 } else if ( this.fieldLabel.length) {
11239 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
11240 tooltip : 'This field is required',
11241 style : this.allowBlank ? ' display:none' : ''
11245 //cls : 'input-group-addon',
11246 html : this.fieldLabel
11254 if(this.indicatorpos == 'right'){
11259 //cls : 'input-group-addon',
11260 html : this.fieldLabel
11265 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
11266 tooltip : 'This field is required',
11267 style : this.allowBlank ? ' display:none' : ''
11287 if (this.parentType === 'Navbar' && this.parent().bar) {
11288 cfg.cls += ' navbar-form';
11291 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
11292 // on BS4 we do this only if not form
11293 cfg.cls += ' navbar-form';
11301 * return the real input element.
11303 inputEl: function ()
11305 return this.el.select('input.form-control',true).first();
11308 tooltipEl : function()
11310 return this.inputEl();
11313 indicatorEl : function()
11315 if (Roo.bootstrap.version == 4) {
11316 return false; // not enabled in v4 yet.
11319 var indicator = this.el.select('i.roo-required-indicator',true).first();
11329 setDisabled : function(v)
11331 var i = this.inputEl().dom;
11333 i.removeAttribute('disabled');
11337 i.setAttribute('disabled','true');
11339 initEvents : function()
11342 this.inputEl().on("keydown" , this.fireKey, this);
11343 this.inputEl().on("focus", this.onFocus, this);
11344 this.inputEl().on("blur", this.onBlur, this);
11346 this.inputEl().relayEvent('keyup', this);
11347 this.inputEl().relayEvent('paste', this);
11349 this.indicator = this.indicatorEl();
11351 if(this.indicator){
11352 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
11355 // reference to original value for reset
11356 this.originalValue = this.getValue();
11357 //Roo.form.TextField.superclass.initEvents.call(this);
11358 if(this.validationEvent == 'keyup'){
11359 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11360 this.inputEl().on('keyup', this.filterValidation, this);
11362 else if(this.validationEvent !== false){
11363 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11366 if(this.selectOnFocus){
11367 this.on("focus", this.preFocus, this);
11370 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11371 this.inputEl().on("keypress", this.filterKeys, this);
11373 this.inputEl().relayEvent('keypress', this);
11376 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
11377 this.el.on("click", this.autoSize, this);
11380 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11381 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11384 if (typeof(this.before) == 'object') {
11385 this.before.render(this.el.select('.roo-input-before',true).first());
11387 if (typeof(this.after) == 'object') {
11388 this.after.render(this.el.select('.roo-input-after',true).first());
11391 this.inputEl().on('change', this.onChange, this);
11394 filterValidation : function(e){
11395 if(!e.isNavKeyPress()){
11396 this.validationTask.delay(this.validationDelay);
11400 * Validates the field value
11401 * @return {Boolean} True if the value is valid, else false
11403 validate : function(){
11404 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11405 if(this.disabled || this.validateValue(this.getRawValue())){
11410 this.markInvalid();
11416 * Validates a value according to the field's validation rules and marks the field as invalid
11417 * if the validation fails
11418 * @param {Mixed} value The value to validate
11419 * @return {Boolean} True if the value is valid, else false
11421 validateValue : function(value)
11423 if(this.getVisibilityEl().hasClass('hidden')){
11427 if(value.length < 1) { // if it's blank
11428 if(this.allowBlank){
11434 if(value.length < this.minLength){
11437 if(value.length > this.maxLength){
11441 var vt = Roo.form.VTypes;
11442 if(!vt[this.vtype](value, this)){
11446 if(typeof this.validator == "function"){
11447 var msg = this.validator(value);
11451 if (typeof(msg) == 'string') {
11452 this.invalidText = msg;
11456 if(this.regex && !this.regex.test(value)){
11464 fireKey : function(e){
11465 //Roo.log('field ' + e.getKey());
11466 if(e.isNavKeyPress()){
11467 this.fireEvent("specialkey", this, e);
11470 focus : function (selectText){
11472 this.inputEl().focus();
11473 if(selectText === true){
11474 this.inputEl().dom.select();
11480 onFocus : function(){
11481 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11482 // this.el.addClass(this.focusClass);
11484 if(!this.hasFocus){
11485 this.hasFocus = true;
11486 this.startValue = this.getValue();
11487 this.fireEvent("focus", this);
11491 beforeBlur : Roo.emptyFn,
11495 onBlur : function(){
11497 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11498 //this.el.removeClass(this.focusClass);
11500 this.hasFocus = false;
11501 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11504 var v = this.getValue();
11505 if(String(v) !== String(this.startValue)){
11506 this.fireEvent('change', this, v, this.startValue);
11508 this.fireEvent("blur", this);
11511 onChange : function(e)
11513 var v = this.getValue();
11514 if(String(v) !== String(this.startValue)){
11515 this.fireEvent('change', this, v, this.startValue);
11521 * Resets the current field value to the originally loaded value and clears any validation messages
11523 reset : function(){
11524 this.setValue(this.originalValue);
11528 * Returns the name of the field
11529 * @return {Mixed} name The name field
11531 getName: function(){
11535 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
11536 * @return {Mixed} value The field value
11538 getValue : function(){
11540 var v = this.inputEl().getValue();
11545 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
11546 * @return {Mixed} value The field value
11548 getRawValue : function(){
11549 var v = this.inputEl().getValue();
11555 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
11556 * @param {Mixed} value The value to set
11558 setRawValue : function(v){
11559 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11562 selectText : function(start, end){
11563 var v = this.getRawValue();
11565 start = start === undefined ? 0 : start;
11566 end = end === undefined ? v.length : end;
11567 var d = this.inputEl().dom;
11568 if(d.setSelectionRange){
11569 d.setSelectionRange(start, end);
11570 }else if(d.createTextRange){
11571 var range = d.createTextRange();
11572 range.moveStart("character", start);
11573 range.moveEnd("character", v.length-end);
11580 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
11581 * @param {Mixed} value The value to set
11583 setValue : function(v){
11586 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11592 processValue : function(value){
11593 if(this.stripCharsRe){
11594 var newValue = value.replace(this.stripCharsRe, '');
11595 if(newValue !== value){
11596 this.setRawValue(newValue);
11603 preFocus : function(){
11605 if(this.selectOnFocus){
11606 this.inputEl().dom.select();
11609 filterKeys : function(e){
11610 var k = e.getKey();
11611 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11614 var c = e.getCharCode(), cc = String.fromCharCode(c);
11615 if(Roo.isIE && (e.isSpecialKey() || !cc)){
11618 if(!this.maskRe.test(cc)){
11623 * Clear any invalid styles/messages for this field
11625 clearInvalid : function(){
11627 if(!this.el || this.preventMark){ // not rendered
11632 this.el.removeClass([this.invalidClass, 'is-invalid']);
11634 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11636 var feedback = this.el.select('.form-control-feedback', true).first();
11639 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11644 if(this.indicator){
11645 this.indicator.removeClass('visible');
11646 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11649 this.fireEvent('valid', this);
11653 * Mark this field as valid
11655 markValid : function()
11657 if(!this.el || this.preventMark){ // not rendered...
11661 this.el.removeClass([this.invalidClass, this.validClass]);
11662 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11664 var feedback = this.el.select('.form-control-feedback', true).first();
11667 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11670 if(this.indicator){
11671 this.indicator.removeClass('visible');
11672 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11680 if(this.allowBlank && !this.getRawValue().length){
11683 if (Roo.bootstrap.version == 3) {
11684 this.el.addClass(this.validClass);
11686 this.inputEl().addClass('is-valid');
11689 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11691 var feedback = this.el.select('.form-control-feedback', true).first();
11694 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11695 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11700 this.fireEvent('valid', this);
11704 * Mark this field as invalid
11705 * @param {String} msg The validation message
11707 markInvalid : function(msg)
11709 if(!this.el || this.preventMark){ // not rendered
11713 this.el.removeClass([this.invalidClass, this.validClass]);
11714 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11716 var feedback = this.el.select('.form-control-feedback', true).first();
11719 this.el.select('.form-control-feedback', true).first().removeClass(
11720 [this.invalidFeedbackClass, this.validFeedbackClass]);
11727 if(this.allowBlank && !this.getRawValue().length){
11731 if(this.indicator){
11732 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11733 this.indicator.addClass('visible');
11735 if (Roo.bootstrap.version == 3) {
11736 this.el.addClass(this.invalidClass);
11738 this.inputEl().addClass('is-invalid');
11743 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11745 var feedback = this.el.select('.form-control-feedback', true).first();
11748 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11750 if(this.getValue().length || this.forceFeedback){
11751 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11758 this.fireEvent('invalid', this, msg);
11761 SafariOnKeyDown : function(event)
11763 // this is a workaround for a password hang bug on chrome/ webkit.
11764 if (this.inputEl().dom.type != 'password') {
11768 var isSelectAll = false;
11770 if(this.inputEl().dom.selectionEnd > 0){
11771 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11773 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11774 event.preventDefault();
11779 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11781 event.preventDefault();
11782 // this is very hacky as keydown always get's upper case.
11784 var cc = String.fromCharCode(event.getCharCode());
11785 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
11789 adjustWidth : function(tag, w){
11790 tag = tag.toLowerCase();
11791 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11792 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11793 if(tag == 'input'){
11796 if(tag == 'textarea'){
11799 }else if(Roo.isOpera){
11800 if(tag == 'input'){
11803 if(tag == 'textarea'){
11811 setFieldLabel : function(v)
11813 if(!this.rendered){
11817 if(this.indicatorEl()){
11818 var ar = this.el.select('label > span',true);
11820 if (ar.elements.length) {
11821 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11822 this.fieldLabel = v;
11826 var br = this.el.select('label',true);
11828 if(br.elements.length) {
11829 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11830 this.fieldLabel = v;
11834 Roo.log('Cannot Found any of label > span || label in input');
11838 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11839 this.fieldLabel = v;
11854 * @class Roo.bootstrap.TextArea
11855 * @extends Roo.bootstrap.Input
11856 * Bootstrap TextArea class
11857 * @cfg {Number} cols Specifies the visible width of a text area
11858 * @cfg {Number} rows Specifies the visible number of lines in a text area
11859 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11860 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11861 * @cfg {string} html text
11864 * Create a new TextArea
11865 * @param {Object} config The config object
11868 Roo.bootstrap.TextArea = function(config){
11869 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11873 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
11883 getAutoCreate : function(){
11885 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11891 if(this.inputType != 'hidden'){
11892 cfg.cls = 'form-group' //input-group
11900 value : this.value || '',
11901 html: this.html || '',
11902 cls : 'form-control',
11903 placeholder : this.placeholder || ''
11907 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11908 input.maxLength = this.maxLength;
11912 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11916 input.cols = this.cols;
11919 if (this.readOnly) {
11920 input.readonly = true;
11924 input.name = this.name;
11928 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11932 ['xs','sm','md','lg'].map(function(size){
11933 if (settings[size]) {
11934 cfg.cls += ' col-' + size + '-' + settings[size];
11938 var inputblock = input;
11940 if(this.hasFeedback && !this.allowBlank){
11944 cls: 'glyphicon form-control-feedback'
11948 cls : 'has-feedback',
11957 if (this.before || this.after) {
11960 cls : 'input-group',
11964 inputblock.cn.push({
11966 cls : 'input-group-addon',
11971 inputblock.cn.push(input);
11973 if(this.hasFeedback && !this.allowBlank){
11974 inputblock.cls += ' has-feedback';
11975 inputblock.cn.push(feedback);
11979 inputblock.cn.push({
11981 cls : 'input-group-addon',
11988 if (align ==='left' && this.fieldLabel.length) {
11993 cls : 'control-label',
11994 html : this.fieldLabel
12005 if(this.labelWidth > 12){
12006 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
12009 if(this.labelWidth < 13 && this.labelmd == 0){
12010 this.labelmd = this.labelWidth;
12013 if(this.labellg > 0){
12014 cfg.cn[0].cls += ' col-lg-' + this.labellg;
12015 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
12018 if(this.labelmd > 0){
12019 cfg.cn[0].cls += ' col-md-' + this.labelmd;
12020 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
12023 if(this.labelsm > 0){
12024 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
12025 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
12028 if(this.labelxs > 0){
12029 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
12030 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
12033 } else if ( this.fieldLabel.length) {
12038 //cls : 'input-group-addon',
12039 html : this.fieldLabel
12057 if (this.disabled) {
12058 input.disabled=true;
12065 * return the real textarea element.
12067 inputEl: function ()
12069 return this.el.select('textarea.form-control',true).first();
12073 * Clear any invalid styles/messages for this field
12075 clearInvalid : function()
12078 if(!this.el || this.preventMark){ // not rendered
12082 var label = this.el.select('label', true).first();
12083 var icon = this.el.select('i.fa-star', true).first();
12088 this.el.removeClass( this.validClass);
12089 this.inputEl().removeClass('is-invalid');
12091 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12093 var feedback = this.el.select('.form-control-feedback', true).first();
12096 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12101 this.fireEvent('valid', this);
12105 * Mark this field as valid
12107 markValid : function()
12109 if(!this.el || this.preventMark){ // not rendered
12113 this.el.removeClass([this.invalidClass, this.validClass]);
12114 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12116 var feedback = this.el.select('.form-control-feedback', true).first();
12119 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12122 if(this.disabled || this.allowBlank){
12126 var label = this.el.select('label', true).first();
12127 var icon = this.el.select('i.fa-star', true).first();
12132 if (Roo.bootstrap.version == 3) {
12133 this.el.addClass(this.validClass);
12135 this.inputEl().addClass('is-valid');
12139 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12141 var feedback = this.el.select('.form-control-feedback', true).first();
12144 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12145 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12150 this.fireEvent('valid', this);
12154 * Mark this field as invalid
12155 * @param {String} msg The validation message
12157 markInvalid : function(msg)
12159 if(!this.el || this.preventMark){ // not rendered
12163 this.el.removeClass([this.invalidClass, this.validClass]);
12164 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12166 var feedback = this.el.select('.form-control-feedback', true).first();
12169 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12172 if(this.disabled || this.allowBlank){
12176 var label = this.el.select('label', true).first();
12177 var icon = this.el.select('i.fa-star', true).first();
12179 if(!this.getValue().length && label && !icon){
12180 this.el.createChild({
12182 cls : 'text-danger fa fa-lg fa-star',
12183 tooltip : 'This field is required',
12184 style : 'margin-right:5px;'
12188 if (Roo.bootstrap.version == 3) {
12189 this.el.addClass(this.invalidClass);
12191 this.inputEl().addClass('is-invalid');
12194 // fixme ... this may be depricated need to test..
12195 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12197 var feedback = this.el.select('.form-control-feedback', true).first();
12200 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12202 if(this.getValue().length || this.forceFeedback){
12203 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12210 this.fireEvent('invalid', this, msg);
12218 * trigger field - base class for combo..
12223 * @class Roo.bootstrap.TriggerField
12224 * @extends Roo.bootstrap.Input
12225 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
12226 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
12227 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
12228 * for which you can provide a custom implementation. For example:
12230 var trigger = new Roo.bootstrap.TriggerField();
12231 trigger.onTriggerClick = myTriggerFn;
12232 trigger.applyTo('my-field');
12235 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
12236 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
12237 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
12238 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
12239 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
12242 * Create a new TriggerField.
12243 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
12244 * to the base TextField)
12246 Roo.bootstrap.TriggerField = function(config){
12247 this.mimicing = false;
12248 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
12251 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
12253 * @cfg {String} triggerClass A CSS class to apply to the trigger
12256 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
12261 * @cfg {Boolean} removable (true|false) special filter default false
12265 /** @cfg {Boolean} grow @hide */
12266 /** @cfg {Number} growMin @hide */
12267 /** @cfg {Number} growMax @hide */
12273 autoSize: Roo.emptyFn,
12277 deferHeight : true,
12280 actionMode : 'wrap',
12285 getAutoCreate : function(){
12287 var align = this.labelAlign || this.parentLabelAlign();
12292 cls: 'form-group' //input-group
12299 type : this.inputType,
12300 cls : 'form-control',
12301 autocomplete: 'new-password',
12302 placeholder : this.placeholder || ''
12306 input.name = this.name;
12309 input.cls += ' input-' + this.size;
12312 if (this.disabled) {
12313 input.disabled=true;
12316 var inputblock = input;
12318 if(this.hasFeedback && !this.allowBlank){
12322 cls: 'glyphicon form-control-feedback'
12325 if(this.removable && !this.editable ){
12327 cls : 'has-feedback',
12333 cls : 'roo-combo-removable-btn close'
12340 cls : 'has-feedback',
12349 if(this.removable && !this.editable ){
12351 cls : 'roo-removable',
12357 cls : 'roo-combo-removable-btn close'
12364 if (this.before || this.after) {
12367 cls : 'input-group',
12371 inputblock.cn.push({
12373 cls : 'input-group-addon input-group-prepend input-group-text',
12378 inputblock.cn.push(input);
12380 if(this.hasFeedback && !this.allowBlank){
12381 inputblock.cls += ' has-feedback';
12382 inputblock.cn.push(feedback);
12386 inputblock.cn.push({
12388 cls : 'input-group-addon input-group-append input-group-text',
12397 var ibwrap = inputblock;
12402 cls: 'roo-select2-choices',
12406 cls: 'roo-select2-search-field',
12418 cls: 'roo-select2-container input-group',
12423 cls: 'form-hidden-field'
12429 if(!this.multiple && this.showToggleBtn){
12435 if (this.caret != false) {
12438 cls: 'fa fa-' + this.caret
12445 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12447 Roo.bootstrap.version == 3 ? caret : '',
12450 cls: 'combobox-clear',
12464 combobox.cls += ' roo-select2-container-multi';
12468 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12469 tooltip : 'This field is required'
12471 if (Roo.bootstrap.version == 4) {
12474 style : 'display:none'
12479 if (align ==='left' && this.fieldLabel.length) {
12481 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12488 cls : 'control-label',
12489 html : this.fieldLabel
12501 var labelCfg = cfg.cn[1];
12502 var contentCfg = cfg.cn[2];
12504 if(this.indicatorpos == 'right'){
12509 cls : 'control-label',
12513 html : this.fieldLabel
12527 labelCfg = cfg.cn[0];
12528 contentCfg = cfg.cn[1];
12531 if(this.labelWidth > 12){
12532 labelCfg.style = "width: " + this.labelWidth + 'px';
12535 if(this.labelWidth < 13 && this.labelmd == 0){
12536 this.labelmd = this.labelWidth;
12539 if(this.labellg > 0){
12540 labelCfg.cls += ' col-lg-' + this.labellg;
12541 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12544 if(this.labelmd > 0){
12545 labelCfg.cls += ' col-md-' + this.labelmd;
12546 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12549 if(this.labelsm > 0){
12550 labelCfg.cls += ' col-sm-' + this.labelsm;
12551 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12554 if(this.labelxs > 0){
12555 labelCfg.cls += ' col-xs-' + this.labelxs;
12556 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12559 } else if ( this.fieldLabel.length) {
12560 // Roo.log(" label");
12565 //cls : 'input-group-addon',
12566 html : this.fieldLabel
12574 if(this.indicatorpos == 'right'){
12582 html : this.fieldLabel
12596 // Roo.log(" no label && no align");
12603 ['xs','sm','md','lg'].map(function(size){
12604 if (settings[size]) {
12605 cfg.cls += ' col-' + size + '-' + settings[size];
12616 onResize : function(w, h){
12617 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12618 // if(typeof w == 'number'){
12619 // var x = w - this.trigger.getWidth();
12620 // this.inputEl().setWidth(this.adjustWidth('input', x));
12621 // this.trigger.setStyle('left', x+'px');
12626 adjustSize : Roo.BoxComponent.prototype.adjustSize,
12629 getResizeEl : function(){
12630 return this.inputEl();
12634 getPositionEl : function(){
12635 return this.inputEl();
12639 alignErrorIcon : function(){
12640 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12644 initEvents : function(){
12648 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12649 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12650 if(!this.multiple && this.showToggleBtn){
12651 this.trigger = this.el.select('span.dropdown-toggle',true).first();
12652 if(this.hideTrigger){
12653 this.trigger.setDisplayed(false);
12655 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12659 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12662 if(this.removable && !this.editable && !this.tickable){
12663 var close = this.closeTriggerEl();
12666 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12667 close.on('click', this.removeBtnClick, this, close);
12671 //this.trigger.addClassOnOver('x-form-trigger-over');
12672 //this.trigger.addClassOnClick('x-form-trigger-click');
12675 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12679 closeTriggerEl : function()
12681 var close = this.el.select('.roo-combo-removable-btn', true).first();
12682 return close ? close : false;
12685 removeBtnClick : function(e, h, el)
12687 e.preventDefault();
12689 if(this.fireEvent("remove", this) !== false){
12691 this.fireEvent("afterremove", this)
12695 createList : function()
12697 this.list = Roo.get(document.body).createChild({
12698 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12699 cls: 'typeahead typeahead-long dropdown-menu shadow',
12700 style: 'display:none'
12703 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12708 initTrigger : function(){
12713 onDestroy : function(){
12715 this.trigger.removeAllListeners();
12716 // this.trigger.remove();
12719 // this.wrap.remove();
12721 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12725 onFocus : function(){
12726 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12728 if(!this.mimicing){
12729 this.wrap.addClass('x-trigger-wrap-focus');
12730 this.mimicing = true;
12731 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12732 if(this.monitorTab){
12733 this.el.on("keydown", this.checkTab, this);
12740 checkTab : function(e){
12741 if(e.getKey() == e.TAB){
12742 this.triggerBlur();
12747 onBlur : function(){
12752 mimicBlur : function(e, t){
12754 if(!this.wrap.contains(t) && this.validateBlur()){
12755 this.triggerBlur();
12761 triggerBlur : function(){
12762 this.mimicing = false;
12763 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12764 if(this.monitorTab){
12765 this.el.un("keydown", this.checkTab, this);
12767 //this.wrap.removeClass('x-trigger-wrap-focus');
12768 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12772 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12773 validateBlur : function(e, t){
12778 onDisable : function(){
12779 this.inputEl().dom.disabled = true;
12780 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12782 // this.wrap.addClass('x-item-disabled');
12787 onEnable : function(){
12788 this.inputEl().dom.disabled = false;
12789 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12791 // this.el.removeClass('x-item-disabled');
12796 onShow : function(){
12797 var ae = this.getActionEl();
12800 ae.dom.style.display = '';
12801 ae.dom.style.visibility = 'visible';
12807 onHide : function(){
12808 var ae = this.getActionEl();
12809 ae.dom.style.display = 'none';
12813 * The function that should handle the trigger's click event. This method does nothing by default until overridden
12814 * by an implementing function.
12816 * @param {EventObject} e
12818 onTriggerClick : Roo.emptyFn
12826 * @class Roo.bootstrap.CardUploader
12827 * @extends Roo.bootstrap.Button
12828 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12829 * @cfg {Number} errorTimeout default 3000
12830 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
12831 * @cfg {Array} html The button text.
12835 * Create a new CardUploader
12836 * @param {Object} config The config object
12839 Roo.bootstrap.CardUploader = function(config){
12843 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12846 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
12854 * When a image is clicked on - and needs to display a slideshow or similar..
12855 * @param {Roo.bootstrap.Card} this
12856 * @param {Object} The image information data
12862 * When a the download link is clicked
12863 * @param {Roo.bootstrap.Card} this
12864 * @param {Object} The image information data contains
12871 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
12874 errorTimeout : 3000,
12878 fileCollection : false,
12881 getAutoCreate : function()
12885 cls :'form-group' ,
12890 //cls : 'input-group-addon',
12891 html : this.fieldLabel
12899 value : this.value,
12900 cls : 'd-none form-control'
12905 multiple : 'multiple',
12907 cls : 'd-none roo-card-upload-selector'
12911 cls : 'roo-card-uploader-button-container w-100 mb-2'
12914 cls : 'card-columns roo-card-uploader-container'
12924 getChildContainer : function() /// what children are added to.
12926 return this.containerEl;
12929 getButtonContainer : function() /// what children are added to.
12931 return this.el.select(".roo-card-uploader-button-container").first();
12934 initEvents : function()
12937 Roo.bootstrap.Input.prototype.initEvents.call(this);
12941 xns: Roo.bootstrap,
12944 container_method : 'getButtonContainer' ,
12945 html : this.html, // fix changable?
12948 'click' : function(btn, e) {
12957 this.urlAPI = (window.createObjectURL && window) ||
12958 (window.URL && URL.revokeObjectURL && URL) ||
12959 (window.webkitURL && webkitURL);
12964 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12966 this.selectorEl.on('change', this.onFileSelected, this);
12969 this.images.forEach(function(img) {
12972 this.images = false;
12974 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12980 onClick : function(e)
12982 e.preventDefault();
12984 this.selectorEl.dom.click();
12988 onFileSelected : function(e)
12990 e.preventDefault();
12992 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12996 Roo.each(this.selectorEl.dom.files, function(file){
12997 this.addFile(file);
13006 addFile : function(file)
13009 if(typeof(file) === 'string'){
13010 throw "Add file by name?"; // should not happen
13014 if(!file || !this.urlAPI){
13024 var url = _this.urlAPI.createObjectURL( file);
13027 id : Roo.bootstrap.CardUploader.ID--,
13028 is_uploaded : false,
13032 mimetype : file.type,
13040 * addCard - add an Attachment to the uploader
13041 * @param data - the data about the image to upload
13045 title : "Title of file",
13046 is_uploaded : false,
13047 src : "http://.....",
13048 srcfile : { the File upload object },
13049 mimetype : file.type,
13052 .. any other data...
13058 addCard : function (data)
13060 // hidden input element?
13061 // if the file is not an image...
13062 //then we need to use something other that and header_image
13067 xns : Roo.bootstrap,
13068 xtype : 'CardFooter',
13071 xns : Roo.bootstrap,
13077 xns : Roo.bootstrap,
13079 html : String.format("<small>{0}</small>", data.title),
13080 cls : 'col-10 text-left',
13085 click : function() {
13087 t.fireEvent( "download", t, data );
13093 xns : Roo.bootstrap,
13095 style: 'max-height: 28px; ',
13101 click : function() {
13102 t.removeCard(data.id)
13114 var cn = this.addxtype(
13117 xns : Roo.bootstrap,
13120 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
13121 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
13122 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
13127 initEvents : function() {
13128 Roo.bootstrap.Card.prototype.initEvents.call(this);
13130 this.imgEl = this.el.select('.card-img-top').first();
13132 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
13133 this.imgEl.set({ 'pointer' : 'cursor' });
13136 this.getCardFooter().addClass('p-1');
13143 // dont' really need ot update items.
13144 // this.items.push(cn);
13145 this.fileCollection.add(cn);
13147 if (!data.srcfile) {
13148 this.updateInput();
13153 var reader = new FileReader();
13154 reader.addEventListener("load", function() {
13155 data.srcdata = reader.result;
13158 reader.readAsDataURL(data.srcfile);
13163 removeCard : function(id)
13166 var card = this.fileCollection.get(id);
13167 card.data.is_deleted = 1;
13168 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
13169 //this.fileCollection.remove(card);
13170 //this.items = this.items.filter(function(e) { return e != card });
13171 // dont' really need ot update items.
13172 card.el.dom.parentNode.removeChild(card.el.dom);
13173 this.updateInput();
13179 this.fileCollection.each(function(card) {
13180 if (card.el.dom && card.el.dom.parentNode) {
13181 card.el.dom.parentNode.removeChild(card.el.dom);
13184 this.fileCollection.clear();
13185 this.updateInput();
13188 updateInput : function()
13191 this.fileCollection.each(function(e) {
13195 this.inputEl().dom.value = JSON.stringify(data);
13205 Roo.bootstrap.CardUploader.ID = -1;/*
13207 * Ext JS Library 1.1.1
13208 * Copyright(c) 2006-2007, Ext JS, LLC.
13210 * Originally Released Under LGPL - original licence link has changed is not relivant.
13213 * <script type="text/javascript">
13218 * @class Roo.data.SortTypes
13220 * Defines the default sorting (casting?) comparison functions used when sorting data.
13222 Roo.data.SortTypes = {
13224 * Default sort that does nothing
13225 * @param {Mixed} s The value being converted
13226 * @return {Mixed} The comparison value
13228 none : function(s){
13233 * The regular expression used to strip tags
13237 stripTagsRE : /<\/?[^>]+>/gi,
13240 * Strips all HTML tags to sort on text only
13241 * @param {Mixed} s The value being converted
13242 * @return {String} The comparison value
13244 asText : function(s){
13245 return String(s).replace(this.stripTagsRE, "");
13249 * Strips all HTML tags to sort on text only - Case insensitive
13250 * @param {Mixed} s The value being converted
13251 * @return {String} The comparison value
13253 asUCText : function(s){
13254 return String(s).toUpperCase().replace(this.stripTagsRE, "");
13258 * Case insensitive string
13259 * @param {Mixed} s The value being converted
13260 * @return {String} The comparison value
13262 asUCString : function(s) {
13263 return String(s).toUpperCase();
13268 * @param {Mixed} s The value being converted
13269 * @return {Number} The comparison value
13271 asDate : function(s) {
13275 if(s instanceof Date){
13276 return s.getTime();
13278 return Date.parse(String(s));
13283 * @param {Mixed} s The value being converted
13284 * @return {Float} The comparison value
13286 asFloat : function(s) {
13287 var val = parseFloat(String(s).replace(/,/g, ""));
13296 * @param {Mixed} s The value being converted
13297 * @return {Number} The comparison value
13299 asInt : function(s) {
13300 var val = parseInt(String(s).replace(/,/g, ""));
13308 * Ext JS Library 1.1.1
13309 * Copyright(c) 2006-2007, Ext JS, LLC.
13311 * Originally Released Under LGPL - original licence link has changed is not relivant.
13314 * <script type="text/javascript">
13318 * @class Roo.data.Record
13319 * Instances of this class encapsulate both record <em>definition</em> information, and record
13320 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13321 * to access Records cached in an {@link Roo.data.Store} object.<br>
13323 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13324 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13327 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13329 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13330 * {@link #create}. The parameters are the same.
13331 * @param {Array} data An associative Array of data values keyed by the field name.
13332 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13333 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13334 * not specified an integer id is generated.
13336 Roo.data.Record = function(data, id){
13337 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13342 * Generate a constructor for a specific record layout.
13343 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13344 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13345 * Each field definition object may contain the following properties: <ul>
13346 * <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,
13347 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13348 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13349 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13350 * is being used, then this is a string containing the javascript expression to reference the data relative to
13351 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13352 * to the data item relative to the record element. If the mapping expression is the same as the field name,
13353 * this may be omitted.</p></li>
13354 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13355 * <ul><li>auto (Default, implies no conversion)</li>
13360 * <li>date</li></ul></p></li>
13361 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13362 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13363 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13364 * by the Reader into an object that will be stored in the Record. It is passed the
13365 * following parameters:<ul>
13366 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13368 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13370 * <br>usage:<br><pre><code>
13371 var TopicRecord = Roo.data.Record.create(
13372 {name: 'title', mapping: 'topic_title'},
13373 {name: 'author', mapping: 'username'},
13374 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13375 {name: 'lastPost', mapping: 'post_time', type: 'date'},
13376 {name: 'lastPoster', mapping: 'user2'},
13377 {name: 'excerpt', mapping: 'post_text'}
13380 var myNewRecord = new TopicRecord({
13381 title: 'Do my job please',
13384 lastPost: new Date(),
13385 lastPoster: 'Animal',
13386 excerpt: 'No way dude!'
13388 myStore.add(myNewRecord);
13393 Roo.data.Record.create = function(o){
13394 var f = function(){
13395 f.superclass.constructor.apply(this, arguments);
13397 Roo.extend(f, Roo.data.Record);
13398 var p = f.prototype;
13399 p.fields = new Roo.util.MixedCollection(false, function(field){
13402 for(var i = 0, len = o.length; i < len; i++){
13403 p.fields.add(new Roo.data.Field(o[i]));
13405 f.getField = function(name){
13406 return p.fields.get(name);
13411 Roo.data.Record.AUTO_ID = 1000;
13412 Roo.data.Record.EDIT = 'edit';
13413 Roo.data.Record.REJECT = 'reject';
13414 Roo.data.Record.COMMIT = 'commit';
13416 Roo.data.Record.prototype = {
13418 * Readonly flag - true if this record has been modified.
13427 join : function(store){
13428 this.store = store;
13432 * Set the named field to the specified value.
13433 * @param {String} name The name of the field to set.
13434 * @param {Object} value The value to set the field to.
13436 set : function(name, value){
13437 if(this.data[name] == value){
13441 if(!this.modified){
13442 this.modified = {};
13444 if(typeof this.modified[name] == 'undefined'){
13445 this.modified[name] = this.data[name];
13447 this.data[name] = value;
13448 if(!this.editing && this.store){
13449 this.store.afterEdit(this);
13454 * Get the value of the named field.
13455 * @param {String} name The name of the field to get the value of.
13456 * @return {Object} The value of the field.
13458 get : function(name){
13459 return this.data[name];
13463 beginEdit : function(){
13464 this.editing = true;
13465 this.modified = {};
13469 cancelEdit : function(){
13470 this.editing = false;
13471 delete this.modified;
13475 endEdit : function(){
13476 this.editing = false;
13477 if(this.dirty && this.store){
13478 this.store.afterEdit(this);
13483 * Usually called by the {@link Roo.data.Store} which owns the Record.
13484 * Rejects all changes made to the Record since either creation, or the last commit operation.
13485 * Modified fields are reverted to their original values.
13487 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13488 * of reject operations.
13490 reject : function(){
13491 var m = this.modified;
13493 if(typeof m[n] != "function"){
13494 this.data[n] = m[n];
13497 this.dirty = false;
13498 delete this.modified;
13499 this.editing = false;
13501 this.store.afterReject(this);
13506 * Usually called by the {@link Roo.data.Store} which owns the Record.
13507 * Commits all changes made to the Record since either creation, or the last commit operation.
13509 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13510 * of commit operations.
13512 commit : function(){
13513 this.dirty = false;
13514 delete this.modified;
13515 this.editing = false;
13517 this.store.afterCommit(this);
13522 hasError : function(){
13523 return this.error != null;
13527 clearError : function(){
13532 * Creates a copy of this record.
13533 * @param {String} id (optional) A new record id if you don't want to use this record's id
13536 copy : function(newId) {
13537 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13541 * Ext JS Library 1.1.1
13542 * Copyright(c) 2006-2007, Ext JS, LLC.
13544 * Originally Released Under LGPL - original licence link has changed is not relivant.
13547 * <script type="text/javascript">
13553 * @class Roo.data.Store
13554 * @extends Roo.util.Observable
13555 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13556 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13558 * 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
13559 * has no knowledge of the format of the data returned by the Proxy.<br>
13561 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13562 * instances from the data object. These records are cached and made available through accessor functions.
13564 * Creates a new Store.
13565 * @param {Object} config A config object containing the objects needed for the Store to access data,
13566 * and read the data into Records.
13568 Roo.data.Store = function(config){
13569 this.data = new Roo.util.MixedCollection(false);
13570 this.data.getKey = function(o){
13573 this.baseParams = {};
13575 this.paramNames = {
13580 "multisort" : "_multisort"
13583 if(config && config.data){
13584 this.inlineData = config.data;
13585 delete config.data;
13588 Roo.apply(this, config);
13590 if(this.reader){ // reader passed
13591 this.reader = Roo.factory(this.reader, Roo.data);
13592 this.reader.xmodule = this.xmodule || false;
13593 if(!this.recordType){
13594 this.recordType = this.reader.recordType;
13596 if(this.reader.onMetaChange){
13597 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13601 if(this.recordType){
13602 this.fields = this.recordType.prototype.fields;
13604 this.modified = [];
13608 * @event datachanged
13609 * Fires when the data cache has changed, and a widget which is using this Store
13610 * as a Record cache should refresh its view.
13611 * @param {Store} this
13613 datachanged : true,
13615 * @event metachange
13616 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13617 * @param {Store} this
13618 * @param {Object} meta The JSON metadata
13623 * Fires when Records have been added to the Store
13624 * @param {Store} this
13625 * @param {Roo.data.Record[]} records The array of Records added
13626 * @param {Number} index The index at which the record(s) were added
13631 * Fires when a Record has been removed from the Store
13632 * @param {Store} this
13633 * @param {Roo.data.Record} record The Record that was removed
13634 * @param {Number} index The index at which the record was removed
13639 * Fires when a Record has been updated
13640 * @param {Store} this
13641 * @param {Roo.data.Record} record The Record that was updated
13642 * @param {String} operation The update operation being performed. Value may be one of:
13644 Roo.data.Record.EDIT
13645 Roo.data.Record.REJECT
13646 Roo.data.Record.COMMIT
13652 * Fires when the data cache has been cleared.
13653 * @param {Store} this
13657 * @event beforeload
13658 * Fires before a request is made for a new data object. If the beforeload handler returns false
13659 * the load action will be canceled.
13660 * @param {Store} this
13661 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13665 * @event beforeloadadd
13666 * Fires after a new set of Records has been loaded.
13667 * @param {Store} this
13668 * @param {Roo.data.Record[]} records The Records that were loaded
13669 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13671 beforeloadadd : true,
13674 * Fires after a new set of Records has been loaded, before they are added to the store.
13675 * @param {Store} this
13676 * @param {Roo.data.Record[]} records The Records that were loaded
13677 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13678 * @params {Object} return from reader
13682 * @event loadexception
13683 * Fires if an exception occurs in the Proxy during loading.
13684 * Called with the signature of the Proxy's "loadexception" event.
13685 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13688 * @param {Object} return from JsonData.reader() - success, totalRecords, records
13689 * @param {Object} load options
13690 * @param {Object} jsonData from your request (normally this contains the Exception)
13692 loadexception : true
13696 this.proxy = Roo.factory(this.proxy, Roo.data);
13697 this.proxy.xmodule = this.xmodule || false;
13698 this.relayEvents(this.proxy, ["loadexception"]);
13700 this.sortToggle = {};
13701 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13703 Roo.data.Store.superclass.constructor.call(this);
13705 if(this.inlineData){
13706 this.loadData(this.inlineData);
13707 delete this.inlineData;
13711 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13713 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
13714 * without a remote query - used by combo/forms at present.
13718 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13721 * @cfg {Array} data Inline data to be loaded when the store is initialized.
13724 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13725 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13728 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13729 * on any HTTP request
13732 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13735 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13739 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13740 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13742 remoteSort : false,
13745 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13746 * loaded or when a record is removed. (defaults to false).
13748 pruneModifiedRecords : false,
13751 lastOptions : null,
13754 * Add Records to the Store and fires the add event.
13755 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13757 add : function(records){
13758 records = [].concat(records);
13759 for(var i = 0, len = records.length; i < len; i++){
13760 records[i].join(this);
13762 var index = this.data.length;
13763 this.data.addAll(records);
13764 this.fireEvent("add", this, records, index);
13768 * Remove a Record from the Store and fires the remove event.
13769 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13771 remove : function(record){
13772 var index = this.data.indexOf(record);
13773 this.data.removeAt(index);
13775 if(this.pruneModifiedRecords){
13776 this.modified.remove(record);
13778 this.fireEvent("remove", this, record, index);
13782 * Remove all Records from the Store and fires the clear event.
13784 removeAll : function(){
13786 if(this.pruneModifiedRecords){
13787 this.modified = [];
13789 this.fireEvent("clear", this);
13793 * Inserts Records to the Store at the given index and fires the add event.
13794 * @param {Number} index The start index at which to insert the passed Records.
13795 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13797 insert : function(index, records){
13798 records = [].concat(records);
13799 for(var i = 0, len = records.length; i < len; i++){
13800 this.data.insert(index, records[i]);
13801 records[i].join(this);
13803 this.fireEvent("add", this, records, index);
13807 * Get the index within the cache of the passed Record.
13808 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13809 * @return {Number} The index of the passed Record. Returns -1 if not found.
13811 indexOf : function(record){
13812 return this.data.indexOf(record);
13816 * Get the index within the cache of the Record with the passed id.
13817 * @param {String} id The id of the Record to find.
13818 * @return {Number} The index of the Record. Returns -1 if not found.
13820 indexOfId : function(id){
13821 return this.data.indexOfKey(id);
13825 * Get the Record with the specified id.
13826 * @param {String} id The id of the Record to find.
13827 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13829 getById : function(id){
13830 return this.data.key(id);
13834 * Get the Record at the specified index.
13835 * @param {Number} index The index of the Record to find.
13836 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13838 getAt : function(index){
13839 return this.data.itemAt(index);
13843 * Returns a range of Records between specified indices.
13844 * @param {Number} startIndex (optional) The starting index (defaults to 0)
13845 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13846 * @return {Roo.data.Record[]} An array of Records
13848 getRange : function(start, end){
13849 return this.data.getRange(start, end);
13853 storeOptions : function(o){
13854 o = Roo.apply({}, o);
13857 this.lastOptions = o;
13861 * Loads the Record cache from the configured Proxy using the configured Reader.
13863 * If using remote paging, then the first load call must specify the <em>start</em>
13864 * and <em>limit</em> properties in the options.params property to establish the initial
13865 * position within the dataset, and the number of Records to cache on each read from the Proxy.
13867 * <strong>It is important to note that for remote data sources, loading is asynchronous,
13868 * and this call will return before the new data has been loaded. Perform any post-processing
13869 * in a callback function, or in a "load" event handler.</strong>
13871 * @param {Object} options An object containing properties which control loading options:<ul>
13872 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13873 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13874 * passed the following arguments:<ul>
13875 * <li>r : Roo.data.Record[]</li>
13876 * <li>options: Options object from the load call</li>
13877 * <li>success: Boolean success indicator</li></ul></li>
13878 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13879 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13882 load : function(options){
13883 options = options || {};
13884 if(this.fireEvent("beforeload", this, options) !== false){
13885 this.storeOptions(options);
13886 var p = Roo.apply(options.params || {}, this.baseParams);
13887 // if meta was not loaded from remote source.. try requesting it.
13888 if (!this.reader.metaFromRemote) {
13889 p._requestMeta = 1;
13891 if(this.sortInfo && this.remoteSort){
13892 var pn = this.paramNames;
13893 p[pn["sort"]] = this.sortInfo.field;
13894 p[pn["dir"]] = this.sortInfo.direction;
13896 if (this.multiSort) {
13897 var pn = this.paramNames;
13898 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13901 this.proxy.load(p, this.reader, this.loadRecords, this, options);
13906 * Reloads the Record cache from the configured Proxy using the configured Reader and
13907 * the options from the last load operation performed.
13908 * @param {Object} options (optional) An object containing properties which may override the options
13909 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13910 * the most recently used options are reused).
13912 reload : function(options){
13913 this.load(Roo.applyIf(options||{}, this.lastOptions));
13917 // Called as a callback by the Reader during a load operation.
13918 loadRecords : function(o, options, success){
13919 if(!o || success === false){
13920 if(success !== false){
13921 this.fireEvent("load", this, [], options, o);
13923 if(options.callback){
13924 options.callback.call(options.scope || this, [], options, false);
13928 // if data returned failure - throw an exception.
13929 if (o.success === false) {
13930 // show a message if no listener is registered.
13931 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13932 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13934 // loadmask wil be hooked into this..
13935 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13938 var r = o.records, t = o.totalRecords || r.length;
13940 this.fireEvent("beforeloadadd", this, r, options, o);
13942 if(!options || options.add !== true){
13943 if(this.pruneModifiedRecords){
13944 this.modified = [];
13946 for(var i = 0, len = r.length; i < len; i++){
13950 this.data = this.snapshot;
13951 delete this.snapshot;
13954 this.data.addAll(r);
13955 this.totalLength = t;
13957 this.fireEvent("datachanged", this);
13959 this.totalLength = Math.max(t, this.data.length+r.length);
13963 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13965 var e = new Roo.data.Record({});
13967 e.set(this.parent.displayField, this.parent.emptyTitle);
13968 e.set(this.parent.valueField, '');
13973 this.fireEvent("load", this, r, options, o);
13974 if(options.callback){
13975 options.callback.call(options.scope || this, r, options, true);
13981 * Loads data from a passed data block. A Reader which understands the format of the data
13982 * must have been configured in the constructor.
13983 * @param {Object} data The data block from which to read the Records. The format of the data expected
13984 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13985 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13987 loadData : function(o, append){
13988 var r = this.reader.readRecords(o);
13989 this.loadRecords(r, {add: append}, true);
13993 * using 'cn' the nested child reader read the child array into it's child stores.
13994 * @param {Object} rec The record with a 'children array
13996 loadDataFromChildren : function(rec)
13998 this.loadData(this.reader.toLoadData(rec));
14003 * Gets the number of cached records.
14005 * <em>If using paging, this may not be the total size of the dataset. If the data object
14006 * used by the Reader contains the dataset size, then the getTotalCount() function returns
14007 * the data set size</em>
14009 getCount : function(){
14010 return this.data.length || 0;
14014 * Gets the total number of records in the dataset as returned by the server.
14016 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
14017 * the dataset size</em>
14019 getTotalCount : function(){
14020 return this.totalLength || 0;
14024 * Returns the sort state of the Store as an object with two properties:
14026 field {String} The name of the field by which the Records are sorted
14027 direction {String} The sort order, "ASC" or "DESC"
14030 getSortState : function(){
14031 return this.sortInfo;
14035 applySort : function(){
14036 if(this.sortInfo && !this.remoteSort){
14037 var s = this.sortInfo, f = s.field;
14038 var st = this.fields.get(f).sortType;
14039 var fn = function(r1, r2){
14040 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
14041 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
14043 this.data.sort(s.direction, fn);
14044 if(this.snapshot && this.snapshot != this.data){
14045 this.snapshot.sort(s.direction, fn);
14051 * Sets the default sort column and order to be used by the next load operation.
14052 * @param {String} fieldName The name of the field to sort by.
14053 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14055 setDefaultSort : function(field, dir){
14056 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
14060 * Sort the Records.
14061 * If remote sorting is used, the sort is performed on the server, and the cache is
14062 * reloaded. If local sorting is used, the cache is sorted internally.
14063 * @param {String} fieldName The name of the field to sort by.
14064 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14066 sort : function(fieldName, dir){
14067 var f = this.fields.get(fieldName);
14069 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
14071 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
14072 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
14077 this.sortToggle[f.name] = dir;
14078 this.sortInfo = {field: f.name, direction: dir};
14079 if(!this.remoteSort){
14081 this.fireEvent("datachanged", this);
14083 this.load(this.lastOptions);
14088 * Calls the specified function for each of the Records in the cache.
14089 * @param {Function} fn The function to call. The Record is passed as the first parameter.
14090 * Returning <em>false</em> aborts and exits the iteration.
14091 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
14093 each : function(fn, scope){
14094 this.data.each(fn, scope);
14098 * Gets all records modified since the last commit. Modified records are persisted across load operations
14099 * (e.g., during paging).
14100 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
14102 getModifiedRecords : function(){
14103 return this.modified;
14107 createFilterFn : function(property, value, anyMatch){
14108 if(!value.exec){ // not a regex
14109 value = String(value);
14110 if(value.length == 0){
14113 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
14115 return function(r){
14116 return value.test(r.data[property]);
14121 * Sums the value of <i>property</i> for each record between start and end and returns the result.
14122 * @param {String} property A field on your records
14123 * @param {Number} start The record index to start at (defaults to 0)
14124 * @param {Number} end The last record index to include (defaults to length - 1)
14125 * @return {Number} The sum
14127 sum : function(property, start, end){
14128 var rs = this.data.items, v = 0;
14129 start = start || 0;
14130 end = (end || end === 0) ? end : rs.length-1;
14132 for(var i = start; i <= end; i++){
14133 v += (rs[i].data[property] || 0);
14139 * Filter the records by a specified property.
14140 * @param {String} field A field on your records
14141 * @param {String/RegExp} value Either a string that the field
14142 * should start with or a RegExp to test against the field
14143 * @param {Boolean} anyMatch True to match any part not just the beginning
14145 filter : function(property, value, anyMatch){
14146 var fn = this.createFilterFn(property, value, anyMatch);
14147 return fn ? this.filterBy(fn) : this.clearFilter();
14151 * Filter by a function. The specified function will be called with each
14152 * record in this data source. If the function returns true the record is included,
14153 * otherwise it is filtered.
14154 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14155 * @param {Object} scope (optional) The scope of the function (defaults to this)
14157 filterBy : function(fn, scope){
14158 this.snapshot = this.snapshot || this.data;
14159 this.data = this.queryBy(fn, scope||this);
14160 this.fireEvent("datachanged", this);
14164 * Query the records by a specified property.
14165 * @param {String} field A field on your records
14166 * @param {String/RegExp} value Either a string that the field
14167 * should start with or a RegExp to test against the field
14168 * @param {Boolean} anyMatch True to match any part not just the beginning
14169 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14171 query : function(property, value, anyMatch){
14172 var fn = this.createFilterFn(property, value, anyMatch);
14173 return fn ? this.queryBy(fn) : this.data.clone();
14177 * Query by a function. The specified function will be called with each
14178 * record in this data source. If the function returns true the record is included
14180 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14181 * @param {Object} scope (optional) The scope of the function (defaults to this)
14182 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14184 queryBy : function(fn, scope){
14185 var data = this.snapshot || this.data;
14186 return data.filterBy(fn, scope||this);
14190 * Collects unique values for a particular dataIndex from this store.
14191 * @param {String} dataIndex The property to collect
14192 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
14193 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
14194 * @return {Array} An array of the unique values
14196 collect : function(dataIndex, allowNull, bypassFilter){
14197 var d = (bypassFilter === true && this.snapshot) ?
14198 this.snapshot.items : this.data.items;
14199 var v, sv, r = [], l = {};
14200 for(var i = 0, len = d.length; i < len; i++){
14201 v = d[i].data[dataIndex];
14203 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
14212 * Revert to a view of the Record cache with no filtering applied.
14213 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
14215 clearFilter : function(suppressEvent){
14216 if(this.snapshot && this.snapshot != this.data){
14217 this.data = this.snapshot;
14218 delete this.snapshot;
14219 if(suppressEvent !== true){
14220 this.fireEvent("datachanged", this);
14226 afterEdit : function(record){
14227 if(this.modified.indexOf(record) == -1){
14228 this.modified.push(record);
14230 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
14234 afterReject : function(record){
14235 this.modified.remove(record);
14236 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
14240 afterCommit : function(record){
14241 this.modified.remove(record);
14242 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
14246 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
14247 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
14249 commitChanges : function(){
14250 var m = this.modified.slice(0);
14251 this.modified = [];
14252 for(var i = 0, len = m.length; i < len; i++){
14258 * Cancel outstanding changes on all changed records.
14260 rejectChanges : function(){
14261 var m = this.modified.slice(0);
14262 this.modified = [];
14263 for(var i = 0, len = m.length; i < len; i++){
14268 onMetaChange : function(meta, rtype, o){
14269 this.recordType = rtype;
14270 this.fields = rtype.prototype.fields;
14271 delete this.snapshot;
14272 this.sortInfo = meta.sortInfo || this.sortInfo;
14273 this.modified = [];
14274 this.fireEvent('metachange', this, this.reader.meta);
14277 moveIndex : function(data, type)
14279 var index = this.indexOf(data);
14281 var newIndex = index + type;
14285 this.insert(newIndex, data);
14290 * Ext JS Library 1.1.1
14291 * Copyright(c) 2006-2007, Ext JS, LLC.
14293 * Originally Released Under LGPL - original licence link has changed is not relivant.
14296 * <script type="text/javascript">
14300 * @class Roo.data.SimpleStore
14301 * @extends Roo.data.Store
14302 * Small helper class to make creating Stores from Array data easier.
14303 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
14304 * @cfg {Array} fields An array of field definition objects, or field name strings.
14305 * @cfg {Object} an existing reader (eg. copied from another store)
14306 * @cfg {Array} data The multi-dimensional array of data
14308 * @param {Object} config
14310 Roo.data.SimpleStore = function(config)
14312 Roo.data.SimpleStore.superclass.constructor.call(this, {
14314 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
14317 Roo.data.Record.create(config.fields)
14319 proxy : new Roo.data.MemoryProxy(config.data)
14323 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
14325 * Ext JS Library 1.1.1
14326 * Copyright(c) 2006-2007, Ext JS, LLC.
14328 * Originally Released Under LGPL - original licence link has changed is not relivant.
14331 * <script type="text/javascript">
14336 * @extends Roo.data.Store
14337 * @class Roo.data.JsonStore
14338 * Small helper class to make creating Stores for JSON data easier. <br/>
14340 var store = new Roo.data.JsonStore({
14341 url: 'get-images.php',
14343 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14346 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14347 * JsonReader and HttpProxy (unless inline data is provided).</b>
14348 * @cfg {Array} fields An array of field definition objects, or field name strings.
14350 * @param {Object} config
14352 Roo.data.JsonStore = function(c){
14353 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14354 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14355 reader: new Roo.data.JsonReader(c, c.fields)
14358 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14360 * Ext JS Library 1.1.1
14361 * Copyright(c) 2006-2007, Ext JS, LLC.
14363 * Originally Released Under LGPL - original licence link has changed is not relivant.
14366 * <script type="text/javascript">
14370 Roo.data.Field = function(config){
14371 if(typeof config == "string"){
14372 config = {name: config};
14374 Roo.apply(this, config);
14377 this.type = "auto";
14380 var st = Roo.data.SortTypes;
14381 // named sortTypes are supported, here we look them up
14382 if(typeof this.sortType == "string"){
14383 this.sortType = st[this.sortType];
14386 // set default sortType for strings and dates
14387 if(!this.sortType){
14390 this.sortType = st.asUCString;
14393 this.sortType = st.asDate;
14396 this.sortType = st.none;
14401 var stripRe = /[\$,%]/g;
14403 // prebuilt conversion function for this field, instead of
14404 // switching every time we're reading a value
14406 var cv, dateFormat = this.dateFormat;
14411 cv = function(v){ return v; };
14414 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14418 return v !== undefined && v !== null && v !== '' ?
14419 parseInt(String(v).replace(stripRe, ""), 10) : '';
14424 return v !== undefined && v !== null && v !== '' ?
14425 parseFloat(String(v).replace(stripRe, ""), 10) : '';
14430 cv = function(v){ return v === true || v === "true" || v == 1; };
14437 if(v instanceof Date){
14441 if(dateFormat == "timestamp"){
14442 return new Date(v*1000);
14444 return Date.parseDate(v, dateFormat);
14446 var parsed = Date.parse(v);
14447 return parsed ? new Date(parsed) : null;
14456 Roo.data.Field.prototype = {
14464 * Ext JS Library 1.1.1
14465 * Copyright(c) 2006-2007, Ext JS, LLC.
14467 * Originally Released Under LGPL - original licence link has changed is not relivant.
14470 * <script type="text/javascript">
14473 // Base class for reading structured data from a data source. This class is intended to be
14474 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14477 * @class Roo.data.DataReader
14478 * Base class for reading structured data from a data source. This class is intended to be
14479 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14482 Roo.data.DataReader = function(meta, recordType){
14486 this.recordType = recordType instanceof Array ?
14487 Roo.data.Record.create(recordType) : recordType;
14490 Roo.data.DataReader.prototype = {
14493 readerType : 'Data',
14495 * Create an empty record
14496 * @param {Object} data (optional) - overlay some values
14497 * @return {Roo.data.Record} record created.
14499 newRow : function(d) {
14501 this.recordType.prototype.fields.each(function(c) {
14503 case 'int' : da[c.name] = 0; break;
14504 case 'date' : da[c.name] = new Date(); break;
14505 case 'float' : da[c.name] = 0.0; break;
14506 case 'boolean' : da[c.name] = false; break;
14507 default : da[c.name] = ""; break;
14511 return new this.recordType(Roo.apply(da, d));
14517 * Ext JS Library 1.1.1
14518 * Copyright(c) 2006-2007, Ext JS, LLC.
14520 * Originally Released Under LGPL - original licence link has changed is not relivant.
14523 * <script type="text/javascript">
14527 * @class Roo.data.DataProxy
14528 * @extends Roo.data.Observable
14529 * This class is an abstract base class for implementations which provide retrieval of
14530 * unformatted data objects.<br>
14532 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14533 * (of the appropriate type which knows how to parse the data object) to provide a block of
14534 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14536 * Custom implementations must implement the load method as described in
14537 * {@link Roo.data.HttpProxy#load}.
14539 Roo.data.DataProxy = function(){
14542 * @event beforeload
14543 * Fires before a network request is made to retrieve a data object.
14544 * @param {Object} This DataProxy object.
14545 * @param {Object} params The params parameter to the load function.
14550 * Fires before the load method's callback is called.
14551 * @param {Object} This DataProxy object.
14552 * @param {Object} o The data object.
14553 * @param {Object} arg The callback argument object passed to the load function.
14557 * @event loadexception
14558 * Fires if an Exception occurs during data retrieval.
14559 * @param {Object} This DataProxy object.
14560 * @param {Object} o The data object.
14561 * @param {Object} arg The callback argument object passed to the load function.
14562 * @param {Object} e The Exception.
14564 loadexception : true
14566 Roo.data.DataProxy.superclass.constructor.call(this);
14569 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14572 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14576 * Ext JS Library 1.1.1
14577 * Copyright(c) 2006-2007, Ext JS, LLC.
14579 * Originally Released Under LGPL - original licence link has changed is not relivant.
14582 * <script type="text/javascript">
14585 * @class Roo.data.MemoryProxy
14586 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14587 * to the Reader when its load method is called.
14589 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14591 Roo.data.MemoryProxy = function(data){
14595 Roo.data.MemoryProxy.superclass.constructor.call(this);
14599 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14602 * Load data from the requested source (in this case an in-memory
14603 * data object passed to the constructor), read the data object into
14604 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14605 * process that block using the passed callback.
14606 * @param {Object} params This parameter is not used by the MemoryProxy class.
14607 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14608 * object into a block of Roo.data.Records.
14609 * @param {Function} callback The function into which to pass the block of Roo.data.records.
14610 * The function must be passed <ul>
14611 * <li>The Record block object</li>
14612 * <li>The "arg" argument from the load function</li>
14613 * <li>A boolean success indicator</li>
14615 * @param {Object} scope The scope in which to call the callback
14616 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14618 load : function(params, reader, callback, scope, arg){
14619 params = params || {};
14622 result = reader.readRecords(params.data ? params.data :this.data);
14624 this.fireEvent("loadexception", this, arg, null, e);
14625 callback.call(scope, null, arg, false);
14628 callback.call(scope, result, arg, true);
14632 update : function(params, records){
14637 * Ext JS Library 1.1.1
14638 * Copyright(c) 2006-2007, Ext JS, LLC.
14640 * Originally Released Under LGPL - original licence link has changed is not relivant.
14643 * <script type="text/javascript">
14646 * @class Roo.data.HttpProxy
14647 * @extends Roo.data.DataProxy
14648 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14649 * configured to reference a certain URL.<br><br>
14651 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14652 * from which the running page was served.<br><br>
14654 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14656 * Be aware that to enable the browser to parse an XML document, the server must set
14657 * the Content-Type header in the HTTP response to "text/xml".
14659 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14660 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
14661 * will be used to make the request.
14663 Roo.data.HttpProxy = function(conn){
14664 Roo.data.HttpProxy.superclass.constructor.call(this);
14665 // is conn a conn config or a real conn?
14667 this.useAjax = !conn || !conn.events;
14671 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14672 // thse are take from connection...
14675 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14678 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14679 * extra parameters to each request made by this object. (defaults to undefined)
14682 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14683 * to each request made by this object. (defaults to undefined)
14686 * @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)
14689 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14692 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14698 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14702 * Return the {@link Roo.data.Connection} object being used by this Proxy.
14703 * @return {Connection} The Connection object. This object may be used to subscribe to events on
14704 * a finer-grained basis than the DataProxy events.
14706 getConnection : function(){
14707 return this.useAjax ? Roo.Ajax : this.conn;
14711 * Load data from the configured {@link Roo.data.Connection}, read the data object into
14712 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14713 * process that block using the passed callback.
14714 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14715 * for the request to the remote server.
14716 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14717 * object into a block of Roo.data.Records.
14718 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14719 * The function must be passed <ul>
14720 * <li>The Record block object</li>
14721 * <li>The "arg" argument from the load function</li>
14722 * <li>A boolean success indicator</li>
14724 * @param {Object} scope The scope in which to call the callback
14725 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14727 load : function(params, reader, callback, scope, arg){
14728 if(this.fireEvent("beforeload", this, params) !== false){
14730 params : params || {},
14732 callback : callback,
14737 callback : this.loadResponse,
14741 Roo.applyIf(o, this.conn);
14742 if(this.activeRequest){
14743 Roo.Ajax.abort(this.activeRequest);
14745 this.activeRequest = Roo.Ajax.request(o);
14747 this.conn.request(o);
14750 callback.call(scope||this, null, arg, false);
14755 loadResponse : function(o, success, response){
14756 delete this.activeRequest;
14758 this.fireEvent("loadexception", this, o, response);
14759 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14764 result = o.reader.read(response);
14766 this.fireEvent("loadexception", this, o, response, e);
14767 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14771 this.fireEvent("load", this, o, o.request.arg);
14772 o.request.callback.call(o.request.scope, result, o.request.arg, true);
14776 update : function(dataSet){
14781 updateResponse : function(dataSet){
14786 * Ext JS Library 1.1.1
14787 * Copyright(c) 2006-2007, Ext JS, LLC.
14789 * Originally Released Under LGPL - original licence link has changed is not relivant.
14792 * <script type="text/javascript">
14796 * @class Roo.data.ScriptTagProxy
14797 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14798 * other than the originating domain of the running page.<br><br>
14800 * <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
14801 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14803 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14804 * source code that is used as the source inside a <script> tag.<br><br>
14806 * In order for the browser to process the returned data, the server must wrap the data object
14807 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14808 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14809 * depending on whether the callback name was passed:
14812 boolean scriptTag = false;
14813 String cb = request.getParameter("callback");
14816 response.setContentType("text/javascript");
14818 response.setContentType("application/x-json");
14820 Writer out = response.getWriter();
14822 out.write(cb + "(");
14824 out.print(dataBlock.toJsonString());
14831 * @param {Object} config A configuration object.
14833 Roo.data.ScriptTagProxy = function(config){
14834 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14835 Roo.apply(this, config);
14836 this.head = document.getElementsByTagName("head")[0];
14839 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14841 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14843 * @cfg {String} url The URL from which to request the data object.
14846 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14850 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14851 * the server the name of the callback function set up by the load call to process the returned data object.
14852 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14853 * javascript output which calls this named function passing the data object as its only parameter.
14855 callbackParam : "callback",
14857 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14858 * name to the request.
14863 * Load data from the configured URL, read the data object into
14864 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14865 * process that block using the passed callback.
14866 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14867 * for the request to the remote server.
14868 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14869 * object into a block of Roo.data.Records.
14870 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14871 * The function must be passed <ul>
14872 * <li>The Record block object</li>
14873 * <li>The "arg" argument from the load function</li>
14874 * <li>A boolean success indicator</li>
14876 * @param {Object} scope The scope in which to call the callback
14877 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14879 load : function(params, reader, callback, scope, arg){
14880 if(this.fireEvent("beforeload", this, params) !== false){
14882 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14884 var url = this.url;
14885 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14887 url += "&_dc=" + (new Date().getTime());
14889 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14892 cb : "stcCallback"+transId,
14893 scriptId : "stcScript"+transId,
14897 callback : callback,
14903 window[trans.cb] = function(o){
14904 conn.handleResponse(o, trans);
14907 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14909 if(this.autoAbort !== false){
14913 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14915 var script = document.createElement("script");
14916 script.setAttribute("src", url);
14917 script.setAttribute("type", "text/javascript");
14918 script.setAttribute("id", trans.scriptId);
14919 this.head.appendChild(script);
14921 this.trans = trans;
14923 callback.call(scope||this, null, arg, false);
14928 isLoading : function(){
14929 return this.trans ? true : false;
14933 * Abort the current server request.
14935 abort : function(){
14936 if(this.isLoading()){
14937 this.destroyTrans(this.trans);
14942 destroyTrans : function(trans, isLoaded){
14943 this.head.removeChild(document.getElementById(trans.scriptId));
14944 clearTimeout(trans.timeoutId);
14946 window[trans.cb] = undefined;
14948 delete window[trans.cb];
14951 // if hasn't been loaded, wait for load to remove it to prevent script error
14952 window[trans.cb] = function(){
14953 window[trans.cb] = undefined;
14955 delete window[trans.cb];
14962 handleResponse : function(o, trans){
14963 this.trans = false;
14964 this.destroyTrans(trans, true);
14967 result = trans.reader.readRecords(o);
14969 this.fireEvent("loadexception", this, o, trans.arg, e);
14970 trans.callback.call(trans.scope||window, null, trans.arg, false);
14973 this.fireEvent("load", this, o, trans.arg);
14974 trans.callback.call(trans.scope||window, result, trans.arg, true);
14978 handleFailure : function(trans){
14979 this.trans = false;
14980 this.destroyTrans(trans, false);
14981 this.fireEvent("loadexception", this, null, trans.arg);
14982 trans.callback.call(trans.scope||window, null, trans.arg, false);
14986 * Ext JS Library 1.1.1
14987 * Copyright(c) 2006-2007, Ext JS, LLC.
14989 * Originally Released Under LGPL - original licence link has changed is not relivant.
14992 * <script type="text/javascript">
14996 * @class Roo.data.JsonReader
14997 * @extends Roo.data.DataReader
14998 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14999 * based on mappings in a provided Roo.data.Record constructor.
15001 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
15002 * in the reply previously.
15007 var RecordDef = Roo.data.Record.create([
15008 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
15009 {name: 'occupation'} // This field will use "occupation" as the mapping.
15011 var myReader = new Roo.data.JsonReader({
15012 totalProperty: "results", // The property which contains the total dataset size (optional)
15013 root: "rows", // The property which contains an Array of row objects
15014 id: "id" // The property within each row object that provides an ID for the record (optional)
15018 * This would consume a JSON file like this:
15020 { 'results': 2, 'rows': [
15021 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
15022 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
15025 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
15026 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
15027 * paged from the remote server.
15028 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
15029 * @cfg {String} root name of the property which contains the Array of row objects.
15030 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15031 * @cfg {Array} fields Array of field definition objects
15033 * Create a new JsonReader
15034 * @param {Object} meta Metadata configuration options
15035 * @param {Object} recordType Either an Array of field definition objects,
15036 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
15038 Roo.data.JsonReader = function(meta, recordType){
15041 // set some defaults:
15042 Roo.applyIf(meta, {
15043 totalProperty: 'total',
15044 successProperty : 'success',
15049 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15051 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
15053 readerType : 'Json',
15056 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
15057 * Used by Store query builder to append _requestMeta to params.
15060 metaFromRemote : false,
15062 * This method is only used by a DataProxy which has retrieved data from a remote server.
15063 * @param {Object} response The XHR object which contains the JSON data in its responseText.
15064 * @return {Object} data A data block which is used by an Roo.data.Store object as
15065 * a cache of Roo.data.Records.
15067 read : function(response){
15068 var json = response.responseText;
15070 var o = /* eval:var:o */ eval("("+json+")");
15072 throw {message: "JsonReader.read: Json object not found"};
15078 this.metaFromRemote = true;
15079 this.meta = o.metaData;
15080 this.recordType = Roo.data.Record.create(o.metaData.fields);
15081 this.onMetaChange(this.meta, this.recordType, o);
15083 return this.readRecords(o);
15086 // private function a store will implement
15087 onMetaChange : function(meta, recordType, o){
15094 simpleAccess: function(obj, subsc) {
15101 getJsonAccessor: function(){
15103 return function(expr) {
15105 return(re.test(expr))
15106 ? new Function("obj", "return obj." + expr)
15111 return Roo.emptyFn;
15116 * Create a data block containing Roo.data.Records from an XML document.
15117 * @param {Object} o An object which contains an Array of row objects in the property specified
15118 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
15119 * which contains the total size of the dataset.
15120 * @return {Object} data A data block which is used by an Roo.data.Store object as
15121 * a cache of Roo.data.Records.
15123 readRecords : function(o){
15125 * After any data loads, the raw JSON data is available for further custom processing.
15129 var s = this.meta, Record = this.recordType,
15130 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
15132 // Generate extraction functions for the totalProperty, the root, the id, and for each field
15134 if(s.totalProperty) {
15135 this.getTotal = this.getJsonAccessor(s.totalProperty);
15137 if(s.successProperty) {
15138 this.getSuccess = this.getJsonAccessor(s.successProperty);
15140 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
15142 var g = this.getJsonAccessor(s.id);
15143 this.getId = function(rec) {
15145 return (r === undefined || r === "") ? null : r;
15148 this.getId = function(){return null;};
15151 for(var jj = 0; jj < fl; jj++){
15153 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
15154 this.ef[jj] = this.getJsonAccessor(map);
15158 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
15159 if(s.totalProperty){
15160 var vt = parseInt(this.getTotal(o), 10);
15165 if(s.successProperty){
15166 var vs = this.getSuccess(o);
15167 if(vs === false || vs === 'false'){
15172 for(var i = 0; i < c; i++){
15175 var id = this.getId(n);
15176 for(var j = 0; j < fl; j++){
15178 var v = this.ef[j](n);
15180 Roo.log('missing convert for ' + f.name);
15184 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
15186 var record = new Record(values, id);
15188 records[i] = record;
15194 totalRecords : totalRecords
15197 // used when loading children.. @see loadDataFromChildren
15198 toLoadData: function(rec)
15200 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15201 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15202 return { data : data, total : data.length };
15207 * Ext JS Library 1.1.1
15208 * Copyright(c) 2006-2007, Ext JS, LLC.
15210 * Originally Released Under LGPL - original licence link has changed is not relivant.
15213 * <script type="text/javascript">
15217 * @class Roo.data.ArrayReader
15218 * @extends Roo.data.DataReader
15219 * Data reader class to create an Array of Roo.data.Record objects from an Array.
15220 * Each element of that Array represents a row of data fields. The
15221 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
15222 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
15226 var RecordDef = Roo.data.Record.create([
15227 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
15228 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
15230 var myReader = new Roo.data.ArrayReader({
15231 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
15235 * This would consume an Array like this:
15237 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
15241 * Create a new JsonReader
15242 * @param {Object} meta Metadata configuration options.
15243 * @param {Object|Array} recordType Either an Array of field definition objects
15245 * @cfg {Array} fields Array of field definition objects
15246 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15247 * as specified to {@link Roo.data.Record#create},
15248 * or an {@link Roo.data.Record} object
15251 * created using {@link Roo.data.Record#create}.
15253 Roo.data.ArrayReader = function(meta, recordType)
15255 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15258 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
15261 * Create a data block containing Roo.data.Records from an XML document.
15262 * @param {Object} o An Array of row objects which represents the dataset.
15263 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
15264 * a cache of Roo.data.Records.
15266 readRecords : function(o)
15268 var sid = this.meta ? this.meta.id : null;
15269 var recordType = this.recordType, fields = recordType.prototype.fields;
15272 for(var i = 0; i < root.length; i++){
15275 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
15276 for(var j = 0, jlen = fields.length; j < jlen; j++){
15277 var f = fields.items[j];
15278 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
15279 var v = n[k] !== undefined ? n[k] : f.defaultValue;
15281 values[f.name] = v;
15283 var record = new recordType(values, id);
15285 records[records.length] = record;
15289 totalRecords : records.length
15292 // used when loading children.. @see loadDataFromChildren
15293 toLoadData: function(rec)
15295 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15296 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15307 * @class Roo.bootstrap.ComboBox
15308 * @extends Roo.bootstrap.TriggerField
15309 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
15310 * @cfg {Boolean} append (true|false) default false
15311 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
15312 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
15313 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
15314 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
15315 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
15316 * @cfg {Boolean} animate default true
15317 * @cfg {Boolean} emptyResultText only for touch device
15318 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15319 * @cfg {String} emptyTitle default ''
15320 * @cfg {Number} width fixed with? experimental
15322 * Create a new ComboBox.
15323 * @param {Object} config Configuration options
15325 Roo.bootstrap.ComboBox = function(config){
15326 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15330 * Fires when the dropdown list is expanded
15331 * @param {Roo.bootstrap.ComboBox} combo This combo box
15336 * Fires when the dropdown list is collapsed
15337 * @param {Roo.bootstrap.ComboBox} combo This combo box
15341 * @event beforeselect
15342 * Fires before a list item is selected. Return false to cancel the selection.
15343 * @param {Roo.bootstrap.ComboBox} combo This combo box
15344 * @param {Roo.data.Record} record The data record returned from the underlying store
15345 * @param {Number} index The index of the selected item in the dropdown list
15347 'beforeselect' : true,
15350 * Fires when a list item is selected
15351 * @param {Roo.bootstrap.ComboBox} combo This combo box
15352 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15353 * @param {Number} index The index of the selected item in the dropdown list
15357 * @event beforequery
15358 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15359 * The event object passed has these properties:
15360 * @param {Roo.bootstrap.ComboBox} combo This combo box
15361 * @param {String} query The query
15362 * @param {Boolean} forceAll true to force "all" query
15363 * @param {Boolean} cancel true to cancel the query
15364 * @param {Object} e The query event object
15366 'beforequery': true,
15369 * Fires when the 'add' icon is pressed (add a listener to enable add button)
15370 * @param {Roo.bootstrap.ComboBox} combo This combo box
15375 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15376 * @param {Roo.bootstrap.ComboBox} combo This combo box
15377 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15382 * Fires when the remove value from the combobox array
15383 * @param {Roo.bootstrap.ComboBox} combo This combo box
15387 * @event afterremove
15388 * Fires when the remove value from the combobox array
15389 * @param {Roo.bootstrap.ComboBox} combo This combo box
15391 'afterremove' : true,
15393 * @event specialfilter
15394 * Fires when specialfilter
15395 * @param {Roo.bootstrap.ComboBox} combo This combo box
15397 'specialfilter' : true,
15400 * Fires when tick the element
15401 * @param {Roo.bootstrap.ComboBox} combo This combo box
15405 * @event touchviewdisplay
15406 * Fires when touch view require special display (default is using displayField)
15407 * @param {Roo.bootstrap.ComboBox} combo This combo box
15408 * @param {Object} cfg set html .
15410 'touchviewdisplay' : true
15415 this.tickItems = [];
15417 this.selectedIndex = -1;
15418 if(this.mode == 'local'){
15419 if(config.queryDelay === undefined){
15420 this.queryDelay = 10;
15422 if(config.minChars === undefined){
15428 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15431 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15432 * rendering into an Roo.Editor, defaults to false)
15435 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15436 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15439 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15442 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15443 * the dropdown list (defaults to undefined, with no header element)
15447 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
15451 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15453 listWidth: undefined,
15455 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15456 * mode = 'remote' or 'text' if mode = 'local')
15458 displayField: undefined,
15461 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15462 * mode = 'remote' or 'value' if mode = 'local').
15463 * Note: use of a valueField requires the user make a selection
15464 * in order for a value to be mapped.
15466 valueField: undefined,
15468 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15473 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15474 * field's data value (defaults to the underlying DOM element's name)
15476 hiddenName: undefined,
15478 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15482 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15484 selectedClass: 'active',
15487 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15491 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15492 * anchor positions (defaults to 'tl-bl')
15494 listAlign: 'tl-bl?',
15496 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15500 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
15501 * query specified by the allQuery config option (defaults to 'query')
15503 triggerAction: 'query',
15505 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15506 * (defaults to 4, does not apply if editable = false)
15510 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15511 * delay (typeAheadDelay) if it matches a known value (defaults to false)
15515 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15516 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15520 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15521 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
15525 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
15526 * when editable = true (defaults to false)
15528 selectOnFocus:false,
15530 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15532 queryParam: 'query',
15534 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
15535 * when mode = 'remote' (defaults to 'Loading...')
15537 loadingText: 'Loading...',
15539 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15543 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15547 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15548 * traditional select (defaults to true)
15552 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15556 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15560 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15561 * listWidth has a higher value)
15565 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15566 * allow the user to set arbitrary text into the field (defaults to false)
15568 forceSelection:false,
15570 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15571 * if typeAhead = true (defaults to 250)
15573 typeAheadDelay : 250,
15575 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15576 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15578 valueNotFoundText : undefined,
15580 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15582 blockFocus : false,
15585 * @cfg {Boolean} disableClear Disable showing of clear button.
15587 disableClear : false,
15589 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
15591 alwaysQuery : false,
15594 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
15599 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15601 invalidClass : "has-warning",
15604 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15606 validClass : "has-success",
15609 * @cfg {Boolean} specialFilter (true|false) special filter default false
15611 specialFilter : false,
15614 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15616 mobileTouchView : true,
15619 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15621 useNativeIOS : false,
15624 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15626 mobile_restrict_height : false,
15628 ios_options : false,
15640 btnPosition : 'right',
15641 triggerList : true,
15642 showToggleBtn : true,
15644 emptyResultText: 'Empty',
15645 triggerText : 'Select',
15649 // element that contains real text value.. (when hidden is used..)
15651 getAutoCreate : function()
15656 * Render classic select for iso
15659 if(Roo.isIOS && this.useNativeIOS){
15660 cfg = this.getAutoCreateNativeIOS();
15668 if(Roo.isTouch && this.mobileTouchView){
15669 cfg = this.getAutoCreateTouchView();
15676 if(!this.tickable){
15677 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15682 * ComboBox with tickable selections
15685 var align = this.labelAlign || this.parentLabelAlign();
15688 cls : 'form-group roo-combobox-tickable' //input-group
15691 var btn_text_select = '';
15692 var btn_text_done = '';
15693 var btn_text_cancel = '';
15695 if (this.btn_text_show) {
15696 btn_text_select = 'Select';
15697 btn_text_done = 'Done';
15698 btn_text_cancel = 'Cancel';
15703 cls : 'tickable-buttons',
15708 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15709 //html : this.triggerText
15710 html: btn_text_select
15716 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15718 html: btn_text_done
15724 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15726 html: btn_text_cancel
15732 buttons.cn.unshift({
15734 cls: 'roo-select2-search-field-input'
15740 Roo.each(buttons.cn, function(c){
15742 c.cls += ' btn-' + _this.size;
15745 if (_this.disabled) {
15752 style : 'display: contents',
15757 cls: 'form-hidden-field'
15761 cls: 'roo-select2-choices',
15765 cls: 'roo-select2-search-field',
15776 cls: 'roo-select2-container input-group roo-select2-container-multi',
15782 // cls: 'typeahead typeahead-long dropdown-menu',
15783 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
15788 if(this.hasFeedback && !this.allowBlank){
15792 cls: 'glyphicon form-control-feedback'
15795 combobox.cn.push(feedback);
15802 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15803 tooltip : 'This field is required'
15805 if (Roo.bootstrap.version == 4) {
15808 style : 'display:none'
15811 if (align ==='left' && this.fieldLabel.length) {
15813 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
15820 cls : 'control-label col-form-label',
15821 html : this.fieldLabel
15833 var labelCfg = cfg.cn[1];
15834 var contentCfg = cfg.cn[2];
15837 if(this.indicatorpos == 'right'){
15843 cls : 'control-label col-form-label',
15847 html : this.fieldLabel
15863 labelCfg = cfg.cn[0];
15864 contentCfg = cfg.cn[1];
15868 if(this.labelWidth > 12){
15869 labelCfg.style = "width: " + this.labelWidth + 'px';
15871 if(this.width * 1 > 0){
15872 contentCfg.style = "width: " + this.width + 'px';
15874 if(this.labelWidth < 13 && this.labelmd == 0){
15875 this.labelmd = this.labelWidth;
15878 if(this.labellg > 0){
15879 labelCfg.cls += ' col-lg-' + this.labellg;
15880 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15883 if(this.labelmd > 0){
15884 labelCfg.cls += ' col-md-' + this.labelmd;
15885 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15888 if(this.labelsm > 0){
15889 labelCfg.cls += ' col-sm-' + this.labelsm;
15890 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15893 if(this.labelxs > 0){
15894 labelCfg.cls += ' col-xs-' + this.labelxs;
15895 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15899 } else if ( this.fieldLabel.length) {
15900 // Roo.log(" label");
15905 //cls : 'input-group-addon',
15906 html : this.fieldLabel
15911 if(this.indicatorpos == 'right'){
15915 //cls : 'input-group-addon',
15916 html : this.fieldLabel
15926 // Roo.log(" no label && no align");
15933 ['xs','sm','md','lg'].map(function(size){
15934 if (settings[size]) {
15935 cfg.cls += ' col-' + size + '-' + settings[size];
15943 _initEventsCalled : false,
15946 initEvents: function()
15948 if (this._initEventsCalled) { // as we call render... prevent looping...
15951 this._initEventsCalled = true;
15954 throw "can not find store for combo";
15957 this.indicator = this.indicatorEl();
15959 this.store = Roo.factory(this.store, Roo.data);
15960 this.store.parent = this;
15962 // if we are building from html. then this element is so complex, that we can not really
15963 // use the rendered HTML.
15964 // so we have to trash and replace the previous code.
15965 if (Roo.XComponent.build_from_html) {
15966 // remove this element....
15967 var e = this.el.dom, k=0;
15968 while (e ) { e = e.previousSibling; ++k;}
15973 this.rendered = false;
15975 this.render(this.parent().getChildContainer(true), k);
15978 if(Roo.isIOS && this.useNativeIOS){
15979 this.initIOSView();
15987 if(Roo.isTouch && this.mobileTouchView){
15988 this.initTouchView();
15993 this.initTickableEvents();
15997 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15999 if(this.hiddenName){
16001 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16003 this.hiddenField.dom.value =
16004 this.hiddenValue !== undefined ? this.hiddenValue :
16005 this.value !== undefined ? this.value : '';
16007 // prevent input submission
16008 this.el.dom.removeAttribute('name');
16009 this.hiddenField.dom.setAttribute('name', this.hiddenName);
16014 // this.el.dom.setAttribute('autocomplete', 'off');
16017 var cls = 'x-combo-list';
16019 //this.list = new Roo.Layer({
16020 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
16026 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16027 _this.list.setWidth(lw);
16030 this.list.on('mouseover', this.onViewOver, this);
16031 this.list.on('mousemove', this.onViewMove, this);
16032 this.list.on('scroll', this.onViewScroll, this);
16035 this.list.swallowEvent('mousewheel');
16036 this.assetHeight = 0;
16039 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
16040 this.assetHeight += this.header.getHeight();
16043 this.innerList = this.list.createChild({cls:cls+'-inner'});
16044 this.innerList.on('mouseover', this.onViewOver, this);
16045 this.innerList.on('mousemove', this.onViewMove, this);
16046 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16048 if(this.allowBlank && !this.pageSize && !this.disableClear){
16049 this.footer = this.list.createChild({cls:cls+'-ft'});
16050 this.pageTb = new Roo.Toolbar(this.footer);
16054 this.footer = this.list.createChild({cls:cls+'-ft'});
16055 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
16056 {pageSize: this.pageSize});
16060 if (this.pageTb && this.allowBlank && !this.disableClear) {
16062 this.pageTb.add(new Roo.Toolbar.Fill(), {
16063 cls: 'x-btn-icon x-btn-clear',
16065 handler: function()
16068 _this.clearValue();
16069 _this.onSelect(false, -1);
16074 this.assetHeight += this.footer.getHeight();
16079 this.tpl = Roo.bootstrap.version == 4 ?
16080 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
16081 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
16084 this.view = new Roo.View(this.list, this.tpl, {
16085 singleSelect:true, store: this.store, selectedClass: this.selectedClass
16087 //this.view.wrapEl.setDisplayed(false);
16088 this.view.on('click', this.onViewClick, this);
16091 this.store.on('beforeload', this.onBeforeLoad, this);
16092 this.store.on('load', this.onLoad, this);
16093 this.store.on('loadexception', this.onLoadException, this);
16095 if(this.resizable){
16096 this.resizer = new Roo.Resizable(this.list, {
16097 pinned:true, handles:'se'
16099 this.resizer.on('resize', function(r, w, h){
16100 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
16101 this.listWidth = w;
16102 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
16103 this.restrictHeight();
16105 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
16108 if(!this.editable){
16109 this.editable = true;
16110 this.setEditable(false);
16115 if (typeof(this.events.add.listeners) != 'undefined') {
16117 this.addicon = this.wrap.createChild(
16118 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
16120 this.addicon.on('click', function(e) {
16121 this.fireEvent('add', this);
16124 if (typeof(this.events.edit.listeners) != 'undefined') {
16126 this.editicon = this.wrap.createChild(
16127 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
16128 if (this.addicon) {
16129 this.editicon.setStyle('margin-left', '40px');
16131 this.editicon.on('click', function(e) {
16133 // we fire even if inothing is selected..
16134 this.fireEvent('edit', this, this.lastData );
16140 this.keyNav = new Roo.KeyNav(this.inputEl(), {
16141 "up" : function(e){
16142 this.inKeyMode = true;
16146 "down" : function(e){
16147 if(!this.isExpanded()){
16148 this.onTriggerClick();
16150 this.inKeyMode = true;
16155 "enter" : function(e){
16156 // this.onViewClick();
16160 if(this.fireEvent("specialkey", this, e)){
16161 this.onViewClick(false);
16167 "esc" : function(e){
16171 "tab" : function(e){
16174 if(this.fireEvent("specialkey", this, e)){
16175 this.onViewClick(false);
16183 doRelay : function(foo, bar, hname){
16184 if(hname == 'down' || this.scope.isExpanded()){
16185 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16194 this.queryDelay = Math.max(this.queryDelay || 10,
16195 this.mode == 'local' ? 10 : 250);
16198 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16200 if(this.typeAhead){
16201 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16203 if(this.editable !== false){
16204 this.inputEl().on("keyup", this.onKeyUp, this);
16206 if(this.forceSelection){
16207 this.inputEl().on('blur', this.doForce, this);
16211 this.choices = this.el.select('ul.roo-select2-choices', true).first();
16212 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16216 initTickableEvents: function()
16220 if(this.hiddenName){
16222 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16224 this.hiddenField.dom.value =
16225 this.hiddenValue !== undefined ? this.hiddenValue :
16226 this.value !== undefined ? this.value : '';
16228 // prevent input submission
16229 this.el.dom.removeAttribute('name');
16230 this.hiddenField.dom.setAttribute('name', this.hiddenName);
16235 // this.list = this.el.select('ul.dropdown-menu',true).first();
16237 this.choices = this.el.select('ul.roo-select2-choices', true).first();
16238 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16239 if(this.triggerList){
16240 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
16243 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
16244 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
16246 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
16247 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
16249 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
16250 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
16252 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
16253 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
16254 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
16257 this.cancelBtn.hide();
16262 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16263 _this.list.setWidth(lw);
16266 this.list.on('mouseover', this.onViewOver, this);
16267 this.list.on('mousemove', this.onViewMove, this);
16269 this.list.on('scroll', this.onViewScroll, this);
16272 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
16273 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
16276 this.view = new Roo.View(this.list, this.tpl, {
16281 selectedClass: this.selectedClass
16284 //this.view.wrapEl.setDisplayed(false);
16285 this.view.on('click', this.onViewClick, this);
16289 this.store.on('beforeload', this.onBeforeLoad, this);
16290 this.store.on('load', this.onLoad, this);
16291 this.store.on('loadexception', this.onLoadException, this);
16294 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
16295 "up" : function(e){
16296 this.inKeyMode = true;
16300 "down" : function(e){
16301 this.inKeyMode = true;
16305 "enter" : function(e){
16306 if(this.fireEvent("specialkey", this, e)){
16307 this.onViewClick(false);
16313 "esc" : function(e){
16314 this.onTickableFooterButtonClick(e, false, false);
16317 "tab" : function(e){
16318 this.fireEvent("specialkey", this, e);
16320 this.onTickableFooterButtonClick(e, false, false);
16327 doRelay : function(e, fn, key){
16328 if(this.scope.isExpanded()){
16329 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16338 this.queryDelay = Math.max(this.queryDelay || 10,
16339 this.mode == 'local' ? 10 : 250);
16342 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16344 if(this.typeAhead){
16345 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16348 if(this.editable !== false){
16349 this.tickableInputEl().on("keyup", this.onKeyUp, this);
16352 this.indicator = this.indicatorEl();
16354 if(this.indicator){
16355 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16356 this.indicator.hide();
16361 onDestroy : function(){
16363 this.view.setStore(null);
16364 this.view.el.removeAllListeners();
16365 this.view.el.remove();
16366 this.view.purgeListeners();
16369 this.list.dom.innerHTML = '';
16373 this.store.un('beforeload', this.onBeforeLoad, this);
16374 this.store.un('load', this.onLoad, this);
16375 this.store.un('loadexception', this.onLoadException, this);
16377 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16381 fireKey : function(e){
16382 if(e.isNavKeyPress() && !this.list.isVisible()){
16383 this.fireEvent("specialkey", this, e);
16388 onResize: function(w, h)
16392 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16394 // if(typeof w != 'number'){
16395 // // we do not handle it!?!?
16398 // var tw = this.trigger.getWidth();
16399 // // tw += this.addicon ? this.addicon.getWidth() : 0;
16400 // // tw += this.editicon ? this.editicon.getWidth() : 0;
16402 // this.inputEl().setWidth( this.adjustWidth('input', x));
16404 // //this.trigger.setStyle('left', x+'px');
16406 // if(this.list && this.listWidth === undefined){
16407 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16408 // this.list.setWidth(lw);
16409 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16417 * Allow or prevent the user from directly editing the field text. If false is passed,
16418 * the user will only be able to select from the items defined in the dropdown list. This method
16419 * is the runtime equivalent of setting the 'editable' config option at config time.
16420 * @param {Boolean} value True to allow the user to directly edit the field text
16422 setEditable : function(value){
16423 if(value == this.editable){
16426 this.editable = value;
16428 this.inputEl().dom.setAttribute('readOnly', true);
16429 this.inputEl().on('mousedown', this.onTriggerClick, this);
16430 this.inputEl().addClass('x-combo-noedit');
16432 this.inputEl().dom.removeAttribute('readOnly');
16433 this.inputEl().un('mousedown', this.onTriggerClick, this);
16434 this.inputEl().removeClass('x-combo-noedit');
16440 onBeforeLoad : function(combo,opts){
16441 if(!this.hasFocus){
16445 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16447 this.restrictHeight();
16448 this.selectedIndex = -1;
16452 onLoad : function(){
16454 this.hasQuery = false;
16456 if(!this.hasFocus){
16460 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16461 this.loading.hide();
16464 if(this.store.getCount() > 0){
16467 this.restrictHeight();
16468 if(this.lastQuery == this.allQuery){
16469 if(this.editable && !this.tickable){
16470 this.inputEl().dom.select();
16474 !this.selectByValue(this.value, true) &&
16477 !this.store.lastOptions ||
16478 typeof(this.store.lastOptions.add) == 'undefined' ||
16479 this.store.lastOptions.add != true
16482 this.select(0, true);
16485 if(this.autoFocus){
16488 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16489 this.taTask.delay(this.typeAheadDelay);
16493 this.onEmptyResults();
16499 onLoadException : function()
16501 this.hasQuery = false;
16503 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16504 this.loading.hide();
16507 if(this.tickable && this.editable){
16512 // only causes errors at present
16513 //Roo.log(this.store.reader.jsonData);
16514 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16516 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16522 onTypeAhead : function(){
16523 if(this.store.getCount() > 0){
16524 var r = this.store.getAt(0);
16525 var newValue = r.data[this.displayField];
16526 var len = newValue.length;
16527 var selStart = this.getRawValue().length;
16529 if(selStart != len){
16530 this.setRawValue(newValue);
16531 this.selectText(selStart, newValue.length);
16537 onSelect : function(record, index){
16539 if(this.fireEvent('beforeselect', this, record, index) !== false){
16541 this.setFromData(index > -1 ? record.data : false);
16544 this.fireEvent('select', this, record, index);
16549 * Returns the currently selected field value or empty string if no value is set.
16550 * @return {String} value The selected value
16552 getValue : function()
16554 if(Roo.isIOS && this.useNativeIOS){
16555 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16559 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16562 if(this.valueField){
16563 return typeof this.value != 'undefined' ? this.value : '';
16565 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16569 getRawValue : function()
16571 if(Roo.isIOS && this.useNativeIOS){
16572 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16575 var v = this.inputEl().getValue();
16581 * Clears any text/value currently set in the field
16583 clearValue : function(){
16585 if(this.hiddenField){
16586 this.hiddenField.dom.value = '';
16589 this.setRawValue('');
16590 this.lastSelectionText = '';
16591 this.lastData = false;
16593 var close = this.closeTriggerEl();
16604 * Sets the specified value into the field. If the value finds a match, the corresponding record text
16605 * will be displayed in the field. If the value does not match the data value of an existing item,
16606 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16607 * Otherwise the field will be blank (although the value will still be set).
16608 * @param {String} value The value to match
16610 setValue : function(v)
16612 if(Roo.isIOS && this.useNativeIOS){
16613 this.setIOSValue(v);
16623 if(this.valueField){
16624 var r = this.findRecord(this.valueField, v);
16626 text = r.data[this.displayField];
16627 }else if(this.valueNotFoundText !== undefined){
16628 text = this.valueNotFoundText;
16631 this.lastSelectionText = text;
16632 if(this.hiddenField){
16633 this.hiddenField.dom.value = v;
16635 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16638 var close = this.closeTriggerEl();
16641 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16647 * @property {Object} the last set data for the element
16652 * Sets the value of the field based on a object which is related to the record format for the store.
16653 * @param {Object} value the value to set as. or false on reset?
16655 setFromData : function(o){
16662 var dv = ''; // display value
16663 var vv = ''; // value value..
16665 if (this.displayField) {
16666 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16668 // this is an error condition!!!
16669 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16672 if(this.valueField){
16673 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16676 var close = this.closeTriggerEl();
16679 if(dv.length || vv * 1 > 0){
16681 this.blockFocus=true;
16687 if(this.hiddenField){
16688 this.hiddenField.dom.value = vv;
16690 this.lastSelectionText = dv;
16691 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16695 // no hidden field.. - we store the value in 'value', but still display
16696 // display field!!!!
16697 this.lastSelectionText = dv;
16698 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16705 reset : function(){
16706 // overridden so that last data is reset..
16713 this.setValue(this.originalValue);
16714 //this.clearInvalid();
16715 this.lastData = false;
16717 this.view.clearSelections();
16723 findRecord : function(prop, value){
16725 if(this.store.getCount() > 0){
16726 this.store.each(function(r){
16727 if(r.data[prop] == value){
16737 getName: function()
16739 // returns hidden if it's set..
16740 if (!this.rendered) {return ''};
16741 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
16745 onViewMove : function(e, t){
16746 this.inKeyMode = false;
16750 onViewOver : function(e, t){
16751 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16754 var item = this.view.findItemFromChild(t);
16757 var index = this.view.indexOf(item);
16758 this.select(index, false);
16763 onViewClick : function(view, doFocus, el, e)
16765 var index = this.view.getSelectedIndexes()[0];
16767 var r = this.store.getAt(index);
16771 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16778 Roo.each(this.tickItems, function(v,k){
16780 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16782 _this.tickItems.splice(k, 1);
16784 if(typeof(e) == 'undefined' && view == false){
16785 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16797 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16798 this.tickItems.push(r.data);
16801 if(typeof(e) == 'undefined' && view == false){
16802 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16809 this.onSelect(r, index);
16811 if(doFocus !== false && !this.blockFocus){
16812 this.inputEl().focus();
16817 restrictHeight : function(){
16818 //this.innerList.dom.style.height = '';
16819 //var inner = this.innerList.dom;
16820 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16821 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16822 //this.list.beginUpdate();
16823 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16824 this.list.alignTo(this.inputEl(), this.listAlign);
16825 this.list.alignTo(this.inputEl(), this.listAlign);
16826 //this.list.endUpdate();
16830 onEmptyResults : function(){
16832 if(this.tickable && this.editable){
16833 this.hasFocus = false;
16834 this.restrictHeight();
16842 * Returns true if the dropdown list is expanded, else false.
16844 isExpanded : function(){
16845 return this.list.isVisible();
16849 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16850 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16851 * @param {String} value The data value of the item to select
16852 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16853 * selected item if it is not currently in view (defaults to true)
16854 * @return {Boolean} True if the value matched an item in the list, else false
16856 selectByValue : function(v, scrollIntoView){
16857 if(v !== undefined && v !== null){
16858 var r = this.findRecord(this.valueField || this.displayField, v);
16860 this.select(this.store.indexOf(r), scrollIntoView);
16868 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16869 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16870 * @param {Number} index The zero-based index of the list item to select
16871 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16872 * selected item if it is not currently in view (defaults to true)
16874 select : function(index, scrollIntoView){
16875 this.selectedIndex = index;
16876 this.view.select(index);
16877 if(scrollIntoView !== false){
16878 var el = this.view.getNode(index);
16880 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16883 this.list.scrollChildIntoView(el, false);
16889 selectNext : function(){
16890 var ct = this.store.getCount();
16892 if(this.selectedIndex == -1){
16894 }else if(this.selectedIndex < ct-1){
16895 this.select(this.selectedIndex+1);
16901 selectPrev : function(){
16902 var ct = this.store.getCount();
16904 if(this.selectedIndex == -1){
16906 }else if(this.selectedIndex != 0){
16907 this.select(this.selectedIndex-1);
16913 onKeyUp : function(e){
16914 if(this.editable !== false && !e.isSpecialKey()){
16915 this.lastKey = e.getKey();
16916 this.dqTask.delay(this.queryDelay);
16921 validateBlur : function(){
16922 return !this.list || !this.list.isVisible();
16926 initQuery : function(){
16928 var v = this.getRawValue();
16930 if(this.tickable && this.editable){
16931 v = this.tickableInputEl().getValue();
16938 doForce : function(){
16939 if(this.inputEl().dom.value.length > 0){
16940 this.inputEl().dom.value =
16941 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16947 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
16948 * query allowing the query action to be canceled if needed.
16949 * @param {String} query The SQL query to execute
16950 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16951 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
16952 * saved in the current store (defaults to false)
16954 doQuery : function(q, forceAll){
16956 if(q === undefined || q === null){
16961 forceAll: forceAll,
16965 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16970 forceAll = qe.forceAll;
16971 if(forceAll === true || (q.length >= this.minChars)){
16973 this.hasQuery = true;
16975 if(this.lastQuery != q || this.alwaysQuery){
16976 this.lastQuery = q;
16977 if(this.mode == 'local'){
16978 this.selectedIndex = -1;
16980 this.store.clearFilter();
16983 if(this.specialFilter){
16984 this.fireEvent('specialfilter', this);
16989 this.store.filter(this.displayField, q);
16992 this.store.fireEvent("datachanged", this.store);
16999 this.store.baseParams[this.queryParam] = q;
17001 var options = {params : this.getParams(q)};
17004 options.add = true;
17005 options.params.start = this.page * this.pageSize;
17008 this.store.load(options);
17011 * this code will make the page width larger, at the beginning, the list not align correctly,
17012 * we should expand the list on onLoad
17013 * so command out it
17018 this.selectedIndex = -1;
17023 this.loadNext = false;
17027 getParams : function(q){
17029 //p[this.queryParam] = q;
17033 p.limit = this.pageSize;
17039 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
17041 collapse : function(){
17042 if(!this.isExpanded()){
17048 this.hasFocus = false;
17052 this.cancelBtn.hide();
17053 this.trigger.show();
17056 this.tickableInputEl().dom.value = '';
17057 this.tickableInputEl().blur();
17062 Roo.get(document).un('mousedown', this.collapseIf, this);
17063 Roo.get(document).un('mousewheel', this.collapseIf, this);
17064 if (!this.editable) {
17065 Roo.get(document).un('keydown', this.listKeyPress, this);
17067 this.fireEvent('collapse', this);
17073 collapseIf : function(e){
17074 var in_combo = e.within(this.el);
17075 var in_list = e.within(this.list);
17076 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
17078 if (in_combo || in_list || is_list) {
17079 //e.stopPropagation();
17084 this.onTickableFooterButtonClick(e, false, false);
17092 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
17094 expand : function(){
17096 if(this.isExpanded() || !this.hasFocus){
17100 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
17101 this.list.setWidth(lw);
17107 this.restrictHeight();
17111 this.tickItems = Roo.apply([], this.item);
17114 this.cancelBtn.show();
17115 this.trigger.hide();
17118 this.tickableInputEl().focus();
17123 Roo.get(document).on('mousedown', this.collapseIf, this);
17124 Roo.get(document).on('mousewheel', this.collapseIf, this);
17125 if (!this.editable) {
17126 Roo.get(document).on('keydown', this.listKeyPress, this);
17129 this.fireEvent('expand', this);
17133 // Implements the default empty TriggerField.onTriggerClick function
17134 onTriggerClick : function(e)
17136 Roo.log('trigger click');
17138 if(this.disabled || !this.triggerList){
17143 this.loadNext = false;
17145 if(this.isExpanded()){
17147 if (!this.blockFocus) {
17148 this.inputEl().focus();
17152 this.hasFocus = true;
17153 if(this.triggerAction == 'all') {
17154 this.doQuery(this.allQuery, true);
17156 this.doQuery(this.getRawValue());
17158 if (!this.blockFocus) {
17159 this.inputEl().focus();
17164 onTickableTriggerClick : function(e)
17171 this.loadNext = false;
17172 this.hasFocus = true;
17174 if(this.triggerAction == 'all') {
17175 this.doQuery(this.allQuery, true);
17177 this.doQuery(this.getRawValue());
17181 onSearchFieldClick : function(e)
17183 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
17184 this.onTickableFooterButtonClick(e, false, false);
17188 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
17193 this.loadNext = false;
17194 this.hasFocus = true;
17196 if(this.triggerAction == 'all') {
17197 this.doQuery(this.allQuery, true);
17199 this.doQuery(this.getRawValue());
17203 listKeyPress : function(e)
17205 //Roo.log('listkeypress');
17206 // scroll to first matching element based on key pres..
17207 if (e.isSpecialKey()) {
17210 var k = String.fromCharCode(e.getKey()).toUpperCase();
17213 var csel = this.view.getSelectedNodes();
17214 var cselitem = false;
17216 var ix = this.view.indexOf(csel[0]);
17217 cselitem = this.store.getAt(ix);
17218 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
17224 this.store.each(function(v) {
17226 // start at existing selection.
17227 if (cselitem.id == v.id) {
17233 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
17234 match = this.store.indexOf(v);
17240 if (match === false) {
17241 return true; // no more action?
17244 this.view.select(match);
17245 var sn = Roo.get(this.view.getSelectedNodes()[0]);
17246 sn.scrollIntoView(sn.dom.parentNode, false);
17249 onViewScroll : function(e, t){
17251 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){
17255 this.hasQuery = true;
17257 this.loading = this.list.select('.loading', true).first();
17259 if(this.loading === null){
17260 this.list.createChild({
17262 cls: 'loading roo-select2-more-results roo-select2-active',
17263 html: 'Loading more results...'
17266 this.loading = this.list.select('.loading', true).first();
17268 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
17270 this.loading.hide();
17273 this.loading.show();
17278 this.loadNext = true;
17280 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
17285 addItem : function(o)
17287 var dv = ''; // display value
17289 if (this.displayField) {
17290 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17292 // this is an error condition!!!
17293 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
17300 var choice = this.choices.createChild({
17302 cls: 'roo-select2-search-choice',
17311 cls: 'roo-select2-search-choice-close fa fa-times',
17316 }, this.searchField);
17318 var close = choice.select('a.roo-select2-search-choice-close', true).first();
17320 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17328 this.inputEl().dom.value = '';
17333 onRemoveItem : function(e, _self, o)
17335 e.preventDefault();
17337 this.lastItem = Roo.apply([], this.item);
17339 var index = this.item.indexOf(o.data) * 1;
17342 Roo.log('not this item?!');
17346 this.item.splice(index, 1);
17351 this.fireEvent('remove', this, e);
17357 syncValue : function()
17359 if(!this.item.length){
17366 Roo.each(this.item, function(i){
17367 if(_this.valueField){
17368 value.push(i[_this.valueField]);
17375 this.value = value.join(',');
17377 if(this.hiddenField){
17378 this.hiddenField.dom.value = this.value;
17381 this.store.fireEvent("datachanged", this.store);
17386 clearItem : function()
17388 if(!this.multiple){
17394 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17402 if(this.tickable && !Roo.isTouch){
17403 this.view.refresh();
17407 inputEl: function ()
17409 if(Roo.isIOS && this.useNativeIOS){
17410 return this.el.select('select.roo-ios-select', true).first();
17413 if(Roo.isTouch && this.mobileTouchView){
17414 return this.el.select('input.form-control',true).first();
17418 return this.searchField;
17421 return this.el.select('input.form-control',true).first();
17424 onTickableFooterButtonClick : function(e, btn, el)
17426 e.preventDefault();
17428 this.lastItem = Roo.apply([], this.item);
17430 if(btn && btn.name == 'cancel'){
17431 this.tickItems = Roo.apply([], this.item);
17440 Roo.each(this.tickItems, function(o){
17448 validate : function()
17450 if(this.getVisibilityEl().hasClass('hidden')){
17454 var v = this.getRawValue();
17457 v = this.getValue();
17460 if(this.disabled || this.allowBlank || v.length){
17465 this.markInvalid();
17469 tickableInputEl : function()
17471 if(!this.tickable || !this.editable){
17472 return this.inputEl();
17475 return this.inputEl().select('.roo-select2-search-field-input', true).first();
17479 getAutoCreateTouchView : function()
17484 cls: 'form-group' //input-group
17490 type : this.inputType,
17491 cls : 'form-control x-combo-noedit',
17492 autocomplete: 'new-password',
17493 placeholder : this.placeholder || '',
17498 input.name = this.name;
17502 input.cls += ' input-' + this.size;
17505 if (this.disabled) {
17506 input.disabled = true;
17510 cls : 'roo-combobox-wrap',
17517 inputblock.cls += ' input-group';
17519 inputblock.cn.unshift({
17521 cls : 'input-group-addon input-group-prepend input-group-text',
17526 if(this.removable && !this.multiple){
17527 inputblock.cls += ' roo-removable';
17529 inputblock.cn.push({
17532 cls : 'roo-combo-removable-btn close'
17536 if(this.hasFeedback && !this.allowBlank){
17538 inputblock.cls += ' has-feedback';
17540 inputblock.cn.push({
17542 cls: 'glyphicon form-control-feedback'
17549 inputblock.cls += (this.before) ? '' : ' input-group';
17551 inputblock.cn.push({
17553 cls : 'input-group-addon input-group-append input-group-text',
17559 var ibwrap = inputblock;
17564 cls: 'roo-select2-choices',
17568 cls: 'roo-select2-search-field',
17581 cls: 'roo-select2-container input-group roo-touchview-combobox ',
17586 cls: 'form-hidden-field'
17592 if(!this.multiple && this.showToggleBtn){
17598 if (this.caret != false) {
17601 cls: 'fa fa-' + this.caret
17608 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17610 Roo.bootstrap.version == 3 ? caret : '',
17613 cls: 'combobox-clear',
17627 combobox.cls += ' roo-select2-container-multi';
17630 var required = this.allowBlank ? {
17632 style: 'display: none'
17635 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17636 tooltip : 'This field is required'
17639 var align = this.labelAlign || this.parentLabelAlign();
17641 if (align ==='left' && this.fieldLabel.length) {
17647 cls : 'control-label col-form-label',
17648 html : this.fieldLabel
17652 cls : 'roo-combobox-wrap ',
17659 var labelCfg = cfg.cn[1];
17660 var contentCfg = cfg.cn[2];
17663 if(this.indicatorpos == 'right'){
17668 cls : 'control-label col-form-label',
17672 html : this.fieldLabel
17678 cls : "roo-combobox-wrap ",
17686 labelCfg = cfg.cn[0];
17687 contentCfg = cfg.cn[1];
17692 if(this.labelWidth > 12){
17693 labelCfg.style = "width: " + this.labelWidth + 'px';
17696 if(this.labelWidth < 13 && this.labelmd == 0){
17697 this.labelmd = this.labelWidth;
17700 if(this.labellg > 0){
17701 labelCfg.cls += ' col-lg-' + this.labellg;
17702 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17705 if(this.labelmd > 0){
17706 labelCfg.cls += ' col-md-' + this.labelmd;
17707 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17710 if(this.labelsm > 0){
17711 labelCfg.cls += ' col-sm-' + this.labelsm;
17712 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17715 if(this.labelxs > 0){
17716 labelCfg.cls += ' col-xs-' + this.labelxs;
17717 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17721 } else if ( this.fieldLabel.length) {
17726 cls : 'control-label',
17727 html : this.fieldLabel
17738 if(this.indicatorpos == 'right'){
17742 cls : 'control-label',
17743 html : this.fieldLabel,
17761 var settings = this;
17763 ['xs','sm','md','lg'].map(function(size){
17764 if (settings[size]) {
17765 cfg.cls += ' col-' + size + '-' + settings[size];
17772 initTouchView : function()
17774 this.renderTouchView();
17776 this.touchViewEl.on('scroll', function(){
17777 this.el.dom.scrollTop = 0;
17780 this.originalValue = this.getValue();
17782 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17784 this.inputEl().on("click", this.showTouchView, this);
17785 if (this.triggerEl) {
17786 this.triggerEl.on("click", this.showTouchView, this);
17790 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17791 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17793 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17795 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17796 this.store.on('load', this.onTouchViewLoad, this);
17797 this.store.on('loadexception', this.onTouchViewLoadException, this);
17799 if(this.hiddenName){
17801 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17803 this.hiddenField.dom.value =
17804 this.hiddenValue !== undefined ? this.hiddenValue :
17805 this.value !== undefined ? this.value : '';
17807 this.el.dom.removeAttribute('name');
17808 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17812 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17813 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17816 if(this.removable && !this.multiple){
17817 var close = this.closeTriggerEl();
17819 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17820 close.on('click', this.removeBtnClick, this, close);
17824 * fix the bug in Safari iOS8
17826 this.inputEl().on("focus", function(e){
17827 document.activeElement.blur();
17830 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17837 renderTouchView : function()
17839 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17840 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17842 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17843 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17845 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17846 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17847 this.touchViewBodyEl.setStyle('overflow', 'auto');
17849 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17850 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17852 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17853 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17857 showTouchView : function()
17863 this.touchViewHeaderEl.hide();
17865 if(this.modalTitle.length){
17866 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17867 this.touchViewHeaderEl.show();
17870 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17871 this.touchViewEl.show();
17873 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17875 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17876 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17878 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17880 if(this.modalTitle.length){
17881 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17884 this.touchViewBodyEl.setHeight(bodyHeight);
17888 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17890 this.touchViewEl.addClass(['in','show']);
17893 if(this._touchViewMask){
17894 Roo.get(document.body).addClass("x-body-masked");
17895 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17896 this._touchViewMask.setStyle('z-index', 10000);
17897 this._touchViewMask.addClass('show');
17900 this.doTouchViewQuery();
17904 hideTouchView : function()
17906 this.touchViewEl.removeClass(['in','show']);
17910 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17912 this.touchViewEl.setStyle('display', 'none');
17915 if(this._touchViewMask){
17916 this._touchViewMask.removeClass('show');
17917 Roo.get(document.body).removeClass("x-body-masked");
17921 setTouchViewValue : function()
17928 Roo.each(this.tickItems, function(o){
17933 this.hideTouchView();
17936 doTouchViewQuery : function()
17945 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17949 if(!this.alwaysQuery || this.mode == 'local'){
17950 this.onTouchViewLoad();
17957 onTouchViewBeforeLoad : function(combo,opts)
17963 onTouchViewLoad : function()
17965 if(this.store.getCount() < 1){
17966 this.onTouchViewEmptyResults();
17970 this.clearTouchView();
17972 var rawValue = this.getRawValue();
17974 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17976 this.tickItems = [];
17978 this.store.data.each(function(d, rowIndex){
17979 var row = this.touchViewListGroup.createChild(template);
17981 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17982 row.addClass(d.data.cls);
17985 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17988 html : d.data[this.displayField]
17991 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17992 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17995 row.removeClass('selected');
17996 if(!this.multiple && this.valueField &&
17997 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
18000 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18001 row.addClass('selected');
18004 if(this.multiple && this.valueField &&
18005 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
18009 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18010 this.tickItems.push(d.data);
18013 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
18017 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
18019 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18021 if(this.modalTitle.length){
18022 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18025 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
18027 if(this.mobile_restrict_height && listHeight < bodyHeight){
18028 this.touchViewBodyEl.setHeight(listHeight);
18033 if(firstChecked && listHeight > bodyHeight){
18034 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
18039 onTouchViewLoadException : function()
18041 this.hideTouchView();
18044 onTouchViewEmptyResults : function()
18046 this.clearTouchView();
18048 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
18050 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
18054 clearTouchView : function()
18056 this.touchViewListGroup.dom.innerHTML = '';
18059 onTouchViewClick : function(e, el, o)
18061 e.preventDefault();
18064 var rowIndex = o.rowIndex;
18066 var r = this.store.getAt(rowIndex);
18068 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
18070 if(!this.multiple){
18071 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
18072 c.dom.removeAttribute('checked');
18075 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18077 this.setFromData(r.data);
18079 var close = this.closeTriggerEl();
18085 this.hideTouchView();
18087 this.fireEvent('select', this, r, rowIndex);
18092 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
18093 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
18094 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
18098 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18099 this.addItem(r.data);
18100 this.tickItems.push(r.data);
18104 getAutoCreateNativeIOS : function()
18107 cls: 'form-group' //input-group,
18112 cls : 'roo-ios-select'
18116 combobox.name = this.name;
18119 if (this.disabled) {
18120 combobox.disabled = true;
18123 var settings = this;
18125 ['xs','sm','md','lg'].map(function(size){
18126 if (settings[size]) {
18127 cfg.cls += ' col-' + size + '-' + settings[size];
18137 initIOSView : function()
18139 this.store.on('load', this.onIOSViewLoad, this);
18144 onIOSViewLoad : function()
18146 if(this.store.getCount() < 1){
18150 this.clearIOSView();
18152 if(this.allowBlank) {
18154 var default_text = '-- SELECT --';
18156 if(this.placeholder.length){
18157 default_text = this.placeholder;
18160 if(this.emptyTitle.length){
18161 default_text += ' - ' + this.emptyTitle + ' -';
18164 var opt = this.inputEl().createChild({
18167 html : default_text
18171 o[this.valueField] = 0;
18172 o[this.displayField] = default_text;
18174 this.ios_options.push({
18181 this.store.data.each(function(d, rowIndex){
18185 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
18186 html = d.data[this.displayField];
18191 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
18192 value = d.data[this.valueField];
18201 if(this.value == d.data[this.valueField]){
18202 option['selected'] = true;
18205 var opt = this.inputEl().createChild(option);
18207 this.ios_options.push({
18214 this.inputEl().on('change', function(){
18215 this.fireEvent('select', this);
18220 clearIOSView: function()
18222 this.inputEl().dom.innerHTML = '';
18224 this.ios_options = [];
18227 setIOSValue: function(v)
18231 if(!this.ios_options){
18235 Roo.each(this.ios_options, function(opts){
18237 opts.el.dom.removeAttribute('selected');
18239 if(opts.data[this.valueField] != v){
18243 opts.el.dom.setAttribute('selected', true);
18249 * @cfg {Boolean} grow
18253 * @cfg {Number} growMin
18257 * @cfg {Number} growMax
18266 Roo.apply(Roo.bootstrap.ComboBox, {
18270 cls: 'modal-header',
18292 cls: 'list-group-item',
18296 cls: 'roo-combobox-list-group-item-value'
18300 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
18314 listItemCheckbox : {
18316 cls: 'list-group-item',
18320 cls: 'roo-combobox-list-group-item-value'
18324 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18340 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18345 cls: 'modal-footer',
18353 cls: 'col-xs-6 text-left',
18356 cls: 'btn btn-danger roo-touch-view-cancel',
18362 cls: 'col-xs-6 text-right',
18365 cls: 'btn btn-success roo-touch-view-ok',
18376 Roo.apply(Roo.bootstrap.ComboBox, {
18378 touchViewTemplate : {
18380 cls: 'modal fade roo-combobox-touch-view',
18384 cls: 'modal-dialog',
18385 style : 'position:fixed', // we have to fix position....
18389 cls: 'modal-content',
18391 Roo.bootstrap.ComboBox.header,
18392 Roo.bootstrap.ComboBox.body,
18393 Roo.bootstrap.ComboBox.footer
18402 * Ext JS Library 1.1.1
18403 * Copyright(c) 2006-2007, Ext JS, LLC.
18405 * Originally Released Under LGPL - original licence link has changed is not relivant.
18408 * <script type="text/javascript">
18413 * @extends Roo.util.Observable
18414 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
18415 * This class also supports single and multi selection modes. <br>
18416 * Create a data model bound view:
18418 var store = new Roo.data.Store(...);
18420 var view = new Roo.View({
18422 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
18424 singleSelect: true,
18425 selectedClass: "ydataview-selected",
18429 // listen for node click?
18430 view.on("click", function(vw, index, node, e){
18431 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18435 dataModel.load("foobar.xml");
18437 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18439 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18440 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18442 * Note: old style constructor is still suported (container, template, config)
18445 * Create a new View
18446 * @param {Object} config The config object
18449 Roo.View = function(config, depreciated_tpl, depreciated_config){
18451 this.parent = false;
18453 if (typeof(depreciated_tpl) == 'undefined') {
18454 // new way.. - universal constructor.
18455 Roo.apply(this, config);
18456 this.el = Roo.get(this.el);
18459 this.el = Roo.get(config);
18460 this.tpl = depreciated_tpl;
18461 Roo.apply(this, depreciated_config);
18463 this.wrapEl = this.el.wrap().wrap();
18464 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18467 if(typeof(this.tpl) == "string"){
18468 this.tpl = new Roo.Template(this.tpl);
18470 // support xtype ctors..
18471 this.tpl = new Roo.factory(this.tpl, Roo);
18475 this.tpl.compile();
18480 * @event beforeclick
18481 * Fires before a click is processed. Returns false to cancel the default action.
18482 * @param {Roo.View} this
18483 * @param {Number} index The index of the target node
18484 * @param {HTMLElement} node The target node
18485 * @param {Roo.EventObject} e The raw event object
18487 "beforeclick" : true,
18490 * Fires when a template node is clicked.
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
18499 * Fires when a template node is double 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
18507 * @event contextmenu
18508 * Fires when a template node is right 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
18514 "contextmenu" : true,
18516 * @event selectionchange
18517 * Fires when the selected nodes change.
18518 * @param {Roo.View} this
18519 * @param {Array} selections Array of the selected nodes
18521 "selectionchange" : true,
18524 * @event beforeselect
18525 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18526 * @param {Roo.View} this
18527 * @param {HTMLElement} node The node to be selected
18528 * @param {Array} selections Array of currently selected nodes
18530 "beforeselect" : true,
18532 * @event preparedata
18533 * Fires on every row to render, to allow you to change the data.
18534 * @param {Roo.View} this
18535 * @param {Object} data to be rendered (change this)
18537 "preparedata" : true
18545 "click": this.onClick,
18546 "dblclick": this.onDblClick,
18547 "contextmenu": this.onContextMenu,
18551 this.selections = [];
18553 this.cmp = new Roo.CompositeElementLite([]);
18555 this.store = Roo.factory(this.store, Roo.data);
18556 this.setStore(this.store, true);
18559 if ( this.footer && this.footer.xtype) {
18561 var fctr = this.wrapEl.appendChild(document.createElement("div"));
18563 this.footer.dataSource = this.store;
18564 this.footer.container = fctr;
18565 this.footer = Roo.factory(this.footer, Roo);
18566 fctr.insertFirst(this.el);
18568 // this is a bit insane - as the paging toolbar seems to detach the el..
18569 // dom.parentNode.parentNode.parentNode
18570 // they get detached?
18574 Roo.View.superclass.constructor.call(this);
18579 Roo.extend(Roo.View, Roo.util.Observable, {
18582 * @cfg {Roo.data.Store} store Data store to load data from.
18587 * @cfg {String|Roo.Element} el The container element.
18592 * @cfg {String|Roo.Template} tpl The template used by this View
18596 * @cfg {String} dataName the named area of the template to use as the data area
18597 * Works with domtemplates roo-name="name"
18601 * @cfg {String} selectedClass The css class to add to selected nodes
18603 selectedClass : "x-view-selected",
18605 * @cfg {String} emptyText The empty text to show when nothing is loaded.
18610 * @cfg {String} text to display on mask (default Loading)
18614 * @cfg {Boolean} multiSelect Allow multiple selection
18616 multiSelect : false,
18618 * @cfg {Boolean} singleSelect Allow single selection
18620 singleSelect: false,
18623 * @cfg {Boolean} toggleSelect - selecting
18625 toggleSelect : false,
18628 * @cfg {Boolean} tickable - selecting
18633 * Returns the element this view is bound to.
18634 * @return {Roo.Element}
18636 getEl : function(){
18637 return this.wrapEl;
18643 * Refreshes the view. - called by datachanged on the store. - do not call directly.
18645 refresh : function(){
18646 //Roo.log('refresh');
18649 // if we are using something like 'domtemplate', then
18650 // the what gets used is:
18651 // t.applySubtemplate(NAME, data, wrapping data..)
18652 // the outer template then get' applied with
18653 // the store 'extra data'
18654 // and the body get's added to the
18655 // roo-name="data" node?
18656 // <span class='roo-tpl-{name}'></span> ?????
18660 this.clearSelections();
18661 this.el.update("");
18663 var records = this.store.getRange();
18664 if(records.length < 1) {
18666 // is this valid?? = should it render a template??
18668 this.el.update(this.emptyText);
18672 if (this.dataName) {
18673 this.el.update(t.apply(this.store.meta)); //????
18674 el = this.el.child('.roo-tpl-' + this.dataName);
18677 for(var i = 0, len = records.length; i < len; i++){
18678 var data = this.prepareData(records[i].data, i, records[i]);
18679 this.fireEvent("preparedata", this, data, i, records[i]);
18681 var d = Roo.apply({}, data);
18684 Roo.apply(d, {'roo-id' : Roo.id()});
18688 Roo.each(this.parent.item, function(item){
18689 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18692 Roo.apply(d, {'roo-data-checked' : 'checked'});
18696 html[html.length] = Roo.util.Format.trim(
18698 t.applySubtemplate(this.dataName, d, this.store.meta) :
18705 el.update(html.join(""));
18706 this.nodes = el.dom.childNodes;
18707 this.updateIndexes(0);
18712 * Function to override to reformat the data that is sent to
18713 * the template for each node.
18714 * DEPRICATED - use the preparedata event handler.
18715 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18716 * a JSON object for an UpdateManager bound view).
18718 prepareData : function(data, index, record)
18720 this.fireEvent("preparedata", this, data, index, record);
18724 onUpdate : function(ds, record){
18725 // Roo.log('on update');
18726 this.clearSelections();
18727 var index = this.store.indexOf(record);
18728 var n = this.nodes[index];
18729 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18730 n.parentNode.removeChild(n);
18731 this.updateIndexes(index, index);
18737 onAdd : function(ds, records, index)
18739 //Roo.log(['on Add', ds, records, index] );
18740 this.clearSelections();
18741 if(this.nodes.length == 0){
18745 var n = this.nodes[index];
18746 for(var i = 0, len = records.length; i < len; i++){
18747 var d = this.prepareData(records[i].data, i, records[i]);
18749 this.tpl.insertBefore(n, d);
18752 this.tpl.append(this.el, d);
18755 this.updateIndexes(index);
18758 onRemove : function(ds, record, index){
18759 // Roo.log('onRemove');
18760 this.clearSelections();
18761 var el = this.dataName ?
18762 this.el.child('.roo-tpl-' + this.dataName) :
18765 el.dom.removeChild(this.nodes[index]);
18766 this.updateIndexes(index);
18770 * Refresh an individual node.
18771 * @param {Number} index
18773 refreshNode : function(index){
18774 this.onUpdate(this.store, this.store.getAt(index));
18777 updateIndexes : function(startIndex, endIndex){
18778 var ns = this.nodes;
18779 startIndex = startIndex || 0;
18780 endIndex = endIndex || ns.length - 1;
18781 for(var i = startIndex; i <= endIndex; i++){
18782 ns[i].nodeIndex = i;
18787 * Changes the data store this view uses and refresh the view.
18788 * @param {Store} store
18790 setStore : function(store, initial){
18791 if(!initial && this.store){
18792 this.store.un("datachanged", this.refresh);
18793 this.store.un("add", this.onAdd);
18794 this.store.un("remove", this.onRemove);
18795 this.store.un("update", this.onUpdate);
18796 this.store.un("clear", this.refresh);
18797 this.store.un("beforeload", this.onBeforeLoad);
18798 this.store.un("load", this.onLoad);
18799 this.store.un("loadexception", this.onLoad);
18803 store.on("datachanged", this.refresh, this);
18804 store.on("add", this.onAdd, this);
18805 store.on("remove", this.onRemove, this);
18806 store.on("update", this.onUpdate, this);
18807 store.on("clear", this.refresh, this);
18808 store.on("beforeload", this.onBeforeLoad, this);
18809 store.on("load", this.onLoad, this);
18810 store.on("loadexception", this.onLoad, this);
18818 * onbeforeLoad - masks the loading area.
18821 onBeforeLoad : function(store,opts)
18823 //Roo.log('onBeforeLoad');
18825 this.el.update("");
18827 this.el.mask(this.mask ? this.mask : "Loading" );
18829 onLoad : function ()
18836 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18837 * @param {HTMLElement} node
18838 * @return {HTMLElement} The template node
18840 findItemFromChild : function(node){
18841 var el = this.dataName ?
18842 this.el.child('.roo-tpl-' + this.dataName,true) :
18845 if(!node || node.parentNode == el){
18848 var p = node.parentNode;
18849 while(p && p != el){
18850 if(p.parentNode == el){
18859 onClick : function(e){
18860 var item = this.findItemFromChild(e.getTarget());
18862 var index = this.indexOf(item);
18863 if(this.onItemClick(item, index, e) !== false){
18864 this.fireEvent("click", this, index, item, e);
18867 this.clearSelections();
18872 onContextMenu : function(e){
18873 var item = this.findItemFromChild(e.getTarget());
18875 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18880 onDblClick : function(e){
18881 var item = this.findItemFromChild(e.getTarget());
18883 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18887 onItemClick : function(item, index, e)
18889 if(this.fireEvent("beforeclick", this, index, item, e) === false){
18892 if (this.toggleSelect) {
18893 var m = this.isSelected(item) ? 'unselect' : 'select';
18896 _t[m](item, true, false);
18899 if(this.multiSelect || this.singleSelect){
18900 if(this.multiSelect && e.shiftKey && this.lastSelection){
18901 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18903 this.select(item, this.multiSelect && e.ctrlKey);
18904 this.lastSelection = item;
18907 if(!this.tickable){
18908 e.preventDefault();
18916 * Get the number of selected nodes.
18919 getSelectionCount : function(){
18920 return this.selections.length;
18924 * Get the currently selected nodes.
18925 * @return {Array} An array of HTMLElements
18927 getSelectedNodes : function(){
18928 return this.selections;
18932 * Get the indexes of the selected nodes.
18935 getSelectedIndexes : function(){
18936 var indexes = [], s = this.selections;
18937 for(var i = 0, len = s.length; i < len; i++){
18938 indexes.push(s[i].nodeIndex);
18944 * Clear all selections
18945 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18947 clearSelections : function(suppressEvent){
18948 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18949 this.cmp.elements = this.selections;
18950 this.cmp.removeClass(this.selectedClass);
18951 this.selections = [];
18952 if(!suppressEvent){
18953 this.fireEvent("selectionchange", this, this.selections);
18959 * Returns true if the passed node is selected
18960 * @param {HTMLElement/Number} node The node or node index
18961 * @return {Boolean}
18963 isSelected : function(node){
18964 var s = this.selections;
18968 node = this.getNode(node);
18969 return s.indexOf(node) !== -1;
18974 * @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
18975 * @param {Boolean} keepExisting (optional) true to keep existing selections
18976 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18978 select : function(nodeInfo, keepExisting, suppressEvent){
18979 if(nodeInfo instanceof Array){
18981 this.clearSelections(true);
18983 for(var i = 0, len = nodeInfo.length; i < len; i++){
18984 this.select(nodeInfo[i], true, true);
18988 var node = this.getNode(nodeInfo);
18989 if(!node || this.isSelected(node)){
18990 return; // already selected.
18993 this.clearSelections(true);
18996 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18997 Roo.fly(node).addClass(this.selectedClass);
18998 this.selections.push(node);
18999 if(!suppressEvent){
19000 this.fireEvent("selectionchange", this, this.selections);
19008 * @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
19009 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
19010 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
19012 unselect : function(nodeInfo, keepExisting, suppressEvent)
19014 if(nodeInfo instanceof Array){
19015 Roo.each(this.selections, function(s) {
19016 this.unselect(s, nodeInfo);
19020 var node = this.getNode(nodeInfo);
19021 if(!node || !this.isSelected(node)){
19022 //Roo.log("not selected");
19023 return; // not selected.
19027 Roo.each(this.selections, function(s) {
19029 Roo.fly(node).removeClass(this.selectedClass);
19036 this.selections= ns;
19037 this.fireEvent("selectionchange", this, this.selections);
19041 * Gets a template node.
19042 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19043 * @return {HTMLElement} The node or null if it wasn't found
19045 getNode : function(nodeInfo){
19046 if(typeof nodeInfo == "string"){
19047 return document.getElementById(nodeInfo);
19048 }else if(typeof nodeInfo == "number"){
19049 return this.nodes[nodeInfo];
19055 * Gets a range template nodes.
19056 * @param {Number} startIndex
19057 * @param {Number} endIndex
19058 * @return {Array} An array of nodes
19060 getNodes : function(start, end){
19061 var ns = this.nodes;
19062 start = start || 0;
19063 end = typeof end == "undefined" ? ns.length - 1 : end;
19066 for(var i = start; i <= end; i++){
19070 for(var i = start; i >= end; i--){
19078 * Finds the index of the passed node
19079 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19080 * @return {Number} The index of the node or -1
19082 indexOf : function(node){
19083 node = this.getNode(node);
19084 if(typeof node.nodeIndex == "number"){
19085 return node.nodeIndex;
19087 var ns = this.nodes;
19088 for(var i = 0, len = ns.length; i < len; i++){
19099 * based on jquery fullcalendar
19103 Roo.bootstrap = Roo.bootstrap || {};
19105 * @class Roo.bootstrap.Calendar
19106 * @extends Roo.bootstrap.Component
19107 * Bootstrap Calendar class
19108 * @cfg {Boolean} loadMask (true|false) default false
19109 * @cfg {Object} header generate the user specific header of the calendar, default false
19112 * Create a new Container
19113 * @param {Object} config The config object
19118 Roo.bootstrap.Calendar = function(config){
19119 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
19123 * Fires when a date is selected
19124 * @param {DatePicker} this
19125 * @param {Date} date The selected date
19129 * @event monthchange
19130 * Fires when the displayed month changes
19131 * @param {DatePicker} this
19132 * @param {Date} date The selected month
19134 'monthchange': true,
19136 * @event evententer
19137 * Fires when mouse over an event
19138 * @param {Calendar} this
19139 * @param {event} Event
19141 'evententer': true,
19143 * @event eventleave
19144 * Fires when the mouse leaves an
19145 * @param {Calendar} this
19148 'eventleave': true,
19150 * @event eventclick
19151 * Fires when the mouse click an
19152 * @param {Calendar} this
19161 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
19164 * @cfg {Number} startDay
19165 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
19173 getAutoCreate : function(){
19176 var fc_button = function(name, corner, style, content ) {
19177 return Roo.apply({},{
19179 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
19181 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
19184 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
19195 style : 'width:100%',
19202 cls : 'fc-header-left',
19204 fc_button('prev', 'left', 'arrow', '‹' ),
19205 fc_button('next', 'right', 'arrow', '›' ),
19206 { tag: 'span', cls: 'fc-header-space' },
19207 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
19215 cls : 'fc-header-center',
19219 cls: 'fc-header-title',
19222 html : 'month / year'
19230 cls : 'fc-header-right',
19232 /* fc_button('month', 'left', '', 'month' ),
19233 fc_button('week', '', '', 'week' ),
19234 fc_button('day', 'right', '', 'day' )
19246 header = this.header;
19249 var cal_heads = function() {
19251 // fixme - handle this.
19253 for (var i =0; i < Date.dayNames.length; i++) {
19254 var d = Date.dayNames[i];
19257 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
19258 html : d.substring(0,3)
19262 ret[0].cls += ' fc-first';
19263 ret[6].cls += ' fc-last';
19266 var cal_cell = function(n) {
19269 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
19274 cls: 'fc-day-number',
19278 cls: 'fc-day-content',
19282 style: 'position: relative;' // height: 17px;
19294 var cal_rows = function() {
19297 for (var r = 0; r < 6; r++) {
19304 for (var i =0; i < Date.dayNames.length; i++) {
19305 var d = Date.dayNames[i];
19306 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
19309 row.cn[0].cls+=' fc-first';
19310 row.cn[0].cn[0].style = 'min-height:90px';
19311 row.cn[6].cls+=' fc-last';
19315 ret[0].cls += ' fc-first';
19316 ret[4].cls += ' fc-prev-last';
19317 ret[5].cls += ' fc-last';
19324 cls: 'fc-border-separate',
19325 style : 'width:100%',
19333 cls : 'fc-first fc-last',
19351 cls : 'fc-content',
19352 style : "position: relative;",
19355 cls : 'fc-view fc-view-month fc-grid',
19356 style : 'position: relative',
19357 unselectable : 'on',
19360 cls : 'fc-event-container',
19361 style : 'position:absolute;z-index:8;top:0;left:0;'
19379 initEvents : function()
19382 throw "can not find store for calendar";
19388 style: "text-align:center",
19392 style: "background-color:white;width:50%;margin:250 auto",
19396 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
19407 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19409 var size = this.el.select('.fc-content', true).first().getSize();
19410 this.maskEl.setSize(size.width, size.height);
19411 this.maskEl.enableDisplayMode("block");
19412 if(!this.loadMask){
19413 this.maskEl.hide();
19416 this.store = Roo.factory(this.store, Roo.data);
19417 this.store.on('load', this.onLoad, this);
19418 this.store.on('beforeload', this.onBeforeLoad, this);
19422 this.cells = this.el.select('.fc-day',true);
19423 //Roo.log(this.cells);
19424 this.textNodes = this.el.query('.fc-day-number');
19425 this.cells.addClassOnOver('fc-state-hover');
19427 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19428 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19429 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19430 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19432 this.on('monthchange', this.onMonthChange, this);
19434 this.update(new Date().clearTime());
19437 resize : function() {
19438 var sz = this.el.getSize();
19440 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19441 this.el.select('.fc-day-content div',true).setHeight(34);
19446 showPrevMonth : function(e){
19447 this.update(this.activeDate.add("mo", -1));
19449 showToday : function(e){
19450 this.update(new Date().clearTime());
19453 showNextMonth : function(e){
19454 this.update(this.activeDate.add("mo", 1));
19458 showPrevYear : function(){
19459 this.update(this.activeDate.add("y", -1));
19463 showNextYear : function(){
19464 this.update(this.activeDate.add("y", 1));
19469 update : function(date)
19471 var vd = this.activeDate;
19472 this.activeDate = date;
19473 // if(vd && this.el){
19474 // var t = date.getTime();
19475 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19476 // Roo.log('using add remove');
19478 // this.fireEvent('monthchange', this, date);
19480 // this.cells.removeClass("fc-state-highlight");
19481 // this.cells.each(function(c){
19482 // if(c.dateValue == t){
19483 // c.addClass("fc-state-highlight");
19484 // setTimeout(function(){
19485 // try{c.dom.firstChild.focus();}catch(e){}
19495 var days = date.getDaysInMonth();
19497 var firstOfMonth = date.getFirstDateOfMonth();
19498 var startingPos = firstOfMonth.getDay()-this.startDay;
19500 if(startingPos < this.startDay){
19504 var pm = date.add(Date.MONTH, -1);
19505 var prevStart = pm.getDaysInMonth()-startingPos;
19507 this.cells = this.el.select('.fc-day',true);
19508 this.textNodes = this.el.query('.fc-day-number');
19509 this.cells.addClassOnOver('fc-state-hover');
19511 var cells = this.cells.elements;
19512 var textEls = this.textNodes;
19514 Roo.each(cells, function(cell){
19515 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19518 days += startingPos;
19520 // convert everything to numbers so it's fast
19521 var day = 86400000;
19522 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19525 //Roo.log(prevStart);
19527 var today = new Date().clearTime().getTime();
19528 var sel = date.clearTime().getTime();
19529 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19530 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19531 var ddMatch = this.disabledDatesRE;
19532 var ddText = this.disabledDatesText;
19533 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19534 var ddaysText = this.disabledDaysText;
19535 var format = this.format;
19537 var setCellClass = function(cal, cell){
19541 //Roo.log('set Cell Class');
19543 var t = d.getTime();
19547 cell.dateValue = t;
19549 cell.className += " fc-today";
19550 cell.className += " fc-state-highlight";
19551 cell.title = cal.todayText;
19554 // disable highlight in other month..
19555 //cell.className += " fc-state-highlight";
19560 cell.className = " fc-state-disabled";
19561 cell.title = cal.minText;
19565 cell.className = " fc-state-disabled";
19566 cell.title = cal.maxText;
19570 if(ddays.indexOf(d.getDay()) != -1){
19571 cell.title = ddaysText;
19572 cell.className = " fc-state-disabled";
19575 if(ddMatch && format){
19576 var fvalue = d.dateFormat(format);
19577 if(ddMatch.test(fvalue)){
19578 cell.title = ddText.replace("%0", fvalue);
19579 cell.className = " fc-state-disabled";
19583 if (!cell.initialClassName) {
19584 cell.initialClassName = cell.dom.className;
19587 cell.dom.className = cell.initialClassName + ' ' + cell.className;
19592 for(; i < startingPos; i++) {
19593 textEls[i].innerHTML = (++prevStart);
19594 d.setDate(d.getDate()+1);
19596 cells[i].className = "fc-past fc-other-month";
19597 setCellClass(this, cells[i]);
19602 for(; i < days; i++){
19603 intDay = i - startingPos + 1;
19604 textEls[i].innerHTML = (intDay);
19605 d.setDate(d.getDate()+1);
19607 cells[i].className = ''; // "x-date-active";
19608 setCellClass(this, cells[i]);
19612 for(; i < 42; i++) {
19613 textEls[i].innerHTML = (++extraDays);
19614 d.setDate(d.getDate()+1);
19616 cells[i].className = "fc-future fc-other-month";
19617 setCellClass(this, cells[i]);
19620 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19622 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19624 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19625 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19627 if(totalRows != 6){
19628 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19629 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19632 this.fireEvent('monthchange', this, date);
19636 if(!this.internalRender){
19637 var main = this.el.dom.firstChild;
19638 var w = main.offsetWidth;
19639 this.el.setWidth(w + this.el.getBorderWidth("lr"));
19640 Roo.fly(main).setWidth(w);
19641 this.internalRender = true;
19642 // opera does not respect the auto grow header center column
19643 // then, after it gets a width opera refuses to recalculate
19644 // without a second pass
19645 if(Roo.isOpera && !this.secondPass){
19646 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19647 this.secondPass = true;
19648 this.update.defer(10, this, [date]);
19655 findCell : function(dt) {
19656 dt = dt.clearTime().getTime();
19658 this.cells.each(function(c){
19659 //Roo.log("check " +c.dateValue + '?=' + dt);
19660 if(c.dateValue == dt){
19670 findCells : function(ev) {
19671 var s = ev.start.clone().clearTime().getTime();
19673 var e= ev.end.clone().clearTime().getTime();
19676 this.cells.each(function(c){
19677 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19679 if(c.dateValue > e){
19682 if(c.dateValue < s){
19691 // findBestRow: function(cells)
19695 // for (var i =0 ; i < cells.length;i++) {
19696 // ret = Math.max(cells[i].rows || 0,ret);
19703 addItem : function(ev)
19705 // look for vertical location slot in
19706 var cells = this.findCells(ev);
19708 // ev.row = this.findBestRow(cells);
19710 // work out the location.
19714 for(var i =0; i < cells.length; i++) {
19716 cells[i].row = cells[0].row;
19719 cells[i].row = cells[i].row + 1;
19729 if (crow.start.getY() == cells[i].getY()) {
19731 crow.end = cells[i];
19748 cells[0].events.push(ev);
19750 this.calevents.push(ev);
19753 clearEvents: function() {
19755 if(!this.calevents){
19759 Roo.each(this.cells.elements, function(c){
19765 Roo.each(this.calevents, function(e) {
19766 Roo.each(e.els, function(el) {
19767 el.un('mouseenter' ,this.onEventEnter, this);
19768 el.un('mouseleave' ,this.onEventLeave, this);
19773 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19779 renderEvents: function()
19783 this.cells.each(function(c) {
19792 if(c.row != c.events.length){
19793 r = 4 - (4 - (c.row - c.events.length));
19796 c.events = ev.slice(0, r);
19797 c.more = ev.slice(r);
19799 if(c.more.length && c.more.length == 1){
19800 c.events.push(c.more.pop());
19803 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19807 this.cells.each(function(c) {
19809 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19812 for (var e = 0; e < c.events.length; e++){
19813 var ev = c.events[e];
19814 var rows = ev.rows;
19816 for(var i = 0; i < rows.length; i++) {
19818 // how many rows should it span..
19821 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19822 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19824 unselectable : "on",
19827 cls: 'fc-event-inner',
19831 // cls: 'fc-event-time',
19832 // html : cells.length > 1 ? '' : ev.time
19836 cls: 'fc-event-title',
19837 html : String.format('{0}', ev.title)
19844 cls: 'ui-resizable-handle ui-resizable-e',
19845 html : '  '
19852 cfg.cls += ' fc-event-start';
19854 if ((i+1) == rows.length) {
19855 cfg.cls += ' fc-event-end';
19858 var ctr = _this.el.select('.fc-event-container',true).first();
19859 var cg = ctr.createChild(cfg);
19861 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19862 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19864 var r = (c.more.length) ? 1 : 0;
19865 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
19866 cg.setWidth(ebox.right - sbox.x -2);
19868 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19869 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19870 cg.on('click', _this.onEventClick, _this, ev);
19881 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19882 style : 'position: absolute',
19883 unselectable : "on",
19886 cls: 'fc-event-inner',
19890 cls: 'fc-event-title',
19898 cls: 'ui-resizable-handle ui-resizable-e',
19899 html : '  '
19905 var ctr = _this.el.select('.fc-event-container',true).first();
19906 var cg = ctr.createChild(cfg);
19908 var sbox = c.select('.fc-day-content',true).first().getBox();
19909 var ebox = c.select('.fc-day-content',true).first().getBox();
19911 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
19912 cg.setWidth(ebox.right - sbox.x -2);
19914 cg.on('click', _this.onMoreEventClick, _this, c.more);
19924 onEventEnter: function (e, el,event,d) {
19925 this.fireEvent('evententer', this, el, event);
19928 onEventLeave: function (e, el,event,d) {
19929 this.fireEvent('eventleave', this, el, event);
19932 onEventClick: function (e, el,event,d) {
19933 this.fireEvent('eventclick', this, el, event);
19936 onMonthChange: function () {
19940 onMoreEventClick: function(e, el, more)
19944 this.calpopover.placement = 'right';
19945 this.calpopover.setTitle('More');
19947 this.calpopover.setContent('');
19949 var ctr = this.calpopover.el.select('.popover-content', true).first();
19951 Roo.each(more, function(m){
19953 cls : 'fc-event-hori fc-event-draggable',
19956 var cg = ctr.createChild(cfg);
19958 cg.on('click', _this.onEventClick, _this, m);
19961 this.calpopover.show(el);
19966 onLoad: function ()
19968 this.calevents = [];
19971 if(this.store.getCount() > 0){
19972 this.store.data.each(function(d){
19975 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19976 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19977 time : d.data.start_time,
19978 title : d.data.title,
19979 description : d.data.description,
19980 venue : d.data.venue
19985 this.renderEvents();
19987 if(this.calevents.length && this.loadMask){
19988 this.maskEl.hide();
19992 onBeforeLoad: function()
19994 this.clearEvents();
19996 this.maskEl.show();
20010 * @class Roo.bootstrap.Popover
20011 * @extends Roo.bootstrap.Component
20012 * Bootstrap Popover class
20013 * @cfg {String} html contents of the popover (or false to use children..)
20014 * @cfg {String} title of popover (or false to hide)
20015 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
20016 * @cfg {String} trigger click || hover (or false to trigger manually)
20017 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
20018 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
20019 * - if false and it has a 'parent' then it will be automatically added to that element
20020 * - if string - Roo.get will be called
20021 * @cfg {Number} delay - delay before showing
20024 * Create a new Popover
20025 * @param {Object} config The config object
20028 Roo.bootstrap.Popover = function(config){
20029 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
20035 * After the popover show
20037 * @param {Roo.bootstrap.Popover} this
20042 * After the popover hide
20044 * @param {Roo.bootstrap.Popover} this
20050 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
20055 placement : 'right',
20056 trigger : 'hover', // hover
20062 can_build_overlaid : false,
20064 maskEl : false, // the mask element
20067 alignEl : false, // when show is called with an element - this get's stored.
20069 getChildContainer : function()
20071 return this.contentEl;
20074 getPopoverHeader : function()
20076 this.title = true; // flag not to hide it..
20077 this.headerEl.addClass('p-0');
20078 return this.headerEl
20082 getAutoCreate : function(){
20085 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
20086 style: 'display:block',
20092 cls : 'popover-inner ',
20096 cls: 'popover-title popover-header',
20097 html : this.title === false ? '' : this.title
20100 cls : 'popover-content popover-body ' + (this.cls || ''),
20101 html : this.html || ''
20112 * @param {string} the title
20114 setTitle: function(str)
20118 this.headerEl.dom.innerHTML = str;
20123 * @param {string} the body content
20125 setContent: function(str)
20128 if (this.contentEl) {
20129 this.contentEl.dom.innerHTML = str;
20133 // as it get's added to the bottom of the page.
20134 onRender : function(ct, position)
20136 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
20141 var cfg = Roo.apply({}, this.getAutoCreate());
20145 cfg.cls += ' ' + this.cls;
20148 cfg.style = this.style;
20150 //Roo.log("adding to ");
20151 this.el = Roo.get(document.body).createChild(cfg, position);
20152 // Roo.log(this.el);
20155 this.contentEl = this.el.select('.popover-content',true).first();
20156 this.headerEl = this.el.select('.popover-title',true).first();
20159 if(typeof(this.items) != 'undefined'){
20160 var items = this.items;
20163 for(var i =0;i < items.length;i++) {
20164 nitems.push(this.addxtype(Roo.apply({}, items[i])));
20168 this.items = nitems;
20170 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
20171 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
20178 resizeMask : function()
20180 this.maskEl.setSize(
20181 Roo.lib.Dom.getViewWidth(true),
20182 Roo.lib.Dom.getViewHeight(true)
20186 initEvents : function()
20190 Roo.bootstrap.Popover.register(this);
20193 this.arrowEl = this.el.select('.arrow',true).first();
20194 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
20195 this.el.enableDisplayMode('block');
20199 if (this.over === false && !this.parent()) {
20202 if (this.triggers === false) {
20207 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
20208 var triggers = this.trigger ? this.trigger.split(' ') : [];
20209 Roo.each(triggers, function(trigger) {
20211 if (trigger == 'click') {
20212 on_el.on('click', this.toggle, this);
20213 } else if (trigger != 'manual') {
20214 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
20215 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
20217 on_el.on(eventIn ,this.enter, this);
20218 on_el.on(eventOut, this.leave, this);
20228 toggle : function () {
20229 this.hoverState == 'in' ? this.leave() : this.enter();
20232 enter : function () {
20234 clearTimeout(this.timeout);
20236 this.hoverState = 'in';
20238 if (!this.delay || !this.delay.show) {
20243 this.timeout = setTimeout(function () {
20244 if (_t.hoverState == 'in') {
20247 }, this.delay.show)
20250 leave : function() {
20251 clearTimeout(this.timeout);
20253 this.hoverState = 'out';
20255 if (!this.delay || !this.delay.hide) {
20260 this.timeout = setTimeout(function () {
20261 if (_t.hoverState == 'out') {
20264 }, this.delay.hide)
20268 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
20269 * @param {string} (left|right|top|bottom) position
20271 show : function (on_el, placement)
20273 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
20274 on_el = on_el || false; // default to false
20277 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
20278 on_el = this.parent().el;
20279 } else if (this.over) {
20280 on_el = Roo.get(this.over);
20285 this.alignEl = Roo.get( on_el );
20288 this.render(document.body);
20294 if (this.title === false) {
20295 this.headerEl.hide();
20300 this.el.dom.style.display = 'block';
20303 if (this.alignEl) {
20304 this.updatePosition(this.placement, true);
20307 // this is usually just done by the builder = to show the popoup in the middle of the scren.
20308 var es = this.el.getSize();
20309 var x = Roo.lib.Dom.getViewWidth()/2;
20310 var y = Roo.lib.Dom.getViewHeight()/2;
20311 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
20316 //var arrow = this.el.select('.arrow',true).first();
20317 //arrow.set(align[2],
20319 this.el.addClass('in');
20323 this.hoverState = 'in';
20326 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
20327 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20328 this.maskEl.dom.style.display = 'block';
20329 this.maskEl.addClass('show');
20331 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20333 this.fireEvent('show', this);
20337 * fire this manually after loading a grid in the table for example
20338 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20339 * @param {Boolean} try and move it if we cant get right position.
20341 updatePosition : function(placement, try_move)
20343 // allow for calling with no parameters
20344 placement = placement ? placement : this.placement;
20345 try_move = typeof(try_move) == 'undefined' ? true : try_move;
20347 this.el.removeClass([
20348 'fade','top','bottom', 'left', 'right','in',
20349 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20351 this.el.addClass(placement + ' bs-popover-' + placement);
20353 if (!this.alignEl ) {
20357 switch (placement) {
20359 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20360 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20361 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20362 //normal display... or moved up/down.
20363 this.el.setXY(offset);
20364 var xy = this.alignEl.getAnchorXY('tr', false);
20366 this.arrowEl.setXY(xy);
20369 // continue through...
20370 return this.updatePosition('left', false);
20374 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20375 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20376 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20377 //normal display... or moved up/down.
20378 this.el.setXY(offset);
20379 var xy = this.alignEl.getAnchorXY('tl', false);
20380 xy[0]-=10;xy[1]+=5; // << fix me
20381 this.arrowEl.setXY(xy);
20385 return this.updatePosition('right', false);
20388 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20389 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20390 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20391 //normal display... or moved up/down.
20392 this.el.setXY(offset);
20393 var xy = this.alignEl.getAnchorXY('t', false);
20394 xy[1]-=10; // << fix me
20395 this.arrowEl.setXY(xy);
20399 return this.updatePosition('bottom', false);
20402 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20403 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20404 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20405 //normal display... or moved up/down.
20406 this.el.setXY(offset);
20407 var xy = this.alignEl.getAnchorXY('b', false);
20408 xy[1]+=2; // << fix me
20409 this.arrowEl.setXY(xy);
20413 return this.updatePosition('top', false);
20424 this.el.setXY([0,0]);
20425 this.el.removeClass('in');
20427 this.hoverState = null;
20428 this.maskEl.hide(); // always..
20429 this.fireEvent('hide', this);
20435 Roo.apply(Roo.bootstrap.Popover, {
20438 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20439 'right' : ['l-br', [10,0], 'right bs-popover-right'],
20440 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20441 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20446 clickHander : false,
20450 onMouseDown : function(e)
20452 if (this.popups.length && !e.getTarget(".roo-popover")) {
20453 /// what is nothing is showing..
20462 register : function(popup)
20464 if (!Roo.bootstrap.Popover.clickHandler) {
20465 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20467 // hide other popups.
20468 popup.on('show', Roo.bootstrap.Popover.onShow, popup);
20469 popup.on('hide', Roo.bootstrap.Popover.onHide, popup);
20470 this.hideAll(); //<< why?
20471 //this.popups.push(popup);
20473 hideAll : function()
20475 this.popups.forEach(function(p) {
20479 onShow : function() {
20480 Roo.bootstrap.Popover.popups.push(this);
20482 onHide : function() {
20483 Roo.bootstrap.Popover.popups.remove(this);
20489 * Card header - holder for the card header elements.
20494 * @class Roo.bootstrap.PopoverNav
20495 * @extends Roo.bootstrap.NavGroup
20496 * Bootstrap Popover header navigation class
20498 * Create a new Popover Header Navigation
20499 * @param {Object} config The config object
20502 Roo.bootstrap.PopoverNav = function(config){
20503 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20506 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
20509 container_method : 'getPopoverHeader'
20527 * @class Roo.bootstrap.Progress
20528 * @extends Roo.bootstrap.Component
20529 * Bootstrap Progress class
20530 * @cfg {Boolean} striped striped of the progress bar
20531 * @cfg {Boolean} active animated of the progress bar
20535 * Create a new Progress
20536 * @param {Object} config The config object
20539 Roo.bootstrap.Progress = function(config){
20540 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20543 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
20548 getAutoCreate : function(){
20556 cfg.cls += ' progress-striped';
20560 cfg.cls += ' active';
20579 * @class Roo.bootstrap.ProgressBar
20580 * @extends Roo.bootstrap.Component
20581 * Bootstrap ProgressBar class
20582 * @cfg {Number} aria_valuenow aria-value now
20583 * @cfg {Number} aria_valuemin aria-value min
20584 * @cfg {Number} aria_valuemax aria-value max
20585 * @cfg {String} label label for the progress bar
20586 * @cfg {String} panel (success | info | warning | danger )
20587 * @cfg {String} role role of the progress bar
20588 * @cfg {String} sr_only text
20592 * Create a new ProgressBar
20593 * @param {Object} config The config object
20596 Roo.bootstrap.ProgressBar = function(config){
20597 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20600 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
20604 aria_valuemax : 100,
20610 getAutoCreate : function()
20615 cls: 'progress-bar',
20616 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20628 cfg.role = this.role;
20631 if(this.aria_valuenow){
20632 cfg['aria-valuenow'] = this.aria_valuenow;
20635 if(this.aria_valuemin){
20636 cfg['aria-valuemin'] = this.aria_valuemin;
20639 if(this.aria_valuemax){
20640 cfg['aria-valuemax'] = this.aria_valuemax;
20643 if(this.label && !this.sr_only){
20644 cfg.html = this.label;
20648 cfg.cls += ' progress-bar-' + this.panel;
20654 update : function(aria_valuenow)
20656 this.aria_valuenow = aria_valuenow;
20658 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20673 * @class Roo.bootstrap.TabGroup
20674 * @extends Roo.bootstrap.Column
20675 * Bootstrap Column class
20676 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20677 * @cfg {Boolean} carousel true to make the group behave like a carousel
20678 * @cfg {Boolean} bullets show bullets for the panels
20679 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20680 * @cfg {Number} timer auto slide timer .. default 0 millisecond
20681 * @cfg {Boolean} showarrow (true|false) show arrow default true
20684 * Create a new TabGroup
20685 * @param {Object} config The config object
20688 Roo.bootstrap.TabGroup = function(config){
20689 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20691 this.navId = Roo.id();
20694 Roo.bootstrap.TabGroup.register(this);
20698 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
20701 transition : false,
20706 slideOnTouch : false,
20709 getAutoCreate : function()
20711 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20713 cfg.cls += ' tab-content';
20715 if (this.carousel) {
20716 cfg.cls += ' carousel slide';
20719 cls : 'carousel-inner',
20723 if(this.bullets && !Roo.isTouch){
20726 cls : 'carousel-bullets',
20730 if(this.bullets_cls){
20731 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20738 cfg.cn[0].cn.push(bullets);
20741 if(this.showarrow){
20742 cfg.cn[0].cn.push({
20744 class : 'carousel-arrow',
20748 class : 'carousel-prev',
20752 class : 'fa fa-chevron-left'
20758 class : 'carousel-next',
20762 class : 'fa fa-chevron-right'
20775 initEvents: function()
20777 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20778 // this.el.on("touchstart", this.onTouchStart, this);
20781 if(this.autoslide){
20784 this.slideFn = window.setInterval(function() {
20785 _this.showPanelNext();
20789 if(this.showarrow){
20790 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20791 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20797 // onTouchStart : function(e, el, o)
20799 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20803 // this.showPanelNext();
20807 getChildContainer : function()
20809 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20813 * register a Navigation item
20814 * @param {Roo.bootstrap.NavItem} the navitem to add
20816 register : function(item)
20818 this.tabs.push( item);
20819 item.navId = this.navId; // not really needed..
20824 getActivePanel : function()
20827 Roo.each(this.tabs, function(t) {
20837 getPanelByName : function(n)
20840 Roo.each(this.tabs, function(t) {
20841 if (t.tabId == n) {
20849 indexOfPanel : function(p)
20852 Roo.each(this.tabs, function(t,i) {
20853 if (t.tabId == p.tabId) {
20862 * show a specific panel
20863 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20864 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20866 showPanel : function (pan)
20868 if(this.transition || typeof(pan) == 'undefined'){
20869 Roo.log("waiting for the transitionend");
20873 if (typeof(pan) == 'number') {
20874 pan = this.tabs[pan];
20877 if (typeof(pan) == 'string') {
20878 pan = this.getPanelByName(pan);
20881 var cur = this.getActivePanel();
20884 Roo.log('pan or acitve pan is undefined');
20888 if (pan.tabId == this.getActivePanel().tabId) {
20892 if (false === cur.fireEvent('beforedeactivate')) {
20896 if(this.bullets > 0 && !Roo.isTouch){
20897 this.setActiveBullet(this.indexOfPanel(pan));
20900 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20902 //class="carousel-item carousel-item-next carousel-item-left"
20904 this.transition = true;
20905 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
20906 var lr = dir == 'next' ? 'left' : 'right';
20907 pan.el.addClass(dir); // or prev
20908 pan.el.addClass('carousel-item-' + dir); // or prev
20909 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20910 cur.el.addClass(lr); // or right
20911 pan.el.addClass(lr);
20912 cur.el.addClass('carousel-item-' +lr); // or right
20913 pan.el.addClass('carousel-item-' +lr);
20917 cur.el.on('transitionend', function() {
20918 Roo.log("trans end?");
20920 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20921 pan.setActive(true);
20923 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20924 cur.setActive(false);
20926 _this.transition = false;
20928 }, this, { single: true } );
20933 cur.setActive(false);
20934 pan.setActive(true);
20939 showPanelNext : function()
20941 var i = this.indexOfPanel(this.getActivePanel());
20943 if (i >= this.tabs.length - 1 && !this.autoslide) {
20947 if (i >= this.tabs.length - 1 && this.autoslide) {
20951 this.showPanel(this.tabs[i+1]);
20954 showPanelPrev : function()
20956 var i = this.indexOfPanel(this.getActivePanel());
20958 if (i < 1 && !this.autoslide) {
20962 if (i < 1 && this.autoslide) {
20963 i = this.tabs.length;
20966 this.showPanel(this.tabs[i-1]);
20970 addBullet: function()
20972 if(!this.bullets || Roo.isTouch){
20975 var ctr = this.el.select('.carousel-bullets',true).first();
20976 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20977 var bullet = ctr.createChild({
20978 cls : 'bullet bullet-' + i
20979 },ctr.dom.lastChild);
20984 bullet.on('click', (function(e, el, o, ii, t){
20986 e.preventDefault();
20988 this.showPanel(ii);
20990 if(this.autoslide && this.slideFn){
20991 clearInterval(this.slideFn);
20992 this.slideFn = window.setInterval(function() {
20993 _this.showPanelNext();
20997 }).createDelegate(this, [i, bullet], true));
21002 setActiveBullet : function(i)
21008 Roo.each(this.el.select('.bullet', true).elements, function(el){
21009 el.removeClass('selected');
21012 var bullet = this.el.select('.bullet-' + i, true).first();
21018 bullet.addClass('selected');
21029 Roo.apply(Roo.bootstrap.TabGroup, {
21033 * register a Navigation Group
21034 * @param {Roo.bootstrap.NavGroup} the navgroup to add
21036 register : function(navgrp)
21038 this.groups[navgrp.navId] = navgrp;
21042 * fetch a Navigation Group based on the navigation ID
21043 * if one does not exist , it will get created.
21044 * @param {string} the navgroup to add
21045 * @returns {Roo.bootstrap.NavGroup} the navgroup
21047 get: function(navId) {
21048 if (typeof(this.groups[navId]) == 'undefined') {
21049 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
21051 return this.groups[navId] ;
21066 * @class Roo.bootstrap.TabPanel
21067 * @extends Roo.bootstrap.Component
21068 * Bootstrap TabPanel class
21069 * @cfg {Boolean} active panel active
21070 * @cfg {String} html panel content
21071 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
21072 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
21073 * @cfg {String} href click to link..
21074 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
21078 * Create a new TabPanel
21079 * @param {Object} config The config object
21082 Roo.bootstrap.TabPanel = function(config){
21083 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
21087 * Fires when the active status changes
21088 * @param {Roo.bootstrap.TabPanel} this
21089 * @param {Boolean} state the new state
21094 * @event beforedeactivate
21095 * Fires before a tab is de-activated - can be used to do validation on a form.
21096 * @param {Roo.bootstrap.TabPanel} this
21097 * @return {Boolean} false if there is an error
21100 'beforedeactivate': true
21103 this.tabId = this.tabId || Roo.id();
21107 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
21114 touchSlide : false,
21115 getAutoCreate : function(){
21120 // item is needed for carousel - not sure if it has any effect otherwise
21121 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
21122 html: this.html || ''
21126 cfg.cls += ' active';
21130 cfg.tabId = this.tabId;
21138 initEvents: function()
21140 var p = this.parent();
21142 this.navId = this.navId || p.navId;
21144 if (typeof(this.navId) != 'undefined') {
21145 // not really needed.. but just in case.. parent should be a NavGroup.
21146 var tg = Roo.bootstrap.TabGroup.get(this.navId);
21150 var i = tg.tabs.length - 1;
21152 if(this.active && tg.bullets > 0 && i < tg.bullets){
21153 tg.setActiveBullet(i);
21157 this.el.on('click', this.onClick, this);
21159 if(Roo.isTouch && this.touchSlide){
21160 this.el.on("touchstart", this.onTouchStart, this);
21161 this.el.on("touchmove", this.onTouchMove, this);
21162 this.el.on("touchend", this.onTouchEnd, this);
21167 onRender : function(ct, position)
21169 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
21172 setActive : function(state)
21174 Roo.log("panel - set active " + this.tabId + "=" + state);
21176 this.active = state;
21178 this.el.removeClass('active');
21180 } else if (!this.el.hasClass('active')) {
21181 this.el.addClass('active');
21184 this.fireEvent('changed', this, state);
21187 onClick : function(e)
21189 e.preventDefault();
21191 if(!this.href.length){
21195 window.location.href = this.href;
21204 onTouchStart : function(e)
21206 this.swiping = false;
21208 this.startX = e.browserEvent.touches[0].clientX;
21209 this.startY = e.browserEvent.touches[0].clientY;
21212 onTouchMove : function(e)
21214 this.swiping = true;
21216 this.endX = e.browserEvent.touches[0].clientX;
21217 this.endY = e.browserEvent.touches[0].clientY;
21220 onTouchEnd : function(e)
21227 var tabGroup = this.parent();
21229 if(this.endX > this.startX){ // swiping right
21230 tabGroup.showPanelPrev();
21234 if(this.startX > this.endX){ // swiping left
21235 tabGroup.showPanelNext();
21254 * @class Roo.bootstrap.DateField
21255 * @extends Roo.bootstrap.Input
21256 * Bootstrap DateField class
21257 * @cfg {Number} weekStart default 0
21258 * @cfg {String} viewMode default empty, (months|years)
21259 * @cfg {String} minViewMode default empty, (months|years)
21260 * @cfg {Number} startDate default -Infinity
21261 * @cfg {Number} endDate default Infinity
21262 * @cfg {Boolean} todayHighlight default false
21263 * @cfg {Boolean} todayBtn default false
21264 * @cfg {Boolean} calendarWeeks default false
21265 * @cfg {Object} daysOfWeekDisabled default empty
21266 * @cfg {Boolean} singleMode default false (true | false)
21268 * @cfg {Boolean} keyboardNavigation default true
21269 * @cfg {String} language default en
21272 * Create a new DateField
21273 * @param {Object} config The config object
21276 Roo.bootstrap.DateField = function(config){
21277 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
21281 * Fires when this field show.
21282 * @param {Roo.bootstrap.DateField} this
21283 * @param {Mixed} date The date value
21288 * Fires when this field hide.
21289 * @param {Roo.bootstrap.DateField} this
21290 * @param {Mixed} date The date value
21295 * Fires when select a date.
21296 * @param {Roo.bootstrap.DateField} this
21297 * @param {Mixed} date The date value
21301 * @event beforeselect
21302 * Fires when before select a date.
21303 * @param {Roo.bootstrap.DateField} this
21304 * @param {Mixed} date The date value
21306 beforeselect : true
21310 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
21313 * @cfg {String} format
21314 * The default date format string which can be overriden for localization support. The format must be
21315 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
21319 * @cfg {String} altFormats
21320 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
21321 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21323 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21331 todayHighlight : false,
21337 keyboardNavigation: true,
21339 calendarWeeks: false,
21341 startDate: -Infinity,
21345 daysOfWeekDisabled: [],
21349 singleMode : false,
21351 UTCDate: function()
21353 return new Date(Date.UTC.apply(Date, arguments));
21356 UTCToday: function()
21358 var today = new Date();
21359 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21362 getDate: function() {
21363 var d = this.getUTCDate();
21364 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21367 getUTCDate: function() {
21371 setDate: function(d) {
21372 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21375 setUTCDate: function(d) {
21377 this.setValue(this.formatDate(this.date));
21380 onRender: function(ct, position)
21383 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21385 this.language = this.language || 'en';
21386 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21387 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21389 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21390 this.format = this.format || 'm/d/y';
21391 this.isInline = false;
21392 this.isInput = true;
21393 this.component = this.el.select('.add-on', true).first() || false;
21394 this.component = (this.component && this.component.length === 0) ? false : this.component;
21395 this.hasInput = this.component && this.inputEl().length;
21397 if (typeof(this.minViewMode === 'string')) {
21398 switch (this.minViewMode) {
21400 this.minViewMode = 1;
21403 this.minViewMode = 2;
21406 this.minViewMode = 0;
21411 if (typeof(this.viewMode === 'string')) {
21412 switch (this.viewMode) {
21425 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21427 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21429 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21431 this.picker().on('mousedown', this.onMousedown, this);
21432 this.picker().on('click', this.onClick, this);
21434 this.picker().addClass('datepicker-dropdown');
21436 this.startViewMode = this.viewMode;
21438 if(this.singleMode){
21439 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21440 v.setVisibilityMode(Roo.Element.DISPLAY);
21444 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21445 v.setStyle('width', '189px');
21449 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21450 if(!this.calendarWeeks){
21455 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21456 v.attr('colspan', function(i, val){
21457 return parseInt(val) + 1;
21462 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21464 this.setStartDate(this.startDate);
21465 this.setEndDate(this.endDate);
21467 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21474 if(this.isInline) {
21479 picker : function()
21481 return this.pickerEl;
21482 // return this.el.select('.datepicker', true).first();
21485 fillDow: function()
21487 var dowCnt = this.weekStart;
21496 if(this.calendarWeeks){
21504 while (dowCnt < this.weekStart + 7) {
21508 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21512 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21515 fillMonths: function()
21518 var months = this.picker().select('>.datepicker-months td', true).first();
21520 months.dom.innerHTML = '';
21526 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21529 months.createChild(month);
21536 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;
21538 if (this.date < this.startDate) {
21539 this.viewDate = new Date(this.startDate);
21540 } else if (this.date > this.endDate) {
21541 this.viewDate = new Date(this.endDate);
21543 this.viewDate = new Date(this.date);
21551 var d = new Date(this.viewDate),
21552 year = d.getUTCFullYear(),
21553 month = d.getUTCMonth(),
21554 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21555 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21556 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21557 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21558 currentDate = this.date && this.date.valueOf(),
21559 today = this.UTCToday();
21561 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21563 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21565 // this.picker.select('>tfoot th.today').
21566 // .text(dates[this.language].today)
21567 // .toggle(this.todayBtn !== false);
21569 this.updateNavArrows();
21572 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21574 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21576 prevMonth.setUTCDate(day);
21578 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21580 var nextMonth = new Date(prevMonth);
21582 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21584 nextMonth = nextMonth.valueOf();
21586 var fillMonths = false;
21588 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21590 while(prevMonth.valueOf() <= nextMonth) {
21593 if (prevMonth.getUTCDay() === this.weekStart) {
21595 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21603 if(this.calendarWeeks){
21604 // ISO 8601: First week contains first thursday.
21605 // ISO also states week starts on Monday, but we can be more abstract here.
21607 // Start of current week: based on weekstart/current date
21608 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21609 // Thursday of this week
21610 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21611 // First Thursday of year, year from thursday
21612 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21613 // Calendar week: ms between thursdays, div ms per day, div 7 days
21614 calWeek = (th - yth) / 864e5 / 7 + 1;
21616 fillMonths.cn.push({
21624 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21626 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21629 if (this.todayHighlight &&
21630 prevMonth.getUTCFullYear() == today.getFullYear() &&
21631 prevMonth.getUTCMonth() == today.getMonth() &&
21632 prevMonth.getUTCDate() == today.getDate()) {
21633 clsName += ' today';
21636 if (currentDate && prevMonth.valueOf() === currentDate) {
21637 clsName += ' active';
21640 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21641 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21642 clsName += ' disabled';
21645 fillMonths.cn.push({
21647 cls: 'day ' + clsName,
21648 html: prevMonth.getDate()
21651 prevMonth.setDate(prevMonth.getDate()+1);
21654 var currentYear = this.date && this.date.getUTCFullYear();
21655 var currentMonth = this.date && this.date.getUTCMonth();
21657 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21659 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21660 v.removeClass('active');
21662 if(currentYear === year && k === currentMonth){
21663 v.addClass('active');
21666 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21667 v.addClass('disabled');
21673 year = parseInt(year/10, 10) * 10;
21675 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21677 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21680 for (var i = -1; i < 11; i++) {
21681 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21683 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21691 showMode: function(dir)
21694 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21697 Roo.each(this.picker().select('>div',true).elements, function(v){
21698 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21701 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21706 if(this.isInline) {
21710 this.picker().removeClass(['bottom', 'top']);
21712 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21714 * place to the top of element!
21718 this.picker().addClass('top');
21719 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21724 this.picker().addClass('bottom');
21726 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21729 parseDate : function(value)
21731 if(!value || value instanceof Date){
21734 var v = Date.parseDate(value, this.format);
21735 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21736 v = Date.parseDate(value, 'Y-m-d');
21738 if(!v && this.altFormats){
21739 if(!this.altFormatsArray){
21740 this.altFormatsArray = this.altFormats.split("|");
21742 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21743 v = Date.parseDate(value, this.altFormatsArray[i]);
21749 formatDate : function(date, fmt)
21751 return (!date || !(date instanceof Date)) ?
21752 date : date.dateFormat(fmt || this.format);
21755 onFocus : function()
21757 Roo.bootstrap.DateField.superclass.onFocus.call(this);
21761 onBlur : function()
21763 Roo.bootstrap.DateField.superclass.onBlur.call(this);
21765 var d = this.inputEl().getValue();
21772 showPopup : function()
21774 this.picker().show();
21778 this.fireEvent('showpopup', this, this.date);
21781 hidePopup : function()
21783 if(this.isInline) {
21786 this.picker().hide();
21787 this.viewMode = this.startViewMode;
21790 this.fireEvent('hidepopup', this, this.date);
21794 onMousedown: function(e)
21796 e.stopPropagation();
21797 e.preventDefault();
21802 Roo.bootstrap.DateField.superclass.keyup.call(this);
21806 setValue: function(v)
21808 if(this.fireEvent('beforeselect', this, v) !== false){
21809 var d = new Date(this.parseDate(v) ).clearTime();
21811 if(isNaN(d.getTime())){
21812 this.date = this.viewDate = '';
21813 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21817 v = this.formatDate(d);
21819 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21821 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21825 this.fireEvent('select', this, this.date);
21829 getValue: function()
21831 return this.formatDate(this.date);
21834 fireKey: function(e)
21836 if (!this.picker().isVisible()){
21837 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21843 var dateChanged = false,
21845 newDate, newViewDate;
21850 e.preventDefault();
21854 if (!this.keyboardNavigation) {
21857 dir = e.keyCode == 37 ? -1 : 1;
21860 newDate = this.moveYear(this.date, dir);
21861 newViewDate = this.moveYear(this.viewDate, dir);
21862 } else if (e.shiftKey){
21863 newDate = this.moveMonth(this.date, dir);
21864 newViewDate = this.moveMonth(this.viewDate, dir);
21866 newDate = new Date(this.date);
21867 newDate.setUTCDate(this.date.getUTCDate() + dir);
21868 newViewDate = new Date(this.viewDate);
21869 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21871 if (this.dateWithinRange(newDate)){
21872 this.date = newDate;
21873 this.viewDate = newViewDate;
21874 this.setValue(this.formatDate(this.date));
21876 e.preventDefault();
21877 dateChanged = true;
21882 if (!this.keyboardNavigation) {
21885 dir = e.keyCode == 38 ? -1 : 1;
21887 newDate = this.moveYear(this.date, dir);
21888 newViewDate = this.moveYear(this.viewDate, dir);
21889 } else if (e.shiftKey){
21890 newDate = this.moveMonth(this.date, dir);
21891 newViewDate = this.moveMonth(this.viewDate, dir);
21893 newDate = new Date(this.date);
21894 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21895 newViewDate = new Date(this.viewDate);
21896 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21898 if (this.dateWithinRange(newDate)){
21899 this.date = newDate;
21900 this.viewDate = newViewDate;
21901 this.setValue(this.formatDate(this.date));
21903 e.preventDefault();
21904 dateChanged = true;
21908 this.setValue(this.formatDate(this.date));
21910 e.preventDefault();
21913 this.setValue(this.formatDate(this.date));
21927 onClick: function(e)
21929 e.stopPropagation();
21930 e.preventDefault();
21932 var target = e.getTarget();
21934 if(target.nodeName.toLowerCase() === 'i'){
21935 target = Roo.get(target).dom.parentNode;
21938 var nodeName = target.nodeName;
21939 var className = target.className;
21940 var html = target.innerHTML;
21941 //Roo.log(nodeName);
21943 switch(nodeName.toLowerCase()) {
21945 switch(className) {
21951 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21952 switch(this.viewMode){
21954 this.viewDate = this.moveMonth(this.viewDate, dir);
21958 this.viewDate = this.moveYear(this.viewDate, dir);
21964 var date = new Date();
21965 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21967 this.setValue(this.formatDate(this.date));
21974 if (className.indexOf('disabled') < 0) {
21975 if (!this.viewDate) {
21976 this.viewDate = new Date();
21978 this.viewDate.setUTCDate(1);
21979 if (className.indexOf('month') > -1) {
21980 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21982 var year = parseInt(html, 10) || 0;
21983 this.viewDate.setUTCFullYear(year);
21987 if(this.singleMode){
21988 this.setValue(this.formatDate(this.viewDate));
21999 //Roo.log(className);
22000 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
22001 var day = parseInt(html, 10) || 1;
22002 var year = (this.viewDate || new Date()).getUTCFullYear(),
22003 month = (this.viewDate || new Date()).getUTCMonth();
22005 if (className.indexOf('old') > -1) {
22012 } else if (className.indexOf('new') > -1) {
22020 //Roo.log([year,month,day]);
22021 this.date = this.UTCDate(year, month, day,0,0,0,0);
22022 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
22024 //Roo.log(this.formatDate(this.date));
22025 this.setValue(this.formatDate(this.date));
22032 setStartDate: function(startDate)
22034 this.startDate = startDate || -Infinity;
22035 if (this.startDate !== -Infinity) {
22036 this.startDate = this.parseDate(this.startDate);
22039 this.updateNavArrows();
22042 setEndDate: function(endDate)
22044 this.endDate = endDate || Infinity;
22045 if (this.endDate !== Infinity) {
22046 this.endDate = this.parseDate(this.endDate);
22049 this.updateNavArrows();
22052 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
22054 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
22055 if (typeof(this.daysOfWeekDisabled) !== 'object') {
22056 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
22058 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
22059 return parseInt(d, 10);
22062 this.updateNavArrows();
22065 updateNavArrows: function()
22067 if(this.singleMode){
22071 var d = new Date(this.viewDate),
22072 year = d.getUTCFullYear(),
22073 month = d.getUTCMonth();
22075 Roo.each(this.picker().select('.prev', true).elements, function(v){
22077 switch (this.viewMode) {
22080 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
22086 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
22093 Roo.each(this.picker().select('.next', true).elements, function(v){
22095 switch (this.viewMode) {
22098 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
22104 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
22112 moveMonth: function(date, dir)
22117 var new_date = new Date(date.valueOf()),
22118 day = new_date.getUTCDate(),
22119 month = new_date.getUTCMonth(),
22120 mag = Math.abs(dir),
22122 dir = dir > 0 ? 1 : -1;
22125 // If going back one month, make sure month is not current month
22126 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
22128 return new_date.getUTCMonth() == month;
22130 // If going forward one month, make sure month is as expected
22131 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
22133 return new_date.getUTCMonth() != new_month;
22135 new_month = month + dir;
22136 new_date.setUTCMonth(new_month);
22137 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
22138 if (new_month < 0 || new_month > 11) {
22139 new_month = (new_month + 12) % 12;
22142 // For magnitudes >1, move one month at a time...
22143 for (var i=0; i<mag; i++) {
22144 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
22145 new_date = this.moveMonth(new_date, dir);
22147 // ...then reset the day, keeping it in the new month
22148 new_month = new_date.getUTCMonth();
22149 new_date.setUTCDate(day);
22151 return new_month != new_date.getUTCMonth();
22154 // Common date-resetting loop -- if date is beyond end of month, make it
22157 new_date.setUTCDate(--day);
22158 new_date.setUTCMonth(new_month);
22163 moveYear: function(date, dir)
22165 return this.moveMonth(date, dir*12);
22168 dateWithinRange: function(date)
22170 return date >= this.startDate && date <= this.endDate;
22176 this.picker().remove();
22179 validateValue : function(value)
22181 if(this.getVisibilityEl().hasClass('hidden')){
22185 if(value.length < 1) {
22186 if(this.allowBlank){
22192 if(value.length < this.minLength){
22195 if(value.length > this.maxLength){
22199 var vt = Roo.form.VTypes;
22200 if(!vt[this.vtype](value, this)){
22204 if(typeof this.validator == "function"){
22205 var msg = this.validator(value);
22211 if(this.regex && !this.regex.test(value)){
22215 if(typeof(this.parseDate(value)) == 'undefined'){
22219 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
22223 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
22233 this.date = this.viewDate = '';
22235 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22240 Roo.apply(Roo.bootstrap.DateField, {
22251 html: '<i class="fa fa-arrow-left"/>'
22261 html: '<i class="fa fa-arrow-right"/>'
22303 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
22304 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
22305 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
22306 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22307 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
22320 navFnc: 'FullYear',
22325 navFnc: 'FullYear',
22330 Roo.apply(Roo.bootstrap.DateField, {
22334 cls: 'datepicker dropdown-menu roo-dynamic shadow',
22338 cls: 'datepicker-days',
22342 cls: 'table-condensed',
22344 Roo.bootstrap.DateField.head,
22348 Roo.bootstrap.DateField.footer
22355 cls: 'datepicker-months',
22359 cls: 'table-condensed',
22361 Roo.bootstrap.DateField.head,
22362 Roo.bootstrap.DateField.content,
22363 Roo.bootstrap.DateField.footer
22370 cls: 'datepicker-years',
22374 cls: 'table-condensed',
22376 Roo.bootstrap.DateField.head,
22377 Roo.bootstrap.DateField.content,
22378 Roo.bootstrap.DateField.footer
22397 * @class Roo.bootstrap.TimeField
22398 * @extends Roo.bootstrap.Input
22399 * Bootstrap DateField class
22403 * Create a new TimeField
22404 * @param {Object} config The config object
22407 Roo.bootstrap.TimeField = function(config){
22408 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22412 * Fires when this field show.
22413 * @param {Roo.bootstrap.DateField} thisthis
22414 * @param {Mixed} date The date value
22419 * Fires when this field hide.
22420 * @param {Roo.bootstrap.DateField} this
22421 * @param {Mixed} date The date value
22426 * Fires when select a date.
22427 * @param {Roo.bootstrap.DateField} this
22428 * @param {Mixed} date The date value
22434 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
22437 * @cfg {String} format
22438 * The default time format string which can be overriden for localization support. The format must be
22439 * valid according to {@link Date#parseDate} (defaults to 'H:i').
22443 getAutoCreate : function()
22445 this.after = '<i class="fa far fa-clock"></i>';
22446 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22450 onRender: function(ct, position)
22453 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22455 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22457 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22459 this.pop = this.picker().select('>.datepicker-time',true).first();
22460 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22462 this.picker().on('mousedown', this.onMousedown, this);
22463 this.picker().on('click', this.onClick, this);
22465 this.picker().addClass('datepicker-dropdown');
22470 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22471 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22472 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22473 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22474 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22475 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22479 fireKey: function(e){
22480 if (!this.picker().isVisible()){
22481 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22487 e.preventDefault();
22495 this.onTogglePeriod();
22498 this.onIncrementMinutes();
22501 this.onDecrementMinutes();
22510 onClick: function(e) {
22511 e.stopPropagation();
22512 e.preventDefault();
22515 picker : function()
22517 return this.pickerEl;
22520 fillTime: function()
22522 var time = this.pop.select('tbody', true).first();
22524 time.dom.innerHTML = '';
22539 cls: 'hours-up fa fas fa-chevron-up'
22559 cls: 'minutes-up fa fas fa-chevron-up'
22580 cls: 'timepicker-hour',
22595 cls: 'timepicker-minute',
22610 cls: 'btn btn-primary period',
22632 cls: 'hours-down fa fas fa-chevron-down'
22652 cls: 'minutes-down fa fas fa-chevron-down'
22670 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22677 var hours = this.time.getHours();
22678 var minutes = this.time.getMinutes();
22691 hours = hours - 12;
22695 hours = '0' + hours;
22699 minutes = '0' + minutes;
22702 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22703 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22704 this.pop.select('button', true).first().dom.innerHTML = period;
22710 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22712 var cls = ['bottom'];
22714 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22721 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22725 //this.picker().setXY(20000,20000);
22726 this.picker().addClass(cls.join('-'));
22730 Roo.each(cls, function(c){
22735 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
22736 //_this.picker().setTop(_this.inputEl().getHeight());
22740 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
22742 //_this.picker().setTop(0 - _this.picker().getHeight());
22747 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22751 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22759 onFocus : function()
22761 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22765 onBlur : function()
22767 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22773 this.picker().show();
22778 this.fireEvent('show', this, this.date);
22783 this.picker().hide();
22786 this.fireEvent('hide', this, this.date);
22789 setTime : function()
22792 this.setValue(this.time.format(this.format));
22794 this.fireEvent('select', this, this.date);
22799 onMousedown: function(e){
22800 e.stopPropagation();
22801 e.preventDefault();
22804 onIncrementHours: function()
22806 Roo.log('onIncrementHours');
22807 this.time = this.time.add(Date.HOUR, 1);
22812 onDecrementHours: function()
22814 Roo.log('onDecrementHours');
22815 this.time = this.time.add(Date.HOUR, -1);
22819 onIncrementMinutes: function()
22821 Roo.log('onIncrementMinutes');
22822 this.time = this.time.add(Date.MINUTE, 1);
22826 onDecrementMinutes: function()
22828 Roo.log('onDecrementMinutes');
22829 this.time = this.time.add(Date.MINUTE, -1);
22833 onTogglePeriod: function()
22835 Roo.log('onTogglePeriod');
22836 this.time = this.time.add(Date.HOUR, 12);
22844 Roo.apply(Roo.bootstrap.TimeField, {
22848 cls: 'datepicker dropdown-menu',
22852 cls: 'datepicker-time',
22856 cls: 'table-condensed',
22885 cls: 'btn btn-info ok',
22913 * @class Roo.bootstrap.MonthField
22914 * @extends Roo.bootstrap.Input
22915 * Bootstrap MonthField class
22917 * @cfg {String} language default en
22920 * Create a new MonthField
22921 * @param {Object} config The config object
22924 Roo.bootstrap.MonthField = function(config){
22925 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22930 * Fires when this field show.
22931 * @param {Roo.bootstrap.MonthField} this
22932 * @param {Mixed} date The date value
22937 * Fires when this field hide.
22938 * @param {Roo.bootstrap.MonthField} this
22939 * @param {Mixed} date The date value
22944 * Fires when select a date.
22945 * @param {Roo.bootstrap.MonthField} this
22946 * @param {String} oldvalue The old value
22947 * @param {String} newvalue The new value
22953 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
22955 onRender: function(ct, position)
22958 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22960 this.language = this.language || 'en';
22961 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22962 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22964 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22965 this.isInline = false;
22966 this.isInput = true;
22967 this.component = this.el.select('.add-on', true).first() || false;
22968 this.component = (this.component && this.component.length === 0) ? false : this.component;
22969 this.hasInput = this.component && this.inputEL().length;
22971 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22973 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22975 this.picker().on('mousedown', this.onMousedown, this);
22976 this.picker().on('click', this.onClick, this);
22978 this.picker().addClass('datepicker-dropdown');
22980 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22981 v.setStyle('width', '189px');
22988 if(this.isInline) {
22994 setValue: function(v, suppressEvent)
22996 var o = this.getValue();
22998 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
23002 if(suppressEvent !== true){
23003 this.fireEvent('select', this, o, v);
23008 getValue: function()
23013 onClick: function(e)
23015 e.stopPropagation();
23016 e.preventDefault();
23018 var target = e.getTarget();
23020 if(target.nodeName.toLowerCase() === 'i'){
23021 target = Roo.get(target).dom.parentNode;
23024 var nodeName = target.nodeName;
23025 var className = target.className;
23026 var html = target.innerHTML;
23028 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
23032 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
23034 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23040 picker : function()
23042 return this.pickerEl;
23045 fillMonths: function()
23048 var months = this.picker().select('>.datepicker-months td', true).first();
23050 months.dom.innerHTML = '';
23056 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
23059 months.createChild(month);
23068 if(typeof(this.vIndex) == 'undefined' && this.value.length){
23069 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
23072 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
23073 e.removeClass('active');
23075 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
23076 e.addClass('active');
23083 if(this.isInline) {
23087 this.picker().removeClass(['bottom', 'top']);
23089 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23091 * place to the top of element!
23095 this.picker().addClass('top');
23096 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23101 this.picker().addClass('bottom');
23103 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23106 onFocus : function()
23108 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
23112 onBlur : function()
23114 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
23116 var d = this.inputEl().getValue();
23125 this.picker().show();
23126 this.picker().select('>.datepicker-months', true).first().show();
23130 this.fireEvent('show', this, this.date);
23135 if(this.isInline) {
23138 this.picker().hide();
23139 this.fireEvent('hide', this, this.date);
23143 onMousedown: function(e)
23145 e.stopPropagation();
23146 e.preventDefault();
23151 Roo.bootstrap.MonthField.superclass.keyup.call(this);
23155 fireKey: function(e)
23157 if (!this.picker().isVisible()){
23158 if (e.keyCode == 27) {// allow escape to hide and re-show picker
23169 e.preventDefault();
23173 dir = e.keyCode == 37 ? -1 : 1;
23175 this.vIndex = this.vIndex + dir;
23177 if(this.vIndex < 0){
23181 if(this.vIndex > 11){
23185 if(isNaN(this.vIndex)){
23189 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23195 dir = e.keyCode == 38 ? -1 : 1;
23197 this.vIndex = this.vIndex + dir * 4;
23199 if(this.vIndex < 0){
23203 if(this.vIndex > 11){
23207 if(isNaN(this.vIndex)){
23211 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23216 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23217 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23221 e.preventDefault();
23224 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23225 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23241 this.picker().remove();
23246 Roo.apply(Roo.bootstrap.MonthField, {
23265 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23266 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
23271 Roo.apply(Roo.bootstrap.MonthField, {
23275 cls: 'datepicker dropdown-menu roo-dynamic',
23279 cls: 'datepicker-months',
23283 cls: 'table-condensed',
23285 Roo.bootstrap.DateField.content
23305 * @class Roo.bootstrap.CheckBox
23306 * @extends Roo.bootstrap.Input
23307 * Bootstrap CheckBox class
23309 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23310 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
23311 * @cfg {String} boxLabel The text that appears beside the checkbox
23312 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
23313 * @cfg {Boolean} checked initnal the element
23314 * @cfg {Boolean} inline inline the element (default false)
23315 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
23316 * @cfg {String} tooltip label tooltip
23319 * Create a new CheckBox
23320 * @param {Object} config The config object
23323 Roo.bootstrap.CheckBox = function(config){
23324 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23329 * Fires when the element is checked or unchecked.
23330 * @param {Roo.bootstrap.CheckBox} this This input
23331 * @param {Boolean} checked The new checked value
23336 * Fires when the element is click.
23337 * @param {Roo.bootstrap.CheckBox} this This input
23344 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
23346 inputType: 'checkbox',
23355 // checkbox success does not make any sense really..
23360 getAutoCreate : function()
23362 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23368 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23371 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
23377 type : this.inputType,
23378 value : this.inputValue,
23379 cls : 'roo-' + this.inputType, //'form-box',
23380 placeholder : this.placeholder || ''
23384 if(this.inputType != 'radio'){
23388 cls : 'roo-hidden-value',
23389 value : this.checked ? this.inputValue : this.valueOff
23394 if (this.weight) { // Validity check?
23395 cfg.cls += " " + this.inputType + "-" + this.weight;
23398 if (this.disabled) {
23399 input.disabled=true;
23403 input.checked = this.checked;
23408 input.name = this.name;
23410 if(this.inputType != 'radio'){
23411 hidden.name = this.name;
23412 input.name = '_hidden_' + this.name;
23417 input.cls += ' input-' + this.size;
23422 ['xs','sm','md','lg'].map(function(size){
23423 if (settings[size]) {
23424 cfg.cls += ' col-' + size + '-' + settings[size];
23428 var inputblock = input;
23430 if (this.before || this.after) {
23433 cls : 'input-group',
23438 inputblock.cn.push({
23440 cls : 'input-group-addon',
23445 inputblock.cn.push(input);
23447 if(this.inputType != 'radio'){
23448 inputblock.cn.push(hidden);
23452 inputblock.cn.push({
23454 cls : 'input-group-addon',
23460 var boxLabelCfg = false;
23466 //'for': id, // box label is handled by onclick - so no for...
23468 html: this.boxLabel
23471 boxLabelCfg.tooltip = this.tooltip;
23477 if (align ==='left' && this.fieldLabel.length) {
23478 // Roo.log("left and has label");
23483 cls : 'control-label',
23484 html : this.fieldLabel
23495 cfg.cn[1].cn.push(boxLabelCfg);
23498 if(this.labelWidth > 12){
23499 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23502 if(this.labelWidth < 13 && this.labelmd == 0){
23503 this.labelmd = this.labelWidth;
23506 if(this.labellg > 0){
23507 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23508 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23511 if(this.labelmd > 0){
23512 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23513 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23516 if(this.labelsm > 0){
23517 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23518 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23521 if(this.labelxs > 0){
23522 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23523 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23526 } else if ( this.fieldLabel.length) {
23527 // Roo.log(" label");
23531 tag: this.boxLabel ? 'span' : 'label',
23533 cls: 'control-label box-input-label',
23534 //cls : 'input-group-addon',
23535 html : this.fieldLabel
23542 cfg.cn.push(boxLabelCfg);
23547 // Roo.log(" no label && no align");
23548 cfg.cn = [ inputblock ] ;
23550 cfg.cn.push(boxLabelCfg);
23558 if(this.inputType != 'radio'){
23559 cfg.cn.push(hidden);
23567 * return the real input element.
23569 inputEl: function ()
23571 return this.el.select('input.roo-' + this.inputType,true).first();
23573 hiddenEl: function ()
23575 return this.el.select('input.roo-hidden-value',true).first();
23578 labelEl: function()
23580 return this.el.select('label.control-label',true).first();
23582 /* depricated... */
23586 return this.labelEl();
23589 boxLabelEl: function()
23591 return this.el.select('label.box-label',true).first();
23594 initEvents : function()
23596 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23598 this.inputEl().on('click', this.onClick, this);
23600 if (this.boxLabel) {
23601 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
23604 this.startValue = this.getValue();
23607 Roo.bootstrap.CheckBox.register(this);
23611 onClick : function(e)
23613 if(this.fireEvent('click', this, e) !== false){
23614 this.setChecked(!this.checked);
23619 setChecked : function(state,suppressEvent)
23621 this.startValue = this.getValue();
23623 if(this.inputType == 'radio'){
23625 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23626 e.dom.checked = false;
23629 this.inputEl().dom.checked = true;
23631 this.inputEl().dom.value = this.inputValue;
23633 if(suppressEvent !== true){
23634 this.fireEvent('check', this, true);
23642 this.checked = state;
23644 this.inputEl().dom.checked = state;
23647 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23649 if(suppressEvent !== true){
23650 this.fireEvent('check', this, state);
23656 getValue : function()
23658 if(this.inputType == 'radio'){
23659 return this.getGroupValue();
23662 return this.hiddenEl().dom.value;
23666 getGroupValue : function()
23668 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23672 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23675 setValue : function(v,suppressEvent)
23677 if(this.inputType == 'radio'){
23678 this.setGroupValue(v, suppressEvent);
23682 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23687 setGroupValue : function(v, suppressEvent)
23689 this.startValue = this.getValue();
23691 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23692 e.dom.checked = false;
23694 if(e.dom.value == v){
23695 e.dom.checked = true;
23699 if(suppressEvent !== true){
23700 this.fireEvent('check', this, true);
23708 validate : function()
23710 if(this.getVisibilityEl().hasClass('hidden')){
23716 (this.inputType == 'radio' && this.validateRadio()) ||
23717 (this.inputType == 'checkbox' && this.validateCheckbox())
23723 this.markInvalid();
23727 validateRadio : function()
23729 if(this.getVisibilityEl().hasClass('hidden')){
23733 if(this.allowBlank){
23739 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23740 if(!e.dom.checked){
23752 validateCheckbox : function()
23755 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23756 //return (this.getValue() == this.inputValue) ? true : false;
23759 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23767 for(var i in group){
23768 if(group[i].el.isVisible(true)){
23776 for(var i in group){
23781 r = (group[i].getValue() == group[i].inputValue) ? true : false;
23788 * Mark this field as valid
23790 markValid : function()
23794 this.fireEvent('valid', this);
23796 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23799 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23806 if(this.inputType == 'radio'){
23807 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23808 var fg = e.findParent('.form-group', false, true);
23809 if (Roo.bootstrap.version == 3) {
23810 fg.removeClass([_this.invalidClass, _this.validClass]);
23811 fg.addClass(_this.validClass);
23813 fg.removeClass(['is-valid', 'is-invalid']);
23814 fg.addClass('is-valid');
23822 var fg = this.el.findParent('.form-group', false, true);
23823 if (Roo.bootstrap.version == 3) {
23824 fg.removeClass([this.invalidClass, this.validClass]);
23825 fg.addClass(this.validClass);
23827 fg.removeClass(['is-valid', 'is-invalid']);
23828 fg.addClass('is-valid');
23833 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23839 for(var i in group){
23840 var fg = group[i].el.findParent('.form-group', false, true);
23841 if (Roo.bootstrap.version == 3) {
23842 fg.removeClass([this.invalidClass, this.validClass]);
23843 fg.addClass(this.validClass);
23845 fg.removeClass(['is-valid', 'is-invalid']);
23846 fg.addClass('is-valid');
23852 * Mark this field as invalid
23853 * @param {String} msg The validation message
23855 markInvalid : function(msg)
23857 if(this.allowBlank){
23863 this.fireEvent('invalid', this, msg);
23865 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23868 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23872 label.markInvalid();
23875 if(this.inputType == 'radio'){
23877 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23878 var fg = e.findParent('.form-group', false, true);
23879 if (Roo.bootstrap.version == 3) {
23880 fg.removeClass([_this.invalidClass, _this.validClass]);
23881 fg.addClass(_this.invalidClass);
23883 fg.removeClass(['is-invalid', 'is-valid']);
23884 fg.addClass('is-invalid');
23892 var fg = this.el.findParent('.form-group', false, true);
23893 if (Roo.bootstrap.version == 3) {
23894 fg.removeClass([_this.invalidClass, _this.validClass]);
23895 fg.addClass(_this.invalidClass);
23897 fg.removeClass(['is-invalid', 'is-valid']);
23898 fg.addClass('is-invalid');
23903 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23909 for(var i in group){
23910 var fg = group[i].el.findParent('.form-group', false, true);
23911 if (Roo.bootstrap.version == 3) {
23912 fg.removeClass([_this.invalidClass, _this.validClass]);
23913 fg.addClass(_this.invalidClass);
23915 fg.removeClass(['is-invalid', 'is-valid']);
23916 fg.addClass('is-invalid');
23922 clearInvalid : function()
23924 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23926 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23928 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23930 if (label && label.iconEl) {
23931 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23932 label.iconEl.removeClass(['is-invalid', 'is-valid']);
23936 disable : function()
23938 if(this.inputType != 'radio'){
23939 Roo.bootstrap.CheckBox.superclass.disable.call(this);
23946 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23947 _this.getActionEl().addClass(this.disabledClass);
23948 e.dom.disabled = true;
23952 this.disabled = true;
23953 this.fireEvent("disable", this);
23957 enable : function()
23959 if(this.inputType != 'radio'){
23960 Roo.bootstrap.CheckBox.superclass.enable.call(this);
23967 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23968 _this.getActionEl().removeClass(this.disabledClass);
23969 e.dom.disabled = false;
23973 this.disabled = false;
23974 this.fireEvent("enable", this);
23978 setBoxLabel : function(v)
23983 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23989 Roo.apply(Roo.bootstrap.CheckBox, {
23994 * register a CheckBox Group
23995 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23997 register : function(checkbox)
23999 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
24000 this.groups[checkbox.groupId] = {};
24003 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
24007 this.groups[checkbox.groupId][checkbox.name] = checkbox;
24011 * fetch a CheckBox Group based on the group ID
24012 * @param {string} the group ID
24013 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
24015 get: function(groupId) {
24016 if (typeof(this.groups[groupId]) == 'undefined') {
24020 return this.groups[groupId] ;
24033 * @class Roo.bootstrap.Radio
24034 * @extends Roo.bootstrap.Component
24035 * Bootstrap Radio class
24036 * @cfg {String} boxLabel - the label associated
24037 * @cfg {String} value - the value of radio
24040 * Create a new Radio
24041 * @param {Object} config The config object
24043 Roo.bootstrap.Radio = function(config){
24044 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
24048 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
24054 getAutoCreate : function()
24058 cls : 'form-group radio',
24063 html : this.boxLabel
24071 initEvents : function()
24073 this.parent().register(this);
24075 this.el.on('click', this.onClick, this);
24079 onClick : function(e)
24081 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
24082 this.setChecked(true);
24086 setChecked : function(state, suppressEvent)
24088 this.parent().setValue(this.value, suppressEvent);
24092 setBoxLabel : function(v)
24097 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24112 * @class Roo.bootstrap.SecurePass
24113 * @extends Roo.bootstrap.Input
24114 * Bootstrap SecurePass class
24118 * Create a new SecurePass
24119 * @param {Object} config The config object
24122 Roo.bootstrap.SecurePass = function (config) {
24123 // these go here, so the translation tool can replace them..
24125 PwdEmpty: "Please type a password, and then retype it to confirm.",
24126 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24127 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24128 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24129 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24130 FNInPwd: "Your password can't contain your first name. Please type a different password.",
24131 LNInPwd: "Your password can't contain your last name. Please type a different password.",
24132 TooWeak: "Your password is Too Weak."
24134 this.meterLabel = "Password strength:";
24135 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
24136 this.meterClass = [
24137 "roo-password-meter-tooweak",
24138 "roo-password-meter-weak",
24139 "roo-password-meter-medium",
24140 "roo-password-meter-strong",
24141 "roo-password-meter-grey"
24146 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
24149 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
24151 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
24153 * PwdEmpty: "Please type a password, and then retype it to confirm.",
24154 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24155 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24156 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24157 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24158 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
24159 * LNInPwd: "Your password can't contain your last name. Please type a different password."
24169 * @cfg {String/Object} Label for the strength meter (defaults to
24170 * 'Password strength:')
24175 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
24176 * ['Weak', 'Medium', 'Strong'])
24179 pwdStrengths: false,
24192 initEvents: function ()
24194 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
24196 if (this.el.is('input[type=password]') && Roo.isSafari) {
24197 this.el.on('keydown', this.SafariOnKeyDown, this);
24200 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
24203 onRender: function (ct, position)
24205 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
24206 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
24207 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
24209 this.trigger.createChild({
24214 cls: 'roo-password-meter-grey col-xs-12',
24217 //width: this.meterWidth + 'px'
24221 cls: 'roo-password-meter-text'
24227 if (this.hideTrigger) {
24228 this.trigger.setDisplayed(false);
24230 this.setSize(this.width || '', this.height || '');
24233 onDestroy: function ()
24235 if (this.trigger) {
24236 this.trigger.removeAllListeners();
24237 this.trigger.remove();
24240 this.wrap.remove();
24242 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
24245 checkStrength: function ()
24247 var pwd = this.inputEl().getValue();
24248 if (pwd == this._lastPwd) {
24253 if (this.ClientSideStrongPassword(pwd)) {
24255 } else if (this.ClientSideMediumPassword(pwd)) {
24257 } else if (this.ClientSideWeakPassword(pwd)) {
24263 Roo.log('strength1: ' + strength);
24265 //var pm = this.trigger.child('div/div/div').dom;
24266 var pm = this.trigger.child('div/div');
24267 pm.removeClass(this.meterClass);
24268 pm.addClass(this.meterClass[strength]);
24271 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24273 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24275 this._lastPwd = pwd;
24279 Roo.bootstrap.SecurePass.superclass.reset.call(this);
24281 this._lastPwd = '';
24283 var pm = this.trigger.child('div/div');
24284 pm.removeClass(this.meterClass);
24285 pm.addClass('roo-password-meter-grey');
24288 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24291 this.inputEl().dom.type='password';
24294 validateValue: function (value)
24296 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
24299 if (value.length == 0) {
24300 if (this.allowBlank) {
24301 this.clearInvalid();
24305 this.markInvalid(this.errors.PwdEmpty);
24306 this.errorMsg = this.errors.PwdEmpty;
24314 if (!value.match(/[\x21-\x7e]+/)) {
24315 this.markInvalid(this.errors.PwdBadChar);
24316 this.errorMsg = this.errors.PwdBadChar;
24319 if (value.length < 6) {
24320 this.markInvalid(this.errors.PwdShort);
24321 this.errorMsg = this.errors.PwdShort;
24324 if (value.length > 16) {
24325 this.markInvalid(this.errors.PwdLong);
24326 this.errorMsg = this.errors.PwdLong;
24330 if (this.ClientSideStrongPassword(value)) {
24332 } else if (this.ClientSideMediumPassword(value)) {
24334 } else if (this.ClientSideWeakPassword(value)) {
24341 if (strength < 2) {
24342 //this.markInvalid(this.errors.TooWeak);
24343 this.errorMsg = this.errors.TooWeak;
24348 console.log('strength2: ' + strength);
24350 //var pm = this.trigger.child('div/div/div').dom;
24352 var pm = this.trigger.child('div/div');
24353 pm.removeClass(this.meterClass);
24354 pm.addClass(this.meterClass[strength]);
24356 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24358 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24360 this.errorMsg = '';
24364 CharacterSetChecks: function (type)
24367 this.fResult = false;
24370 isctype: function (character, type)
24373 case this.kCapitalLetter:
24374 if (character >= 'A' && character <= 'Z') {
24379 case this.kSmallLetter:
24380 if (character >= 'a' && character <= 'z') {
24386 if (character >= '0' && character <= '9') {
24391 case this.kPunctuation:
24392 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24403 IsLongEnough: function (pwd, size)
24405 return !(pwd == null || isNaN(size) || pwd.length < size);
24408 SpansEnoughCharacterSets: function (word, nb)
24410 if (!this.IsLongEnough(word, nb))
24415 var characterSetChecks = new Array(
24416 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24417 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24420 for (var index = 0; index < word.length; ++index) {
24421 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24422 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24423 characterSetChecks[nCharSet].fResult = true;
24430 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24431 if (characterSetChecks[nCharSet].fResult) {
24436 if (nCharSets < nb) {
24442 ClientSideStrongPassword: function (pwd)
24444 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24447 ClientSideMediumPassword: function (pwd)
24449 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24452 ClientSideWeakPassword: function (pwd)
24454 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24457 })//<script type="text/javascript">
24460 * Based Ext JS Library 1.1.1
24461 * Copyright(c) 2006-2007, Ext JS, LLC.
24467 * @class Roo.HtmlEditorCore
24468 * @extends Roo.Component
24469 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24471 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24474 Roo.HtmlEditorCore = function(config){
24477 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24482 * @event initialize
24483 * Fires when the editor is fully initialized (including the iframe)
24484 * @param {Roo.HtmlEditorCore} this
24489 * Fires when the editor is first receives the focus. Any insertion must wait
24490 * until after this event.
24491 * @param {Roo.HtmlEditorCore} this
24495 * @event beforesync
24496 * Fires before the textarea is updated with content from the editor iframe. Return false
24497 * to cancel the sync.
24498 * @param {Roo.HtmlEditorCore} this
24499 * @param {String} html
24503 * @event beforepush
24504 * Fires before the iframe editor is updated with content from the textarea. Return false
24505 * to cancel the push.
24506 * @param {Roo.HtmlEditorCore} this
24507 * @param {String} html
24512 * Fires when the textarea is updated with content from the editor iframe.
24513 * @param {Roo.HtmlEditorCore} this
24514 * @param {String} html
24519 * Fires when the iframe editor is updated with content from the textarea.
24520 * @param {Roo.HtmlEditorCore} this
24521 * @param {String} html
24526 * @event editorevent
24527 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24528 * @param {Roo.HtmlEditorCore} this
24534 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24536 // defaults : white / black...
24537 this.applyBlacklists();
24544 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
24548 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
24554 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
24559 * @cfg {Number} height (in pixels)
24563 * @cfg {Number} width (in pixels)
24568 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24571 stylesheets: false,
24576 // private properties
24577 validationEvent : false,
24579 initialized : false,
24581 sourceEditMode : false,
24582 onFocus : Roo.emptyFn,
24584 hideMode:'offsets',
24588 // blacklist + whitelisted elements..
24595 * Protected method that will not generally be called directly. It
24596 * is called when the editor initializes the iframe with HTML contents. Override this method if you
24597 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24599 getDocMarkup : function(){
24603 // inherit styels from page...??
24604 if (this.stylesheets === false) {
24606 Roo.get(document.head).select('style').each(function(node) {
24607 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24610 Roo.get(document.head).select('link').each(function(node) {
24611 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24614 } else if (!this.stylesheets.length) {
24616 st = '<style type="text/css">' +
24617 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24620 for (var i in this.stylesheets) {
24621 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24626 st += '<style type="text/css">' +
24627 'IMG { cursor: pointer } ' +
24630 var cls = 'roo-htmleditor-body';
24632 if(this.bodyCls.length){
24633 cls += ' ' + this.bodyCls;
24636 return '<html><head>' + st +
24637 //<style type="text/css">' +
24638 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24640 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
24644 onRender : function(ct, position)
24647 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24648 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24651 this.el.dom.style.border = '0 none';
24652 this.el.dom.setAttribute('tabIndex', -1);
24653 this.el.addClass('x-hidden hide');
24657 if(Roo.isIE){ // fix IE 1px bogus margin
24658 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24662 this.frameId = Roo.id();
24666 var iframe = this.owner.wrap.createChild({
24668 cls: 'form-control', // bootstrap..
24670 name: this.frameId,
24671 frameBorder : 'no',
24672 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
24677 this.iframe = iframe.dom;
24679 this.assignDocWin();
24681 this.doc.designMode = 'on';
24684 this.doc.write(this.getDocMarkup());
24688 var task = { // must defer to wait for browser to be ready
24690 //console.log("run task?" + this.doc.readyState);
24691 this.assignDocWin();
24692 if(this.doc.body || this.doc.readyState == 'complete'){
24694 this.doc.designMode="on";
24698 Roo.TaskMgr.stop(task);
24699 this.initEditor.defer(10, this);
24706 Roo.TaskMgr.start(task);
24711 onResize : function(w, h)
24713 Roo.log('resize: ' +w + ',' + h );
24714 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24718 if(typeof w == 'number'){
24720 this.iframe.style.width = w + 'px';
24722 if(typeof h == 'number'){
24724 this.iframe.style.height = h + 'px';
24726 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24733 * Toggles the editor between standard and source edit mode.
24734 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24736 toggleSourceEdit : function(sourceEditMode){
24738 this.sourceEditMode = sourceEditMode === true;
24740 if(this.sourceEditMode){
24742 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
24745 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24746 //this.iframe.className = '';
24749 //this.setSize(this.owner.wrap.getSize());
24750 //this.fireEvent('editmodechange', this, this.sourceEditMode);
24757 * Protected method that will not generally be called directly. If you need/want
24758 * custom HTML cleanup, this is the method you should override.
24759 * @param {String} html The HTML to be cleaned
24760 * return {String} The cleaned HTML
24762 cleanHtml : function(html){
24763 html = String(html);
24764 if(html.length > 5){
24765 if(Roo.isSafari){ // strip safari nonsense
24766 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24769 if(html == ' '){
24776 * HTML Editor -> Textarea
24777 * Protected method that will not generally be called directly. Syncs the contents
24778 * of the editor iframe with the textarea.
24780 syncValue : function(){
24781 if(this.initialized){
24782 var bd = (this.doc.body || this.doc.documentElement);
24783 //this.cleanUpPaste(); -- this is done else where and causes havoc..
24784 var html = bd.innerHTML;
24786 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24787 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24789 html = '<div style="'+m[0]+'">' + html + '</div>';
24792 html = this.cleanHtml(html);
24793 // fix up the special chars.. normaly like back quotes in word...
24794 // however we do not want to do this with chinese..
24795 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24797 var cc = match.charCodeAt();
24799 // Get the character value, handling surrogate pairs
24800 if (match.length == 2) {
24801 // It's a surrogate pair, calculate the Unicode code point
24802 var high = match.charCodeAt(0) - 0xD800;
24803 var low = match.charCodeAt(1) - 0xDC00;
24804 cc = (high * 0x400) + low + 0x10000;
24806 (cc >= 0x4E00 && cc < 0xA000 ) ||
24807 (cc >= 0x3400 && cc < 0x4E00 ) ||
24808 (cc >= 0xf900 && cc < 0xfb00 )
24813 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24814 return "&#" + cc + ";";
24821 if(this.owner.fireEvent('beforesync', this, html) !== false){
24822 this.el.dom.value = html;
24823 this.owner.fireEvent('sync', this, html);
24829 * Protected method that will not generally be called directly. Pushes the value of the textarea
24830 * into the iframe editor.
24832 pushValue : function(){
24833 if(this.initialized){
24834 var v = this.el.dom.value.trim();
24836 // if(v.length < 1){
24840 if(this.owner.fireEvent('beforepush', this, v) !== false){
24841 var d = (this.doc.body || this.doc.documentElement);
24843 this.cleanUpPaste();
24844 this.el.dom.value = d.innerHTML;
24845 this.owner.fireEvent('push', this, v);
24851 deferFocus : function(){
24852 this.focus.defer(10, this);
24856 focus : function(){
24857 if(this.win && !this.sourceEditMode){
24864 assignDocWin: function()
24866 var iframe = this.iframe;
24869 this.doc = iframe.contentWindow.document;
24870 this.win = iframe.contentWindow;
24872 // if (!Roo.get(this.frameId)) {
24875 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24876 // this.win = Roo.get(this.frameId).dom.contentWindow;
24878 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24882 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24883 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24888 initEditor : function(){
24889 //console.log("INIT EDITOR");
24890 this.assignDocWin();
24894 this.doc.designMode="on";
24896 this.doc.write(this.getDocMarkup());
24899 var dbody = (this.doc.body || this.doc.documentElement);
24900 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24901 // this copies styles from the containing element into thsi one..
24902 // not sure why we need all of this..
24903 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24905 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24906 //ss['background-attachment'] = 'fixed'; // w3c
24907 dbody.bgProperties = 'fixed'; // ie
24908 //Roo.DomHelper.applyStyles(dbody, ss);
24909 Roo.EventManager.on(this.doc, {
24910 //'mousedown': this.onEditorEvent,
24911 'mouseup': this.onEditorEvent,
24912 'dblclick': this.onEditorEvent,
24913 'click': this.onEditorEvent,
24914 'keyup': this.onEditorEvent,
24919 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24921 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24922 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24924 this.initialized = true;
24926 this.owner.fireEvent('initialize', this);
24931 onDestroy : function(){
24937 //for (var i =0; i < this.toolbars.length;i++) {
24938 // // fixme - ask toolbars for heights?
24939 // this.toolbars[i].onDestroy();
24942 //this.wrap.dom.innerHTML = '';
24943 //this.wrap.remove();
24948 onFirstFocus : function(){
24950 this.assignDocWin();
24953 this.activated = true;
24956 if(Roo.isGecko){ // prevent silly gecko errors
24958 var s = this.win.getSelection();
24959 if(!s.focusNode || s.focusNode.nodeType != 3){
24960 var r = s.getRangeAt(0);
24961 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24966 this.execCmd('useCSS', true);
24967 this.execCmd('styleWithCSS', false);
24970 this.owner.fireEvent('activate', this);
24974 adjustFont: function(btn){
24975 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24976 //if(Roo.isSafari){ // safari
24979 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24980 if(Roo.isSafari){ // safari
24981 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24982 v = (v < 10) ? 10 : v;
24983 v = (v > 48) ? 48 : v;
24984 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24989 v = Math.max(1, v+adjust);
24991 this.execCmd('FontSize', v );
24994 onEditorEvent : function(e)
24996 this.owner.fireEvent('editorevent', this, e);
24997 // this.updateToolbar();
24998 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25001 insertTag : function(tg)
25003 // could be a bit smarter... -> wrap the current selected tRoo..
25004 if (tg.toLowerCase() == 'span' ||
25005 tg.toLowerCase() == 'code' ||
25006 tg.toLowerCase() == 'sup' ||
25007 tg.toLowerCase() == 'sub'
25010 range = this.createRange(this.getSelection());
25011 var wrappingNode = this.doc.createElement(tg.toLowerCase());
25012 wrappingNode.appendChild(range.extractContents());
25013 range.insertNode(wrappingNode);
25020 this.execCmd("formatblock", tg);
25024 insertText : function(txt)
25028 var range = this.createRange();
25029 range.deleteContents();
25030 //alert(Sender.getAttribute('label'));
25032 range.insertNode(this.doc.createTextNode(txt));
25038 * Executes a Midas editor command on the editor document and performs necessary focus and
25039 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25040 * @param {String} cmd The Midas command
25041 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25043 relayCmd : function(cmd, value){
25045 this.execCmd(cmd, value);
25046 this.owner.fireEvent('editorevent', this);
25047 //this.updateToolbar();
25048 this.owner.deferFocus();
25052 * Executes a Midas editor command directly on the editor document.
25053 * For visual commands, you should use {@link #relayCmd} instead.
25054 * <b>This should only be called after the editor is initialized.</b>
25055 * @param {String} cmd The Midas command
25056 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25058 execCmd : function(cmd, value){
25059 this.doc.execCommand(cmd, false, value === undefined ? null : value);
25066 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25068 * @param {String} text | dom node..
25070 insertAtCursor : function(text)
25073 if(!this.activated){
25079 var r = this.doc.selection.createRange();
25090 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25094 // from jquery ui (MIT licenced)
25096 var win = this.win;
25098 if (win.getSelection && win.getSelection().getRangeAt) {
25099 range = win.getSelection().getRangeAt(0);
25100 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25101 range.insertNode(node);
25102 } else if (win.document.selection && win.document.selection.createRange) {
25103 // no firefox support
25104 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25105 win.document.selection.createRange().pasteHTML(txt);
25107 // no firefox support
25108 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25109 this.execCmd('InsertHTML', txt);
25118 mozKeyPress : function(e){
25120 var c = e.getCharCode(), cmd;
25123 c = String.fromCharCode(c).toLowerCase();
25137 this.cleanUpPaste.defer(100, this);
25145 e.preventDefault();
25153 fixKeys : function(){ // load time branching for fastest keydown performance
25155 return function(e){
25156 var k = e.getKey(), r;
25159 r = this.doc.selection.createRange();
25162 r.pasteHTML('    ');
25169 r = this.doc.selection.createRange();
25171 var target = r.parentElement();
25172 if(!target || target.tagName.toLowerCase() != 'li'){
25174 r.pasteHTML('<br />');
25180 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25181 this.cleanUpPaste.defer(100, this);
25187 }else if(Roo.isOpera){
25188 return function(e){
25189 var k = e.getKey();
25193 this.execCmd('InsertHTML','    ');
25196 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25197 this.cleanUpPaste.defer(100, this);
25202 }else if(Roo.isSafari){
25203 return function(e){
25204 var k = e.getKey();
25208 this.execCmd('InsertText','\t');
25212 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25213 this.cleanUpPaste.defer(100, this);
25221 getAllAncestors: function()
25223 var p = this.getSelectedNode();
25226 a.push(p); // push blank onto stack..
25227 p = this.getParentElement();
25231 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25235 a.push(this.doc.body);
25239 lastSelNode : false,
25242 getSelection : function()
25244 this.assignDocWin();
25245 return Roo.isIE ? this.doc.selection : this.win.getSelection();
25248 getSelectedNode: function()
25250 // this may only work on Gecko!!!
25252 // should we cache this!!!!
25257 var range = this.createRange(this.getSelection()).cloneRange();
25260 var parent = range.parentElement();
25262 var testRange = range.duplicate();
25263 testRange.moveToElementText(parent);
25264 if (testRange.inRange(range)) {
25267 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25270 parent = parent.parentElement;
25275 // is ancestor a text element.
25276 var ac = range.commonAncestorContainer;
25277 if (ac.nodeType == 3) {
25278 ac = ac.parentNode;
25281 var ar = ac.childNodes;
25284 var other_nodes = [];
25285 var has_other_nodes = false;
25286 for (var i=0;i<ar.length;i++) {
25287 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
25290 // fullly contained node.
25292 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25297 // probably selected..
25298 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25299 other_nodes.push(ar[i]);
25303 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
25308 has_other_nodes = true;
25310 if (!nodes.length && other_nodes.length) {
25311 nodes= other_nodes;
25313 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25319 createRange: function(sel)
25321 // this has strange effects when using with
25322 // top toolbar - not sure if it's a great idea.
25323 //this.editor.contentWindow.focus();
25324 if (typeof sel != "undefined") {
25326 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25328 return this.doc.createRange();
25331 return this.doc.createRange();
25334 getParentElement: function()
25337 this.assignDocWin();
25338 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25340 var range = this.createRange(sel);
25343 var p = range.commonAncestorContainer;
25344 while (p.nodeType == 3) { // text node
25355 * Range intersection.. the hard stuff...
25359 * [ -- selected range --- ]
25363 * if end is before start or hits it. fail.
25364 * if start is after end or hits it fail.
25366 * if either hits (but other is outside. - then it's not
25372 // @see http://www.thismuchiknow.co.uk/?p=64.
25373 rangeIntersectsNode : function(range, node)
25375 var nodeRange = node.ownerDocument.createRange();
25377 nodeRange.selectNode(node);
25379 nodeRange.selectNodeContents(node);
25382 var rangeStartRange = range.cloneRange();
25383 rangeStartRange.collapse(true);
25385 var rangeEndRange = range.cloneRange();
25386 rangeEndRange.collapse(false);
25388 var nodeStartRange = nodeRange.cloneRange();
25389 nodeStartRange.collapse(true);
25391 var nodeEndRange = nodeRange.cloneRange();
25392 nodeEndRange.collapse(false);
25394 return rangeStartRange.compareBoundaryPoints(
25395 Range.START_TO_START, nodeEndRange) == -1 &&
25396 rangeEndRange.compareBoundaryPoints(
25397 Range.START_TO_START, nodeStartRange) == 1;
25401 rangeCompareNode : function(range, node)
25403 var nodeRange = node.ownerDocument.createRange();
25405 nodeRange.selectNode(node);
25407 nodeRange.selectNodeContents(node);
25411 range.collapse(true);
25413 nodeRange.collapse(true);
25415 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25416 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
25418 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25420 var nodeIsBefore = ss == 1;
25421 var nodeIsAfter = ee == -1;
25423 if (nodeIsBefore && nodeIsAfter) {
25426 if (!nodeIsBefore && nodeIsAfter) {
25427 return 1; //right trailed.
25430 if (nodeIsBefore && !nodeIsAfter) {
25431 return 2; // left trailed.
25437 // private? - in a new class?
25438 cleanUpPaste : function()
25440 // cleans up the whole document..
25441 Roo.log('cleanuppaste');
25443 this.cleanUpChildren(this.doc.body);
25444 var clean = this.cleanWordChars(this.doc.body.innerHTML);
25445 if (clean != this.doc.body.innerHTML) {
25446 this.doc.body.innerHTML = clean;
25451 cleanWordChars : function(input) {// change the chars to hex code
25452 var he = Roo.HtmlEditorCore;
25454 var output = input;
25455 Roo.each(he.swapCodes, function(sw) {
25456 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25458 output = output.replace(swapper, sw[1]);
25465 cleanUpChildren : function (n)
25467 if (!n.childNodes.length) {
25470 for (var i = n.childNodes.length-1; i > -1 ; i--) {
25471 this.cleanUpChild(n.childNodes[i]);
25478 cleanUpChild : function (node)
25481 //console.log(node);
25482 if (node.nodeName == "#text") {
25483 // clean up silly Windows -- stuff?
25486 if (node.nodeName == "#comment") {
25487 node.parentNode.removeChild(node);
25488 // clean up silly Windows -- stuff?
25491 var lcname = node.tagName.toLowerCase();
25492 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25493 // whitelist of tags..
25495 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25497 node.parentNode.removeChild(node);
25502 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25504 // spans with no attributes - just remove them..
25505 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
25506 remove_keep_children = true;
25509 // remove <a name=....> as rendering on yahoo mailer is borked with this.
25510 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25512 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25513 // remove_keep_children = true;
25516 if (remove_keep_children) {
25517 this.cleanUpChildren(node);
25518 // inserts everything just before this node...
25519 while (node.childNodes.length) {
25520 var cn = node.childNodes[0];
25521 node.removeChild(cn);
25522 node.parentNode.insertBefore(cn, node);
25524 node.parentNode.removeChild(node);
25528 if (!node.attributes || !node.attributes.length) {
25533 this.cleanUpChildren(node);
25537 function cleanAttr(n,v)
25540 if (v.match(/^\./) || v.match(/^\//)) {
25543 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25546 if (v.match(/^#/)) {
25549 if (v.match(/^\{/)) { // allow template editing.
25552 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25553 node.removeAttribute(n);
25557 var cwhite = this.cwhite;
25558 var cblack = this.cblack;
25560 function cleanStyle(n,v)
25562 if (v.match(/expression/)) { //XSS?? should we even bother..
25563 node.removeAttribute(n);
25567 var parts = v.split(/;/);
25570 Roo.each(parts, function(p) {
25571 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25575 var l = p.split(':').shift().replace(/\s+/g,'');
25576 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25578 if ( cwhite.length && cblack.indexOf(l) > -1) {
25579 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25580 //node.removeAttribute(n);
25584 // only allow 'c whitelisted system attributes'
25585 if ( cwhite.length && cwhite.indexOf(l) < 0) {
25586 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25587 //node.removeAttribute(n);
25597 if (clean.length) {
25598 node.setAttribute(n, clean.join(';'));
25600 node.removeAttribute(n);
25606 for (var i = node.attributes.length-1; i > -1 ; i--) {
25607 var a = node.attributes[i];
25610 if (a.name.toLowerCase().substr(0,2)=='on') {
25611 node.removeAttribute(a.name);
25614 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25615 node.removeAttribute(a.name);
25618 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25619 cleanAttr(a.name,a.value); // fixme..
25622 if (a.name == 'style') {
25623 cleanStyle(a.name,a.value);
25626 /// clean up MS crap..
25627 // tecnically this should be a list of valid class'es..
25630 if (a.name == 'class') {
25631 if (a.value.match(/^Mso/)) {
25632 node.removeAttribute('class');
25635 if (a.value.match(/^body$/)) {
25636 node.removeAttribute('class');
25647 this.cleanUpChildren(node);
25653 * Clean up MS wordisms...
25655 cleanWord : function(node)
25658 this.cleanWord(this.doc.body);
25663 node.nodeName == 'SPAN' &&
25664 !node.hasAttributes() &&
25665 node.childNodes.length == 1 &&
25666 node.firstChild.nodeName == "#text"
25668 var textNode = node.firstChild;
25669 node.removeChild(textNode);
25670 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25671 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25673 node.parentNode.insertBefore(textNode, node);
25674 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25675 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25677 node.parentNode.removeChild(node);
25680 if (node.nodeName == "#text") {
25681 // clean up silly Windows -- stuff?
25684 if (node.nodeName == "#comment") {
25685 node.parentNode.removeChild(node);
25686 // clean up silly Windows -- stuff?
25690 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25691 node.parentNode.removeChild(node);
25694 //Roo.log(node.tagName);
25695 // remove - but keep children..
25696 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25697 //Roo.log('-- removed');
25698 while (node.childNodes.length) {
25699 var cn = node.childNodes[0];
25700 node.removeChild(cn);
25701 node.parentNode.insertBefore(cn, node);
25702 // move node to parent - and clean it..
25703 this.cleanWord(cn);
25705 node.parentNode.removeChild(node);
25706 /// no need to iterate chidlren = it's got none..
25707 //this.iterateChildren(node, this.cleanWord);
25711 if (node.className.length) {
25713 var cn = node.className.split(/\W+/);
25715 Roo.each(cn, function(cls) {
25716 if (cls.match(/Mso[a-zA-Z]+/)) {
25721 node.className = cna.length ? cna.join(' ') : '';
25723 node.removeAttribute("class");
25727 if (node.hasAttribute("lang")) {
25728 node.removeAttribute("lang");
25731 if (node.hasAttribute("style")) {
25733 var styles = node.getAttribute("style").split(";");
25735 Roo.each(styles, function(s) {
25736 if (!s.match(/:/)) {
25739 var kv = s.split(":");
25740 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25743 // what ever is left... we allow.
25746 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25747 if (!nstyle.length) {
25748 node.removeAttribute('style');
25751 this.iterateChildren(node, this.cleanWord);
25757 * iterateChildren of a Node, calling fn each time, using this as the scole..
25758 * @param {DomNode} node node to iterate children of.
25759 * @param {Function} fn method of this class to call on each item.
25761 iterateChildren : function(node, fn)
25763 if (!node.childNodes.length) {
25766 for (var i = node.childNodes.length-1; i > -1 ; i--) {
25767 fn.call(this, node.childNodes[i])
25773 * cleanTableWidths.
25775 * Quite often pasting from word etc.. results in tables with column and widths.
25776 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25779 cleanTableWidths : function(node)
25784 this.cleanTableWidths(this.doc.body);
25789 if (node.nodeName == "#text" || node.nodeName == "#comment") {
25792 Roo.log(node.tagName);
25793 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25794 this.iterateChildren(node, this.cleanTableWidths);
25797 if (node.hasAttribute('width')) {
25798 node.removeAttribute('width');
25802 if (node.hasAttribute("style")) {
25805 var styles = node.getAttribute("style").split(";");
25807 Roo.each(styles, function(s) {
25808 if (!s.match(/:/)) {
25811 var kv = s.split(":");
25812 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25815 // what ever is left... we allow.
25818 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25819 if (!nstyle.length) {
25820 node.removeAttribute('style');
25824 this.iterateChildren(node, this.cleanTableWidths);
25832 domToHTML : function(currentElement, depth, nopadtext) {
25834 depth = depth || 0;
25835 nopadtext = nopadtext || false;
25837 if (!currentElement) {
25838 return this.domToHTML(this.doc.body);
25841 //Roo.log(currentElement);
25843 var allText = false;
25844 var nodeName = currentElement.nodeName;
25845 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25847 if (nodeName == '#text') {
25849 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25854 if (nodeName != 'BODY') {
25857 // Prints the node tagName, such as <A>, <IMG>, etc
25860 for(i = 0; i < currentElement.attributes.length;i++) {
25862 var aname = currentElement.attributes.item(i).name;
25863 if (!currentElement.attributes.item(i).value.length) {
25866 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25869 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25878 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25881 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25886 // Traverse the tree
25888 var currentElementChild = currentElement.childNodes.item(i);
25889 var allText = true;
25890 var innerHTML = '';
25892 while (currentElementChild) {
25893 // Formatting code (indent the tree so it looks nice on the screen)
25894 var nopad = nopadtext;
25895 if (lastnode == 'SPAN') {
25899 if (currentElementChild.nodeName == '#text') {
25900 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25901 toadd = nopadtext ? toadd : toadd.trim();
25902 if (!nopad && toadd.length > 80) {
25903 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
25905 innerHTML += toadd;
25908 currentElementChild = currentElement.childNodes.item(i);
25914 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
25916 // Recursively traverse the tree structure of the child node
25917 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
25918 lastnode = currentElementChild.nodeName;
25920 currentElementChild=currentElement.childNodes.item(i);
25926 // The remaining code is mostly for formatting the tree
25927 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
25932 ret+= "</"+tagName+">";
25938 applyBlacklists : function()
25940 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
25941 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
25945 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25946 if (b.indexOf(tag) > -1) {
25949 this.white.push(tag);
25953 Roo.each(w, function(tag) {
25954 if (b.indexOf(tag) > -1) {
25957 if (this.white.indexOf(tag) > -1) {
25960 this.white.push(tag);
25965 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25966 if (w.indexOf(tag) > -1) {
25969 this.black.push(tag);
25973 Roo.each(b, function(tag) {
25974 if (w.indexOf(tag) > -1) {
25977 if (this.black.indexOf(tag) > -1) {
25980 this.black.push(tag);
25985 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
25986 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
25990 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25991 if (b.indexOf(tag) > -1) {
25994 this.cwhite.push(tag);
25998 Roo.each(w, function(tag) {
25999 if (b.indexOf(tag) > -1) {
26002 if (this.cwhite.indexOf(tag) > -1) {
26005 this.cwhite.push(tag);
26010 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
26011 if (w.indexOf(tag) > -1) {
26014 this.cblack.push(tag);
26018 Roo.each(b, function(tag) {
26019 if (w.indexOf(tag) > -1) {
26022 if (this.cblack.indexOf(tag) > -1) {
26025 this.cblack.push(tag);
26030 setStylesheets : function(stylesheets)
26032 if(typeof(stylesheets) == 'string'){
26033 Roo.get(this.iframe.contentDocument.head).createChild({
26035 rel : 'stylesheet',
26044 Roo.each(stylesheets, function(s) {
26049 Roo.get(_this.iframe.contentDocument.head).createChild({
26051 rel : 'stylesheet',
26060 removeStylesheets : function()
26064 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
26069 setStyle : function(style)
26071 Roo.get(this.iframe.contentDocument.head).createChild({
26080 // hide stuff that is not compatible
26094 * @event specialkey
26098 * @cfg {String} fieldClass @hide
26101 * @cfg {String} focusClass @hide
26104 * @cfg {String} autoCreate @hide
26107 * @cfg {String} inputType @hide
26110 * @cfg {String} invalidClass @hide
26113 * @cfg {String} invalidText @hide
26116 * @cfg {String} msgFx @hide
26119 * @cfg {String} validateOnBlur @hide
26123 Roo.HtmlEditorCore.white = [
26124 'area', 'br', 'img', 'input', 'hr', 'wbr',
26126 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
26127 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
26128 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
26129 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
26130 'table', 'ul', 'xmp',
26132 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
26135 'dir', 'menu', 'ol', 'ul', 'dl',
26141 Roo.HtmlEditorCore.black = [
26142 // 'embed', 'object', // enable - backend responsiblity to clean thiese
26144 'base', 'basefont', 'bgsound', 'blink', 'body',
26145 'frame', 'frameset', 'head', 'html', 'ilayer',
26146 'iframe', 'layer', 'link', 'meta', 'object',
26147 'script', 'style' ,'title', 'xml' // clean later..
26149 Roo.HtmlEditorCore.clean = [
26150 'script', 'style', 'title', 'xml'
26152 Roo.HtmlEditorCore.remove = [
26157 Roo.HtmlEditorCore.ablack = [
26161 Roo.HtmlEditorCore.aclean = [
26162 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
26166 Roo.HtmlEditorCore.pwhite= [
26167 'http', 'https', 'mailto'
26170 // white listed style attributes.
26171 Roo.HtmlEditorCore.cwhite= [
26172 // 'text-align', /// default is to allow most things..
26178 // black listed style attributes.
26179 Roo.HtmlEditorCore.cblack= [
26180 // 'font-size' -- this can be set by the project
26184 Roo.HtmlEditorCore.swapCodes =[
26185 [ 8211, "–" ],
26186 [ 8212, "—" ],
26203 * @class Roo.bootstrap.HtmlEditor
26204 * @extends Roo.bootstrap.TextArea
26205 * Bootstrap HtmlEditor class
26208 * Create a new HtmlEditor
26209 * @param {Object} config The config object
26212 Roo.bootstrap.HtmlEditor = function(config){
26213 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
26214 if (!this.toolbars) {
26215 this.toolbars = [];
26218 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26221 * @event initialize
26222 * Fires when the editor is fully initialized (including the iframe)
26223 * @param {HtmlEditor} this
26228 * Fires when the editor is first receives the focus. Any insertion must wait
26229 * until after this event.
26230 * @param {HtmlEditor} this
26234 * @event beforesync
26235 * Fires before the textarea is updated with content from the editor iframe. Return false
26236 * to cancel the sync.
26237 * @param {HtmlEditor} this
26238 * @param {String} html
26242 * @event beforepush
26243 * Fires before the iframe editor is updated with content from the textarea. Return false
26244 * to cancel the push.
26245 * @param {HtmlEditor} this
26246 * @param {String} html
26251 * Fires when the textarea is updated with content from the editor iframe.
26252 * @param {HtmlEditor} this
26253 * @param {String} html
26258 * Fires when the iframe editor is updated with content from the textarea.
26259 * @param {HtmlEditor} this
26260 * @param {String} html
26264 * @event editmodechange
26265 * Fires when the editor switches edit modes
26266 * @param {HtmlEditor} this
26267 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26269 editmodechange: true,
26271 * @event editorevent
26272 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26273 * @param {HtmlEditor} this
26277 * @event firstfocus
26278 * Fires when on first focus - needed by toolbars..
26279 * @param {HtmlEditor} this
26284 * Auto save the htmlEditor value as a file into Events
26285 * @param {HtmlEditor} this
26289 * @event savedpreview
26290 * preview the saved version of htmlEditor
26291 * @param {HtmlEditor} this
26298 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
26302 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26307 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
26312 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
26317 * @cfg {Number} height (in pixels)
26321 * @cfg {Number} width (in pixels)
26326 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26329 stylesheets: false,
26334 // private properties
26335 validationEvent : false,
26337 initialized : false,
26340 onFocus : Roo.emptyFn,
26342 hideMode:'offsets',
26344 tbContainer : false,
26348 toolbarContainer :function() {
26349 return this.wrap.select('.x-html-editor-tb',true).first();
26353 * Protected method that will not generally be called directly. It
26354 * is called when the editor creates its toolbar. Override this method if you need to
26355 * add custom toolbar buttons.
26356 * @param {HtmlEditor} editor
26358 createToolbar : function(){
26359 Roo.log('renewing');
26360 Roo.log("create toolbars");
26362 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26363 this.toolbars[0].render(this.toolbarContainer());
26367 // if (!editor.toolbars || !editor.toolbars.length) {
26368 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26371 // for (var i =0 ; i < editor.toolbars.length;i++) {
26372 // editor.toolbars[i] = Roo.factory(
26373 // typeof(editor.toolbars[i]) == 'string' ?
26374 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
26375 // Roo.bootstrap.HtmlEditor);
26376 // editor.toolbars[i].init(editor);
26382 onRender : function(ct, position)
26384 // Roo.log("Call onRender: " + this.xtype);
26386 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26388 this.wrap = this.inputEl().wrap({
26389 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26392 this.editorcore.onRender(ct, position);
26394 if (this.resizable) {
26395 this.resizeEl = new Roo.Resizable(this.wrap, {
26399 minHeight : this.height,
26400 height: this.height,
26401 handles : this.resizable,
26404 resize : function(r, w, h) {
26405 _t.onResize(w,h); // -something
26411 this.createToolbar(this);
26414 if(!this.width && this.resizable){
26415 this.setSize(this.wrap.getSize());
26417 if (this.resizeEl) {
26418 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26419 // should trigger onReize..
26425 onResize : function(w, h)
26427 Roo.log('resize: ' +w + ',' + h );
26428 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26432 if(this.inputEl() ){
26433 if(typeof w == 'number'){
26434 var aw = w - this.wrap.getFrameWidth('lr');
26435 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26438 if(typeof h == 'number'){
26439 var tbh = -11; // fixme it needs to tool bar size!
26440 for (var i =0; i < this.toolbars.length;i++) {
26441 // fixme - ask toolbars for heights?
26442 tbh += this.toolbars[i].el.getHeight();
26443 //if (this.toolbars[i].footer) {
26444 // tbh += this.toolbars[i].footer.el.getHeight();
26452 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26453 ah -= 5; // knock a few pixes off for look..
26454 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26458 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26459 this.editorcore.onResize(ew,eh);
26464 * Toggles the editor between standard and source edit mode.
26465 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26467 toggleSourceEdit : function(sourceEditMode)
26469 this.editorcore.toggleSourceEdit(sourceEditMode);
26471 if(this.editorcore.sourceEditMode){
26472 Roo.log('editor - showing textarea');
26475 // Roo.log(this.syncValue());
26477 this.inputEl().removeClass(['hide', 'x-hidden']);
26478 this.inputEl().dom.removeAttribute('tabIndex');
26479 this.inputEl().focus();
26481 Roo.log('editor - hiding textarea');
26483 // Roo.log(this.pushValue());
26486 this.inputEl().addClass(['hide', 'x-hidden']);
26487 this.inputEl().dom.setAttribute('tabIndex', -1);
26488 //this.deferFocus();
26491 if(this.resizable){
26492 this.setSize(this.wrap.getSize());
26495 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26498 // private (for BoxComponent)
26499 adjustSize : Roo.BoxComponent.prototype.adjustSize,
26501 // private (for BoxComponent)
26502 getResizeEl : function(){
26506 // private (for BoxComponent)
26507 getPositionEl : function(){
26512 initEvents : function(){
26513 this.originalValue = this.getValue();
26517 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26520 // markInvalid : Roo.emptyFn,
26522 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26525 // clearInvalid : Roo.emptyFn,
26527 setValue : function(v){
26528 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26529 this.editorcore.pushValue();
26534 deferFocus : function(){
26535 this.focus.defer(10, this);
26539 focus : function(){
26540 this.editorcore.focus();
26546 onDestroy : function(){
26552 for (var i =0; i < this.toolbars.length;i++) {
26553 // fixme - ask toolbars for heights?
26554 this.toolbars[i].onDestroy();
26557 this.wrap.dom.innerHTML = '';
26558 this.wrap.remove();
26563 onFirstFocus : function(){
26564 //Roo.log("onFirstFocus");
26565 this.editorcore.onFirstFocus();
26566 for (var i =0; i < this.toolbars.length;i++) {
26567 this.toolbars[i].onFirstFocus();
26573 syncValue : function()
26575 this.editorcore.syncValue();
26578 pushValue : function()
26580 this.editorcore.pushValue();
26584 // hide stuff that is not compatible
26598 * @event specialkey
26602 * @cfg {String} fieldClass @hide
26605 * @cfg {String} focusClass @hide
26608 * @cfg {String} autoCreate @hide
26611 * @cfg {String} inputType @hide
26615 * @cfg {String} invalidText @hide
26618 * @cfg {String} msgFx @hide
26621 * @cfg {String} validateOnBlur @hide
26630 Roo.namespace('Roo.bootstrap.htmleditor');
26632 * @class Roo.bootstrap.HtmlEditorToolbar1
26638 new Roo.bootstrap.HtmlEditor({
26641 new Roo.bootstrap.HtmlEditorToolbar1({
26642 disable : { fonts: 1 , format: 1, ..., ... , ...],
26648 * @cfg {Object} disable List of elements to disable..
26649 * @cfg {Array} btns List of additional buttons.
26653 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26656 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26659 Roo.apply(this, config);
26661 // default disabled, based on 'good practice'..
26662 this.disable = this.disable || {};
26663 Roo.applyIf(this.disable, {
26666 specialElements : true
26668 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26670 this.editor = config.editor;
26671 this.editorcore = config.editor.editorcore;
26673 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26675 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26676 // dont call parent... till later.
26678 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
26683 editorcore : false,
26688 "h1","h2","h3","h4","h5","h6",
26690 "abbr", "acronym", "address", "cite", "samp", "var",
26694 onRender : function(ct, position)
26696 // Roo.log("Call onRender: " + this.xtype);
26698 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26700 this.el.dom.style.marginBottom = '0';
26702 var editorcore = this.editorcore;
26703 var editor= this.editor;
26706 var btn = function(id,cmd , toggle, handler, html){
26708 var event = toggle ? 'toggle' : 'click';
26713 xns: Roo.bootstrap,
26717 enableToggle:toggle !== false,
26719 pressed : toggle ? false : null,
26722 a.listeners[toggle ? 'toggle' : 'click'] = function() {
26723 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
26729 // var cb_box = function...
26734 xns: Roo.bootstrap,
26739 xns: Roo.bootstrap,
26743 Roo.each(this.formats, function(f) {
26744 style.menu.items.push({
26746 xns: Roo.bootstrap,
26747 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26752 editorcore.insertTag(this.tagname);
26759 children.push(style);
26761 btn('bold',false,true);
26762 btn('italic',false,true);
26763 btn('align-left', 'justifyleft',true);
26764 btn('align-center', 'justifycenter',true);
26765 btn('align-right' , 'justifyright',true);
26766 btn('link', false, false, function(btn) {
26767 //Roo.log("create link?");
26768 var url = prompt(this.createLinkText, this.defaultLinkValue);
26769 if(url && url != 'http:/'+'/'){
26770 this.editorcore.relayCmd('createlink', url);
26773 btn('list','insertunorderedlist',true);
26774 btn('pencil', false,true, function(btn){
26776 this.toggleSourceEdit(btn.pressed);
26779 if (this.editor.btns.length > 0) {
26780 for (var i = 0; i<this.editor.btns.length; i++) {
26781 children.push(this.editor.btns[i]);
26789 xns: Roo.bootstrap,
26794 xns: Roo.bootstrap,
26799 cog.menu.items.push({
26801 xns: Roo.bootstrap,
26802 html : Clean styles,
26807 editorcore.insertTag(this.tagname);
26816 this.xtype = 'NavSimplebar';
26818 for(var i=0;i< children.length;i++) {
26820 this.buttons.add(this.addxtypeChild(children[i]));
26824 editor.on('editorevent', this.updateToolbar, this);
26826 onBtnClick : function(id)
26828 this.editorcore.relayCmd(id);
26829 this.editorcore.focus();
26833 * Protected method that will not generally be called directly. It triggers
26834 * a toolbar update by reading the markup state of the current selection in the editor.
26836 updateToolbar: function(){
26838 if(!this.editorcore.activated){
26839 this.editor.onFirstFocus(); // is this neeed?
26843 var btns = this.buttons;
26844 var doc = this.editorcore.doc;
26845 btns.get('bold').setActive(doc.queryCommandState('bold'));
26846 btns.get('italic').setActive(doc.queryCommandState('italic'));
26847 //btns.get('underline').setActive(doc.queryCommandState('underline'));
26849 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26850 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26851 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26853 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26854 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26857 var ans = this.editorcore.getAllAncestors();
26858 if (this.formatCombo) {
26861 var store = this.formatCombo.store;
26862 this.formatCombo.setValue("");
26863 for (var i =0; i < ans.length;i++) {
26864 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26866 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26874 // hides menus... - so this cant be on a menu...
26875 Roo.bootstrap.MenuMgr.hideAll();
26877 Roo.bootstrap.MenuMgr.hideAll();
26878 //this.editorsyncValue();
26880 onFirstFocus: function() {
26881 this.buttons.each(function(item){
26885 toggleSourceEdit : function(sourceEditMode){
26888 if(sourceEditMode){
26889 Roo.log("disabling buttons");
26890 this.buttons.each( function(item){
26891 if(item.cmd != 'pencil'){
26897 Roo.log("enabling buttons");
26898 if(this.editorcore.initialized){
26899 this.buttons.each( function(item){
26905 Roo.log("calling toggole on editor");
26906 // tell the editor that it's been pressed..
26907 this.editor.toggleSourceEdit(sourceEditMode);
26921 * @class Roo.bootstrap.Markdown
26922 * @extends Roo.bootstrap.TextArea
26923 * Bootstrap Showdown editable area
26924 * @cfg {string} content
26927 * Create a new Showdown
26930 Roo.bootstrap.Markdown = function(config){
26931 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26935 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
26939 initEvents : function()
26942 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26943 this.markdownEl = this.el.createChild({
26944 cls : 'roo-markdown-area'
26946 this.inputEl().addClass('d-none');
26947 if (this.getValue() == '') {
26948 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26951 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26953 this.markdownEl.on('click', this.toggleTextEdit, this);
26954 this.on('blur', this.toggleTextEdit, this);
26955 this.on('specialkey', this.resizeTextArea, this);
26958 toggleTextEdit : function()
26960 var sh = this.markdownEl.getHeight();
26961 this.inputEl().addClass('d-none');
26962 this.markdownEl.addClass('d-none');
26963 if (!this.editing) {
26965 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26966 this.inputEl().removeClass('d-none');
26967 this.inputEl().focus();
26968 this.editing = true;
26971 // show showdown...
26972 this.updateMarkdown();
26973 this.markdownEl.removeClass('d-none');
26974 this.editing = false;
26977 updateMarkdown : function()
26979 if (this.getValue() == '') {
26980 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26984 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26987 resizeTextArea: function () {
26990 Roo.log([sh, this.getValue().split("\n").length * 30]);
26991 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26993 setValue : function(val)
26995 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26996 if (!this.editing) {
26997 this.updateMarkdown();
27003 if (!this.editing) {
27004 this.toggleTextEdit();
27012 * @class Roo.bootstrap.Table.AbstractSelectionModel
27013 * @extends Roo.util.Observable
27014 * Abstract base class for grid SelectionModels. It provides the interface that should be
27015 * implemented by descendant classes. This class should not be directly instantiated.
27018 Roo.bootstrap.Table.AbstractSelectionModel = function(){
27019 this.locked = false;
27020 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
27024 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
27025 /** @ignore Called by the grid automatically. Do not call directly. */
27026 init : function(grid){
27032 * Locks the selections.
27035 this.locked = true;
27039 * Unlocks the selections.
27041 unlock : function(){
27042 this.locked = false;
27046 * Returns true if the selections are locked.
27047 * @return {Boolean}
27049 isLocked : function(){
27050 return this.locked;
27054 initEvents : function ()
27060 * @extends Roo.bootstrap.Table.AbstractSelectionModel
27061 * @class Roo.bootstrap.Table.RowSelectionModel
27062 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
27063 * It supports multiple selections and keyboard selection/navigation.
27065 * @param {Object} config
27068 Roo.bootstrap.Table.RowSelectionModel = function(config){
27069 Roo.apply(this, config);
27070 this.selections = new Roo.util.MixedCollection(false, function(o){
27075 this.lastActive = false;
27079 * @event selectionchange
27080 * Fires when the selection changes
27081 * @param {SelectionModel} this
27083 "selectionchange" : true,
27085 * @event afterselectionchange
27086 * Fires after the selection changes (eg. by key press or clicking)
27087 * @param {SelectionModel} this
27089 "afterselectionchange" : true,
27091 * @event beforerowselect
27092 * Fires when a row is selected being selected, return false to cancel.
27093 * @param {SelectionModel} this
27094 * @param {Number} rowIndex The selected index
27095 * @param {Boolean} keepExisting False if other selections will be cleared
27097 "beforerowselect" : true,
27100 * Fires when a row is selected.
27101 * @param {SelectionModel} this
27102 * @param {Number} rowIndex The selected index
27103 * @param {Roo.data.Record} r The record
27105 "rowselect" : true,
27107 * @event rowdeselect
27108 * Fires when a row is deselected.
27109 * @param {SelectionModel} this
27110 * @param {Number} rowIndex The selected index
27112 "rowdeselect" : true
27114 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
27115 this.locked = false;
27118 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
27120 * @cfg {Boolean} singleSelect
27121 * True to allow selection of only one row at a time (defaults to false)
27123 singleSelect : false,
27126 initEvents : function()
27129 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
27130 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
27131 //}else{ // allow click to work like normal
27132 // this.grid.on("rowclick", this.handleDragableRowClick, this);
27134 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
27135 this.grid.on("rowclick", this.handleMouseDown, this);
27137 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
27138 "up" : function(e){
27140 this.selectPrevious(e.shiftKey);
27141 }else if(this.last !== false && this.lastActive !== false){
27142 var last = this.last;
27143 this.selectRange(this.last, this.lastActive-1);
27144 this.grid.getView().focusRow(this.lastActive);
27145 if(last !== false){
27149 this.selectFirstRow();
27151 this.fireEvent("afterselectionchange", this);
27153 "down" : function(e){
27155 this.selectNext(e.shiftKey);
27156 }else if(this.last !== false && this.lastActive !== false){
27157 var last = this.last;
27158 this.selectRange(this.last, this.lastActive+1);
27159 this.grid.getView().focusRow(this.lastActive);
27160 if(last !== false){
27164 this.selectFirstRow();
27166 this.fireEvent("afterselectionchange", this);
27170 this.grid.store.on('load', function(){
27171 this.selections.clear();
27174 var view = this.grid.view;
27175 view.on("refresh", this.onRefresh, this);
27176 view.on("rowupdated", this.onRowUpdated, this);
27177 view.on("rowremoved", this.onRemove, this);
27182 onRefresh : function()
27184 var ds = this.grid.store, i, v = this.grid.view;
27185 var s = this.selections;
27186 s.each(function(r){
27187 if((i = ds.indexOfId(r.id)) != -1){
27196 onRemove : function(v, index, r){
27197 this.selections.remove(r);
27201 onRowUpdated : function(v, index, r){
27202 if(this.isSelected(r)){
27203 v.onRowSelect(index);
27209 * @param {Array} records The records to select
27210 * @param {Boolean} keepExisting (optional) True to keep existing selections
27212 selectRecords : function(records, keepExisting)
27215 this.clearSelections();
27217 var ds = this.grid.store;
27218 for(var i = 0, len = records.length; i < len; i++){
27219 this.selectRow(ds.indexOf(records[i]), true);
27224 * Gets the number of selected rows.
27227 getCount : function(){
27228 return this.selections.length;
27232 * Selects the first row in the grid.
27234 selectFirstRow : function(){
27239 * Select the last row.
27240 * @param {Boolean} keepExisting (optional) True to keep existing selections
27242 selectLastRow : function(keepExisting){
27243 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
27244 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
27248 * Selects the row immediately following the last selected row.
27249 * @param {Boolean} keepExisting (optional) True to keep existing selections
27251 selectNext : function(keepExisting)
27253 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
27254 this.selectRow(this.last+1, keepExisting);
27255 this.grid.getView().focusRow(this.last);
27260 * Selects the row that precedes the last selected row.
27261 * @param {Boolean} keepExisting (optional) True to keep existing selections
27263 selectPrevious : function(keepExisting){
27265 this.selectRow(this.last-1, keepExisting);
27266 this.grid.getView().focusRow(this.last);
27271 * Returns the selected records
27272 * @return {Array} Array of selected records
27274 getSelections : function(){
27275 return [].concat(this.selections.items);
27279 * Returns the first selected record.
27282 getSelected : function(){
27283 return this.selections.itemAt(0);
27288 * Clears all selections.
27290 clearSelections : function(fast)
27296 var ds = this.grid.store;
27297 var s = this.selections;
27298 s.each(function(r){
27299 this.deselectRow(ds.indexOfId(r.id));
27303 this.selections.clear();
27310 * Selects all rows.
27312 selectAll : function(){
27316 this.selections.clear();
27317 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
27318 this.selectRow(i, true);
27323 * Returns True if there is a selection.
27324 * @return {Boolean}
27326 hasSelection : function(){
27327 return this.selections.length > 0;
27331 * Returns True if the specified row is selected.
27332 * @param {Number/Record} record The record or index of the record to check
27333 * @return {Boolean}
27335 isSelected : function(index){
27336 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
27337 return (r && this.selections.key(r.id) ? true : false);
27341 * Returns True if the specified record id is selected.
27342 * @param {String} id The id of record to check
27343 * @return {Boolean}
27345 isIdSelected : function(id){
27346 return (this.selections.key(id) ? true : false);
27351 handleMouseDBClick : function(e, t){
27355 handleMouseDown : function(e, t)
27357 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27358 if(this.isLocked() || rowIndex < 0 ){
27361 if(e.shiftKey && this.last !== false){
27362 var last = this.last;
27363 this.selectRange(last, rowIndex, e.ctrlKey);
27364 this.last = last; // reset the last
27368 var isSelected = this.isSelected(rowIndex);
27369 //Roo.log("select row:" + rowIndex);
27371 this.deselectRow(rowIndex);
27373 this.selectRow(rowIndex, true);
27377 if(e.button !== 0 && isSelected){
27378 alert('rowIndex 2: ' + rowIndex);
27379 view.focusRow(rowIndex);
27380 }else if(e.ctrlKey && isSelected){
27381 this.deselectRow(rowIndex);
27382 }else if(!isSelected){
27383 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27384 view.focusRow(rowIndex);
27388 this.fireEvent("afterselectionchange", this);
27391 handleDragableRowClick : function(grid, rowIndex, e)
27393 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27394 this.selectRow(rowIndex, false);
27395 grid.view.focusRow(rowIndex);
27396 this.fireEvent("afterselectionchange", this);
27401 * Selects multiple rows.
27402 * @param {Array} rows Array of the indexes of the row to select
27403 * @param {Boolean} keepExisting (optional) True to keep existing selections
27405 selectRows : function(rows, keepExisting){
27407 this.clearSelections();
27409 for(var i = 0, len = rows.length; i < len; i++){
27410 this.selectRow(rows[i], true);
27415 * Selects a range of rows. All rows in between startRow and endRow are also selected.
27416 * @param {Number} startRow The index of the first row in the range
27417 * @param {Number} endRow The index of the last row in the range
27418 * @param {Boolean} keepExisting (optional) True to retain existing selections
27420 selectRange : function(startRow, endRow, keepExisting){
27425 this.clearSelections();
27427 if(startRow <= endRow){
27428 for(var i = startRow; i <= endRow; i++){
27429 this.selectRow(i, true);
27432 for(var i = startRow; i >= endRow; i--){
27433 this.selectRow(i, true);
27439 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27440 * @param {Number} startRow The index of the first row in the range
27441 * @param {Number} endRow The index of the last row in the range
27443 deselectRange : function(startRow, endRow, preventViewNotify){
27447 for(var i = startRow; i <= endRow; i++){
27448 this.deselectRow(i, preventViewNotify);
27454 * @param {Number} row The index of the row to select
27455 * @param {Boolean} keepExisting (optional) True to keep existing selections
27457 selectRow : function(index, keepExisting, preventViewNotify)
27459 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27462 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27463 if(!keepExisting || this.singleSelect){
27464 this.clearSelections();
27467 var r = this.grid.store.getAt(index);
27468 //console.log('selectRow - record id :' + r.id);
27470 this.selections.add(r);
27471 this.last = this.lastActive = index;
27472 if(!preventViewNotify){
27473 var proxy = new Roo.Element(
27474 this.grid.getRowDom(index)
27476 proxy.addClass('bg-info info');
27478 this.fireEvent("rowselect", this, index, r);
27479 this.fireEvent("selectionchange", this);
27485 * @param {Number} row The index of the row to deselect
27487 deselectRow : function(index, preventViewNotify)
27492 if(this.last == index){
27495 if(this.lastActive == index){
27496 this.lastActive = false;
27499 var r = this.grid.store.getAt(index);
27504 this.selections.remove(r);
27505 //.console.log('deselectRow - record id :' + r.id);
27506 if(!preventViewNotify){
27508 var proxy = new Roo.Element(
27509 this.grid.getRowDom(index)
27511 proxy.removeClass('bg-info info');
27513 this.fireEvent("rowdeselect", this, index);
27514 this.fireEvent("selectionchange", this);
27518 restoreLast : function(){
27520 this.last = this._last;
27525 acceptsNav : function(row, col, cm){
27526 return !cm.isHidden(col) && cm.isCellEditable(col, row);
27530 onEditorKey : function(field, e){
27531 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27536 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27538 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27540 }else if(k == e.ENTER && !e.ctrlKey){
27544 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27546 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27548 }else if(k == e.ESC){
27552 g.startEditing(newCell[0], newCell[1]);
27558 * Ext JS Library 1.1.1
27559 * Copyright(c) 2006-2007, Ext JS, LLC.
27561 * Originally Released Under LGPL - original licence link has changed is not relivant.
27564 * <script type="text/javascript">
27568 * @class Roo.bootstrap.PagingToolbar
27569 * @extends Roo.bootstrap.NavSimplebar
27570 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27572 * Create a new PagingToolbar
27573 * @param {Object} config The config object
27574 * @param {Roo.data.Store} store
27576 Roo.bootstrap.PagingToolbar = function(config)
27578 // old args format still supported... - xtype is prefered..
27579 // created from xtype...
27581 this.ds = config.dataSource;
27583 if (config.store && !this.ds) {
27584 this.store= Roo.factory(config.store, Roo.data);
27585 this.ds = this.store;
27586 this.ds.xmodule = this.xmodule || false;
27589 this.toolbarItems = [];
27590 if (config.items) {
27591 this.toolbarItems = config.items;
27594 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27599 this.bind(this.ds);
27602 if (Roo.bootstrap.version == 4) {
27603 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27605 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27610 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27612 * @cfg {Roo.data.Store} dataSource
27613 * The underlying data store providing the paged data
27616 * @cfg {String/HTMLElement/Element} container
27617 * container The id or element that will contain the toolbar
27620 * @cfg {Boolean} displayInfo
27621 * True to display the displayMsg (defaults to false)
27624 * @cfg {Number} pageSize
27625 * The number of records to display per page (defaults to 20)
27629 * @cfg {String} displayMsg
27630 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27632 displayMsg : 'Displaying {0} - {1} of {2}',
27634 * @cfg {String} emptyMsg
27635 * The message to display when no records are found (defaults to "No data to display")
27637 emptyMsg : 'No data to display',
27639 * Customizable piece of the default paging text (defaults to "Page")
27642 beforePageText : "Page",
27644 * Customizable piece of the default paging text (defaults to "of %0")
27647 afterPageText : "of {0}",
27649 * Customizable piece of the default paging text (defaults to "First Page")
27652 firstText : "First Page",
27654 * Customizable piece of the default paging text (defaults to "Previous Page")
27657 prevText : "Previous Page",
27659 * Customizable piece of the default paging text (defaults to "Next Page")
27662 nextText : "Next Page",
27664 * Customizable piece of the default paging text (defaults to "Last Page")
27667 lastText : "Last Page",
27669 * Customizable piece of the default paging text (defaults to "Refresh")
27672 refreshText : "Refresh",
27676 onRender : function(ct, position)
27678 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27679 this.navgroup.parentId = this.id;
27680 this.navgroup.onRender(this.el, null);
27681 // add the buttons to the navgroup
27683 if(this.displayInfo){
27684 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27685 this.displayEl = this.el.select('.x-paging-info', true).first();
27686 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27687 // this.displayEl = navel.el.select('span',true).first();
27693 Roo.each(_this.buttons, function(e){ // this might need to use render????
27694 Roo.factory(e).render(_this.el);
27698 Roo.each(_this.toolbarItems, function(e) {
27699 _this.navgroup.addItem(e);
27703 this.first = this.navgroup.addItem({
27704 tooltip: this.firstText,
27705 cls: "prev btn-outline-secondary",
27706 html : ' <i class="fa fa-step-backward"></i>',
27708 preventDefault: true,
27709 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27712 this.prev = this.navgroup.addItem({
27713 tooltip: this.prevText,
27714 cls: "prev btn-outline-secondary",
27715 html : ' <i class="fa fa-backward"></i>',
27717 preventDefault: true,
27718 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
27720 //this.addSeparator();
27723 var field = this.navgroup.addItem( {
27725 cls : 'x-paging-position btn-outline-secondary',
27727 html : this.beforePageText +
27728 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27729 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
27732 this.field = field.el.select('input', true).first();
27733 this.field.on("keydown", this.onPagingKeydown, this);
27734 this.field.on("focus", function(){this.dom.select();});
27737 this.afterTextEl = field.el.select('.x-paging-after',true).first();
27738 //this.field.setHeight(18);
27739 //this.addSeparator();
27740 this.next = this.navgroup.addItem({
27741 tooltip: this.nextText,
27742 cls: "next btn-outline-secondary",
27743 html : ' <i class="fa fa-forward"></i>',
27745 preventDefault: true,
27746 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
27748 this.last = this.navgroup.addItem({
27749 tooltip: this.lastText,
27750 html : ' <i class="fa fa-step-forward"></i>',
27751 cls: "next btn-outline-secondary",
27753 preventDefault: true,
27754 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
27756 //this.addSeparator();
27757 this.loading = this.navgroup.addItem({
27758 tooltip: this.refreshText,
27759 cls: "btn-outline-secondary",
27760 html : ' <i class="fa fa-refresh"></i>',
27761 preventDefault: true,
27762 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27768 updateInfo : function(){
27769 if(this.displayEl){
27770 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27771 var msg = count == 0 ?
27775 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
27777 this.displayEl.update(msg);
27782 onLoad : function(ds, r, o)
27784 this.cursor = o.params && o.params.start ? o.params.start : 0;
27786 var d = this.getPageData(),
27791 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27792 this.field.dom.value = ap;
27793 this.first.setDisabled(ap == 1);
27794 this.prev.setDisabled(ap == 1);
27795 this.next.setDisabled(ap == ps);
27796 this.last.setDisabled(ap == ps);
27797 this.loading.enable();
27802 getPageData : function(){
27803 var total = this.ds.getTotalCount();
27806 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27807 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27812 onLoadError : function(){
27813 this.loading.enable();
27817 onPagingKeydown : function(e){
27818 var k = e.getKey();
27819 var d = this.getPageData();
27821 var v = this.field.dom.value, pageNum;
27822 if(!v || isNaN(pageNum = parseInt(v, 10))){
27823 this.field.dom.value = d.activePage;
27826 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27827 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27830 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))
27832 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27833 this.field.dom.value = pageNum;
27834 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27837 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27839 var v = this.field.dom.value, pageNum;
27840 var increment = (e.shiftKey) ? 10 : 1;
27841 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27844 if(!v || isNaN(pageNum = parseInt(v, 10))) {
27845 this.field.dom.value = d.activePage;
27848 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27850 this.field.dom.value = parseInt(v, 10) + increment;
27851 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27852 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27859 beforeLoad : function(){
27861 this.loading.disable();
27866 onClick : function(which){
27875 ds.load({params:{start: 0, limit: this.pageSize}});
27878 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27881 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27884 var total = ds.getTotalCount();
27885 var extra = total % this.pageSize;
27886 var lastStart = extra ? (total - extra) : total-this.pageSize;
27887 ds.load({params:{start: lastStart, limit: this.pageSize}});
27890 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27896 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27897 * @param {Roo.data.Store} store The data store to unbind
27899 unbind : function(ds){
27900 ds.un("beforeload", this.beforeLoad, this);
27901 ds.un("load", this.onLoad, this);
27902 ds.un("loadexception", this.onLoadError, this);
27903 ds.un("remove", this.updateInfo, this);
27904 ds.un("add", this.updateInfo, this);
27905 this.ds = undefined;
27909 * Binds the paging toolbar to the specified {@link Roo.data.Store}
27910 * @param {Roo.data.Store} store The data store to bind
27912 bind : function(ds){
27913 ds.on("beforeload", this.beforeLoad, this);
27914 ds.on("load", this.onLoad, this);
27915 ds.on("loadexception", this.onLoadError, this);
27916 ds.on("remove", this.updateInfo, this);
27917 ds.on("add", this.updateInfo, this);
27928 * @class Roo.bootstrap.MessageBar
27929 * @extends Roo.bootstrap.Component
27930 * Bootstrap MessageBar class
27931 * @cfg {String} html contents of the MessageBar
27932 * @cfg {String} weight (info | success | warning | danger) default info
27933 * @cfg {String} beforeClass insert the bar before the given class
27934 * @cfg {Boolean} closable (true | false) default false
27935 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27938 * Create a new Element
27939 * @param {Object} config The config object
27942 Roo.bootstrap.MessageBar = function(config){
27943 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27946 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
27952 beforeClass: 'bootstrap-sticky-wrap',
27954 getAutoCreate : function(){
27958 cls: 'alert alert-dismissable alert-' + this.weight,
27963 html: this.html || ''
27969 cfg.cls += ' alert-messages-fixed';
27983 onRender : function(ct, position)
27985 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27988 var cfg = Roo.apply({}, this.getAutoCreate());
27992 cfg.cls += ' ' + this.cls;
27995 cfg.style = this.style;
27997 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27999 this.el.setVisibilityMode(Roo.Element.DISPLAY);
28002 this.el.select('>button.close').on('click', this.hide, this);
28008 if (!this.rendered) {
28014 this.fireEvent('show', this);
28020 if (!this.rendered) {
28026 this.fireEvent('hide', this);
28029 update : function()
28031 // var e = this.el.dom.firstChild;
28033 // if(this.closable){
28034 // e = e.nextSibling;
28037 // e.data = this.html || '';
28039 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28055 * @class Roo.bootstrap.Graph
28056 * @extends Roo.bootstrap.Component
28057 * Bootstrap Graph class
28061 @cfg {String} graphtype bar | vbar | pie
28062 @cfg {number} g_x coodinator | centre x (pie)
28063 @cfg {number} g_y coodinator | centre y (pie)
28064 @cfg {number} g_r radius (pie)
28065 @cfg {number} g_height height of the chart (respected by all elements in the set)
28066 @cfg {number} g_width width of the chart (respected by all elements in the set)
28067 @cfg {Object} title The title of the chart
28070 -opts (object) options for the chart
28072 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28073 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28075 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.
28076 o stacked (boolean) whether or not to tread values as in a stacked bar chart
28078 o stretch (boolean)
28080 -opts (object) options for the pie
28083 o startAngle (number)
28084 o endAngle (number)
28088 * Create a new Input
28089 * @param {Object} config The config object
28092 Roo.bootstrap.Graph = function(config){
28093 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28099 * The img click event for the img.
28100 * @param {Roo.EventObject} e
28106 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
28117 //g_colors: this.colors,
28124 getAutoCreate : function(){
28135 onRender : function(ct,position){
28138 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28140 if (typeof(Raphael) == 'undefined') {
28141 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28145 this.raphael = Raphael(this.el.dom);
28147 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28148 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28149 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28150 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28152 r.text(160, 10, "Single Series Chart").attr(txtattr);
28153 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28154 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28155 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28157 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28158 r.barchart(330, 10, 300, 220, data1);
28159 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28160 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28163 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28164 // r.barchart(30, 30, 560, 250, xdata, {
28165 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28166 // axis : "0 0 1 1",
28167 // axisxlabels : xdata
28168 // //yvalues : cols,
28171 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28173 // this.load(null,xdata,{
28174 // axis : "0 0 1 1",
28175 // axisxlabels : xdata
28180 load : function(graphtype,xdata,opts)
28182 this.raphael.clear();
28184 graphtype = this.graphtype;
28189 var r = this.raphael,
28190 fin = function () {
28191 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28193 fout = function () {
28194 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28196 pfin = function() {
28197 this.sector.stop();
28198 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28201 this.label[0].stop();
28202 this.label[0].attr({ r: 7.5 });
28203 this.label[1].attr({ "font-weight": 800 });
28206 pfout = function() {
28207 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28210 this.label[0].animate({ r: 5 }, 500, "bounce");
28211 this.label[1].attr({ "font-weight": 400 });
28217 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28220 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28223 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
28224 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28226 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28233 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28238 setTitle: function(o)
28243 initEvents: function() {
28246 this.el.on('click', this.onClick, this);
28250 onClick : function(e)
28252 Roo.log('img onclick');
28253 this.fireEvent('click', this, e);
28265 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28268 * @class Roo.bootstrap.dash.NumberBox
28269 * @extends Roo.bootstrap.Component
28270 * Bootstrap NumberBox class
28271 * @cfg {String} headline Box headline
28272 * @cfg {String} content Box content
28273 * @cfg {String} icon Box icon
28274 * @cfg {String} footer Footer text
28275 * @cfg {String} fhref Footer href
28278 * Create a new NumberBox
28279 * @param {Object} config The config object
28283 Roo.bootstrap.dash.NumberBox = function(config){
28284 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28288 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
28297 getAutoCreate : function(){
28301 cls : 'small-box ',
28309 cls : 'roo-headline',
28310 html : this.headline
28314 cls : 'roo-content',
28315 html : this.content
28329 cls : 'ion ' + this.icon
28338 cls : 'small-box-footer',
28339 href : this.fhref || '#',
28343 cfg.cn.push(footer);
28350 onRender : function(ct,position){
28351 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28358 setHeadline: function (value)
28360 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28363 setFooter: function (value, href)
28365 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28368 this.el.select('a.small-box-footer',true).first().attr('href', href);
28373 setContent: function (value)
28375 this.el.select('.roo-content',true).first().dom.innerHTML = value;
28378 initEvents: function()
28392 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28395 * @class Roo.bootstrap.dash.TabBox
28396 * @extends Roo.bootstrap.Component
28397 * Bootstrap TabBox class
28398 * @cfg {String} title Title of the TabBox
28399 * @cfg {String} icon Icon of the TabBox
28400 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28401 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28404 * Create a new TabBox
28405 * @param {Object} config The config object
28409 Roo.bootstrap.dash.TabBox = function(config){
28410 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28415 * When a pane is added
28416 * @param {Roo.bootstrap.dash.TabPane} pane
28420 * @event activatepane
28421 * When a pane is activated
28422 * @param {Roo.bootstrap.dash.TabPane} pane
28424 "activatepane" : true
28432 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28437 tabScrollable : false,
28439 getChildContainer : function()
28441 return this.el.select('.tab-content', true).first();
28444 getAutoCreate : function(){
28448 cls: 'pull-left header',
28456 cls: 'fa ' + this.icon
28462 cls: 'nav nav-tabs pull-right',
28468 if(this.tabScrollable){
28475 cls: 'nav nav-tabs pull-right',
28486 cls: 'nav-tabs-custom',
28491 cls: 'tab-content no-padding',
28499 initEvents : function()
28501 //Roo.log('add add pane handler');
28502 this.on('addpane', this.onAddPane, this);
28505 * Updates the box title
28506 * @param {String} html to set the title to.
28508 setTitle : function(value)
28510 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28512 onAddPane : function(pane)
28514 this.panes.push(pane);
28515 //Roo.log('addpane');
28517 // tabs are rendere left to right..
28518 if(!this.showtabs){
28522 var ctr = this.el.select('.nav-tabs', true).first();
28525 var existing = ctr.select('.nav-tab',true);
28526 var qty = existing.getCount();;
28529 var tab = ctr.createChild({
28531 cls : 'nav-tab' + (qty ? '' : ' active'),
28539 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28542 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28544 pane.el.addClass('active');
28549 onTabClick : function(ev,un,ob,pane)
28551 //Roo.log('tab - prev default');
28552 ev.preventDefault();
28555 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28556 pane.tab.addClass('active');
28557 //Roo.log(pane.title);
28558 this.getChildContainer().select('.tab-pane',true).removeClass('active');
28559 // technically we should have a deactivate event.. but maybe add later.
28560 // and it should not de-activate the selected tab...
28561 this.fireEvent('activatepane', pane);
28562 pane.el.addClass('active');
28563 pane.fireEvent('activate');
28568 getActivePane : function()
28571 Roo.each(this.panes, function(p) {
28572 if(p.el.hasClass('active')){
28593 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28595 * @class Roo.bootstrap.TabPane
28596 * @extends Roo.bootstrap.Component
28597 * Bootstrap TabPane class
28598 * @cfg {Boolean} active (false | true) Default false
28599 * @cfg {String} title title of panel
28603 * Create a new TabPane
28604 * @param {Object} config The config object
28607 Roo.bootstrap.dash.TabPane = function(config){
28608 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28614 * When a pane is activated
28615 * @param {Roo.bootstrap.dash.TabPane} pane
28622 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
28627 // the tabBox that this is attached to.
28630 getAutoCreate : function()
28638 cfg.cls += ' active';
28643 initEvents : function()
28645 //Roo.log('trigger add pane handler');
28646 this.parent().fireEvent('addpane', this)
28650 * Updates the tab title
28651 * @param {String} html to set the title to.
28653 setTitle: function(str)
28659 this.tab.select('a', true).first().dom.innerHTML = str;
28676 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28679 * @class Roo.bootstrap.menu.Menu
28680 * @extends Roo.bootstrap.Component
28681 * Bootstrap Menu class - container for Menu
28682 * @cfg {String} html Text of the menu
28683 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28684 * @cfg {String} icon Font awesome icon
28685 * @cfg {String} pos Menu align to (top | bottom) default bottom
28689 * Create a new Menu
28690 * @param {Object} config The config object
28694 Roo.bootstrap.menu.Menu = function(config){
28695 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28699 * @event beforeshow
28700 * Fires before this menu is displayed
28701 * @param {Roo.bootstrap.menu.Menu} this
28705 * @event beforehide
28706 * Fires before this menu is hidden
28707 * @param {Roo.bootstrap.menu.Menu} this
28712 * Fires after this menu is displayed
28713 * @param {Roo.bootstrap.menu.Menu} this
28718 * Fires after this menu is hidden
28719 * @param {Roo.bootstrap.menu.Menu} this
28724 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28725 * @param {Roo.bootstrap.menu.Menu} this
28726 * @param {Roo.EventObject} e
28733 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
28737 weight : 'default',
28742 getChildContainer : function() {
28743 if(this.isSubMenu){
28747 return this.el.select('ul.dropdown-menu', true).first();
28750 getAutoCreate : function()
28755 cls : 'roo-menu-text',
28763 cls : 'fa ' + this.icon
28774 cls : 'dropdown-button btn btn-' + this.weight,
28779 cls : 'dropdown-toggle btn btn-' + this.weight,
28789 cls : 'dropdown-menu'
28795 if(this.pos == 'top'){
28796 cfg.cls += ' dropup';
28799 if(this.isSubMenu){
28802 cls : 'dropdown-menu'
28809 onRender : function(ct, position)
28811 this.isSubMenu = ct.hasClass('dropdown-submenu');
28813 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28816 initEvents : function()
28818 if(this.isSubMenu){
28822 this.hidden = true;
28824 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28825 this.triggerEl.on('click', this.onTriggerPress, this);
28827 this.buttonEl = this.el.select('button.dropdown-button', true).first();
28828 this.buttonEl.on('click', this.onClick, this);
28834 if(this.isSubMenu){
28838 return this.el.select('ul.dropdown-menu', true).first();
28841 onClick : function(e)
28843 this.fireEvent("click", this, e);
28846 onTriggerPress : function(e)
28848 if (this.isVisible()) {
28855 isVisible : function(){
28856 return !this.hidden;
28861 this.fireEvent("beforeshow", this);
28863 this.hidden = false;
28864 this.el.addClass('open');
28866 Roo.get(document).on("mouseup", this.onMouseUp, this);
28868 this.fireEvent("show", this);
28875 this.fireEvent("beforehide", this);
28877 this.hidden = true;
28878 this.el.removeClass('open');
28880 Roo.get(document).un("mouseup", this.onMouseUp);
28882 this.fireEvent("hide", this);
28885 onMouseUp : function()
28899 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28902 * @class Roo.bootstrap.menu.Item
28903 * @extends Roo.bootstrap.Component
28904 * Bootstrap MenuItem class
28905 * @cfg {Boolean} submenu (true | false) default false
28906 * @cfg {String} html text of the item
28907 * @cfg {String} href the link
28908 * @cfg {Boolean} disable (true | false) default false
28909 * @cfg {Boolean} preventDefault (true | false) default true
28910 * @cfg {String} icon Font awesome icon
28911 * @cfg {String} pos Submenu align to (left | right) default right
28915 * Create a new Item
28916 * @param {Object} config The config object
28920 Roo.bootstrap.menu.Item = function(config){
28921 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28925 * Fires when the mouse is hovering over this menu
28926 * @param {Roo.bootstrap.menu.Item} this
28927 * @param {Roo.EventObject} e
28932 * Fires when the mouse exits this menu
28933 * @param {Roo.bootstrap.menu.Item} this
28934 * @param {Roo.EventObject} e
28940 * The raw click event for the entire grid.
28941 * @param {Roo.EventObject} e
28947 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
28952 preventDefault: true,
28957 getAutoCreate : function()
28962 cls : 'roo-menu-item-text',
28970 cls : 'fa ' + this.icon
28979 href : this.href || '#',
28986 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28990 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28992 if(this.pos == 'left'){
28993 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
29000 initEvents : function()
29002 this.el.on('mouseover', this.onMouseOver, this);
29003 this.el.on('mouseout', this.onMouseOut, this);
29005 this.el.select('a', true).first().on('click', this.onClick, this);
29009 onClick : function(e)
29011 if(this.preventDefault){
29012 e.preventDefault();
29015 this.fireEvent("click", this, e);
29018 onMouseOver : function(e)
29020 if(this.submenu && this.pos == 'left'){
29021 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29024 this.fireEvent("mouseover", this, e);
29027 onMouseOut : function(e)
29029 this.fireEvent("mouseout", this, e);
29041 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29044 * @class Roo.bootstrap.menu.Separator
29045 * @extends Roo.bootstrap.Component
29046 * Bootstrap Separator class
29049 * Create a new Separator
29050 * @param {Object} config The config object
29054 Roo.bootstrap.menu.Separator = function(config){
29055 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29058 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
29060 getAutoCreate : function(){
29063 cls: 'dropdown-divider divider'
29081 * @class Roo.bootstrap.Tooltip
29082 * Bootstrap Tooltip class
29083 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29084 * to determine which dom element triggers the tooltip.
29086 * It needs to add support for additional attributes like tooltip-position
29089 * Create a new Toolti
29090 * @param {Object} config The config object
29093 Roo.bootstrap.Tooltip = function(config){
29094 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29096 this.alignment = Roo.bootstrap.Tooltip.alignment;
29098 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29099 this.alignment = config.alignment;
29104 Roo.apply(Roo.bootstrap.Tooltip, {
29106 * @function init initialize tooltip monitoring.
29110 currentTip : false,
29111 currentRegion : false,
29117 Roo.get(document).on('mouseover', this.enter ,this);
29118 Roo.get(document).on('mouseout', this.leave, this);
29121 this.currentTip = new Roo.bootstrap.Tooltip();
29124 enter : function(ev)
29126 var dom = ev.getTarget();
29128 //Roo.log(['enter',dom]);
29129 var el = Roo.fly(dom);
29130 if (this.currentEl) {
29132 //Roo.log(this.currentEl);
29133 //Roo.log(this.currentEl.contains(dom));
29134 if (this.currentEl == el) {
29137 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29143 if (this.currentTip.el) {
29144 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29148 if(!el || el.dom == document){
29154 if (!el.attr('tooltip')) {
29155 pel = el.findParent("[tooltip]");
29157 bindEl = Roo.get(pel);
29163 // you can not look for children, as if el is the body.. then everythign is the child..
29164 if (!pel && !el.attr('tooltip')) { //
29165 if (!el.select("[tooltip]").elements.length) {
29168 // is the mouse over this child...?
29169 bindEl = el.select("[tooltip]").first();
29170 var xy = ev.getXY();
29171 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29172 //Roo.log("not in region.");
29175 //Roo.log("child element over..");
29178 this.currentEl = el;
29179 this.currentTip.bind(bindEl);
29180 this.currentRegion = Roo.lib.Region.getRegion(dom);
29181 this.currentTip.enter();
29184 leave : function(ev)
29186 var dom = ev.getTarget();
29187 //Roo.log(['leave',dom]);
29188 if (!this.currentEl) {
29193 if (dom != this.currentEl.dom) {
29196 var xy = ev.getXY();
29197 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
29200 // only activate leave if mouse cursor is outside... bounding box..
29205 if (this.currentTip) {
29206 this.currentTip.leave();
29208 //Roo.log('clear currentEl');
29209 this.currentEl = false;
29214 'left' : ['r-l', [-2,0], 'right'],
29215 'right' : ['l-r', [2,0], 'left'],
29216 'bottom' : ['t-b', [0,2], 'top'],
29217 'top' : [ 'b-t', [0,-2], 'bottom']
29223 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
29228 delay : null, // can be { show : 300 , hide: 500}
29232 hoverState : null, //???
29234 placement : 'bottom',
29238 getAutoCreate : function(){
29245 cls : 'tooltip-arrow arrow'
29248 cls : 'tooltip-inner'
29255 bind : function(el)
29260 initEvents : function()
29262 this.arrowEl = this.el.select('.arrow', true).first();
29263 this.innerEl = this.el.select('.tooltip-inner', true).first();
29266 enter : function () {
29268 if (this.timeout != null) {
29269 clearTimeout(this.timeout);
29272 this.hoverState = 'in';
29273 //Roo.log("enter - show");
29274 if (!this.delay || !this.delay.show) {
29279 this.timeout = setTimeout(function () {
29280 if (_t.hoverState == 'in') {
29283 }, this.delay.show);
29287 clearTimeout(this.timeout);
29289 this.hoverState = 'out';
29290 if (!this.delay || !this.delay.hide) {
29296 this.timeout = setTimeout(function () {
29297 //Roo.log("leave - timeout");
29299 if (_t.hoverState == 'out') {
29301 Roo.bootstrap.Tooltip.currentEl = false;
29306 show : function (msg)
29309 this.render(document.body);
29312 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29314 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29316 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29318 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29319 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29321 var placement = typeof this.placement == 'function' ?
29322 this.placement.call(this, this.el, on_el) :
29325 var autoToken = /\s?auto?\s?/i;
29326 var autoPlace = autoToken.test(placement);
29328 placement = placement.replace(autoToken, '') || 'top';
29332 //this.el.setXY([0,0]);
29334 //this.el.dom.style.display='block';
29336 //this.el.appendTo(on_el);
29338 var p = this.getPosition();
29339 var box = this.el.getBox();
29345 var align = this.alignment[placement];
29347 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29349 if(placement == 'top' || placement == 'bottom'){
29351 placement = 'right';
29354 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29355 placement = 'left';
29358 var scroll = Roo.select('body', true).first().getScroll();
29360 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29364 align = this.alignment[placement];
29366 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29370 var elems = document.getElementsByTagName('div');
29371 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29372 for (var i = 0; i < elems.length; i++) {
29373 var zindex = Number.parseInt(
29374 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29377 if (zindex > highest) {
29384 this.el.dom.style.zIndex = highest;
29386 this.el.alignTo(this.bindEl, align[0],align[1]);
29387 //var arrow = this.el.select('.arrow',true).first();
29388 //arrow.set(align[2],
29390 this.el.addClass(placement);
29391 this.el.addClass("bs-tooltip-"+ placement);
29393 this.el.addClass('in fade show');
29395 this.hoverState = null;
29397 if (this.el.hasClass('fade')) {
29412 //this.el.setXY([0,0]);
29413 this.el.removeClass(['show', 'in']);
29429 * @class Roo.bootstrap.LocationPicker
29430 * @extends Roo.bootstrap.Component
29431 * Bootstrap LocationPicker class
29432 * @cfg {Number} latitude Position when init default 0
29433 * @cfg {Number} longitude Position when init default 0
29434 * @cfg {Number} zoom default 15
29435 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29436 * @cfg {Boolean} mapTypeControl default false
29437 * @cfg {Boolean} disableDoubleClickZoom default false
29438 * @cfg {Boolean} scrollwheel default true
29439 * @cfg {Boolean} streetViewControl default false
29440 * @cfg {Number} radius default 0
29441 * @cfg {String} locationName
29442 * @cfg {Boolean} draggable default true
29443 * @cfg {Boolean} enableAutocomplete default false
29444 * @cfg {Boolean} enableReverseGeocode default true
29445 * @cfg {String} markerTitle
29448 * Create a new LocationPicker
29449 * @param {Object} config The config object
29453 Roo.bootstrap.LocationPicker = function(config){
29455 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29460 * Fires when the picker initialized.
29461 * @param {Roo.bootstrap.LocationPicker} this
29462 * @param {Google Location} location
29466 * @event positionchanged
29467 * Fires when the picker position changed.
29468 * @param {Roo.bootstrap.LocationPicker} this
29469 * @param {Google Location} location
29471 positionchanged : true,
29474 * Fires when the map resize.
29475 * @param {Roo.bootstrap.LocationPicker} this
29480 * Fires when the map show.
29481 * @param {Roo.bootstrap.LocationPicker} this
29486 * Fires when the map hide.
29487 * @param {Roo.bootstrap.LocationPicker} this
29492 * Fires when click the map.
29493 * @param {Roo.bootstrap.LocationPicker} this
29494 * @param {Map event} e
29498 * @event mapRightClick
29499 * Fires when right click the map.
29500 * @param {Roo.bootstrap.LocationPicker} this
29501 * @param {Map event} e
29503 mapRightClick : true,
29505 * @event markerClick
29506 * Fires when click the marker.
29507 * @param {Roo.bootstrap.LocationPicker} this
29508 * @param {Map event} e
29510 markerClick : true,
29512 * @event markerRightClick
29513 * Fires when right click the marker.
29514 * @param {Roo.bootstrap.LocationPicker} this
29515 * @param {Map event} e
29517 markerRightClick : true,
29519 * @event OverlayViewDraw
29520 * Fires when OverlayView Draw
29521 * @param {Roo.bootstrap.LocationPicker} this
29523 OverlayViewDraw : true,
29525 * @event OverlayViewOnAdd
29526 * Fires when OverlayView Draw
29527 * @param {Roo.bootstrap.LocationPicker} this
29529 OverlayViewOnAdd : true,
29531 * @event OverlayViewOnRemove
29532 * Fires when OverlayView Draw
29533 * @param {Roo.bootstrap.LocationPicker} this
29535 OverlayViewOnRemove : true,
29537 * @event OverlayViewShow
29538 * Fires when OverlayView Draw
29539 * @param {Roo.bootstrap.LocationPicker} this
29540 * @param {Pixel} cpx
29542 OverlayViewShow : true,
29544 * @event OverlayViewHide
29545 * Fires when OverlayView Draw
29546 * @param {Roo.bootstrap.LocationPicker} this
29548 OverlayViewHide : true,
29550 * @event loadexception
29551 * Fires when load google lib failed.
29552 * @param {Roo.bootstrap.LocationPicker} this
29554 loadexception : true
29559 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
29561 gMapContext: false,
29567 mapTypeControl: false,
29568 disableDoubleClickZoom: false,
29570 streetViewControl: false,
29574 enableAutocomplete: false,
29575 enableReverseGeocode: true,
29578 getAutoCreate: function()
29583 cls: 'roo-location-picker'
29589 initEvents: function(ct, position)
29591 if(!this.el.getWidth() || this.isApplied()){
29595 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29600 initial: function()
29602 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29603 this.fireEvent('loadexception', this);
29607 if(!this.mapTypeId){
29608 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29611 this.gMapContext = this.GMapContext();
29613 this.initOverlayView();
29615 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29619 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29620 _this.setPosition(_this.gMapContext.marker.position);
29623 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29624 _this.fireEvent('mapClick', this, event);
29628 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29629 _this.fireEvent('mapRightClick', this, event);
29633 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29634 _this.fireEvent('markerClick', this, event);
29638 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29639 _this.fireEvent('markerRightClick', this, event);
29643 this.setPosition(this.gMapContext.location);
29645 this.fireEvent('initial', this, this.gMapContext.location);
29648 initOverlayView: function()
29652 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29656 _this.fireEvent('OverlayViewDraw', _this);
29661 _this.fireEvent('OverlayViewOnAdd', _this);
29664 onRemove: function()
29666 _this.fireEvent('OverlayViewOnRemove', _this);
29669 show: function(cpx)
29671 _this.fireEvent('OverlayViewShow', _this, cpx);
29676 _this.fireEvent('OverlayViewHide', _this);
29682 fromLatLngToContainerPixel: function(event)
29684 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29687 isApplied: function()
29689 return this.getGmapContext() == false ? false : true;
29692 getGmapContext: function()
29694 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29697 GMapContext: function()
29699 var position = new google.maps.LatLng(this.latitude, this.longitude);
29701 var _map = new google.maps.Map(this.el.dom, {
29704 mapTypeId: this.mapTypeId,
29705 mapTypeControl: this.mapTypeControl,
29706 disableDoubleClickZoom: this.disableDoubleClickZoom,
29707 scrollwheel: this.scrollwheel,
29708 streetViewControl: this.streetViewControl,
29709 locationName: this.locationName,
29710 draggable: this.draggable,
29711 enableAutocomplete: this.enableAutocomplete,
29712 enableReverseGeocode: this.enableReverseGeocode
29715 var _marker = new google.maps.Marker({
29716 position: position,
29718 title: this.markerTitle,
29719 draggable: this.draggable
29726 location: position,
29727 radius: this.radius,
29728 locationName: this.locationName,
29729 addressComponents: {
29730 formatted_address: null,
29731 addressLine1: null,
29732 addressLine2: null,
29734 streetNumber: null,
29738 stateOrProvince: null
29741 domContainer: this.el.dom,
29742 geodecoder: new google.maps.Geocoder()
29746 drawCircle: function(center, radius, options)
29748 if (this.gMapContext.circle != null) {
29749 this.gMapContext.circle.setMap(null);
29753 options = Roo.apply({}, options, {
29754 strokeColor: "#0000FF",
29755 strokeOpacity: .35,
29757 fillColor: "#0000FF",
29761 options.map = this.gMapContext.map;
29762 options.radius = radius;
29763 options.center = center;
29764 this.gMapContext.circle = new google.maps.Circle(options);
29765 return this.gMapContext.circle;
29771 setPosition: function(location)
29773 this.gMapContext.location = location;
29774 this.gMapContext.marker.setPosition(location);
29775 this.gMapContext.map.panTo(location);
29776 this.drawCircle(location, this.gMapContext.radius, {});
29780 if (this.gMapContext.settings.enableReverseGeocode) {
29781 this.gMapContext.geodecoder.geocode({
29782 latLng: this.gMapContext.location
29783 }, function(results, status) {
29785 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29786 _this.gMapContext.locationName = results[0].formatted_address;
29787 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29789 _this.fireEvent('positionchanged', this, location);
29796 this.fireEvent('positionchanged', this, location);
29801 google.maps.event.trigger(this.gMapContext.map, "resize");
29803 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29805 this.fireEvent('resize', this);
29808 setPositionByLatLng: function(latitude, longitude)
29810 this.setPosition(new google.maps.LatLng(latitude, longitude));
29813 getCurrentPosition: function()
29816 latitude: this.gMapContext.location.lat(),
29817 longitude: this.gMapContext.location.lng()
29821 getAddressName: function()
29823 return this.gMapContext.locationName;
29826 getAddressComponents: function()
29828 return this.gMapContext.addressComponents;
29831 address_component_from_google_geocode: function(address_components)
29835 for (var i = 0; i < address_components.length; i++) {
29836 var component = address_components[i];
29837 if (component.types.indexOf("postal_code") >= 0) {
29838 result.postalCode = component.short_name;
29839 } else if (component.types.indexOf("street_number") >= 0) {
29840 result.streetNumber = component.short_name;
29841 } else if (component.types.indexOf("route") >= 0) {
29842 result.streetName = component.short_name;
29843 } else if (component.types.indexOf("neighborhood") >= 0) {
29844 result.city = component.short_name;
29845 } else if (component.types.indexOf("locality") >= 0) {
29846 result.city = component.short_name;
29847 } else if (component.types.indexOf("sublocality") >= 0) {
29848 result.district = component.short_name;
29849 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29850 result.stateOrProvince = component.short_name;
29851 } else if (component.types.indexOf("country") >= 0) {
29852 result.country = component.short_name;
29856 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29857 result.addressLine2 = "";
29861 setZoomLevel: function(zoom)
29863 this.gMapContext.map.setZoom(zoom);
29876 this.fireEvent('show', this);
29887 this.fireEvent('hide', this);
29892 Roo.apply(Roo.bootstrap.LocationPicker, {
29894 OverlayView : function(map, options)
29896 options = options || {};
29903 * @class Roo.bootstrap.Alert
29904 * @extends Roo.bootstrap.Component
29905 * Bootstrap Alert class - shows an alert area box
29907 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29908 Enter a valid email address
29911 * @cfg {String} title The title of alert
29912 * @cfg {String} html The content of alert
29913 * @cfg {String} weight (success|info|warning|danger) Weight of the message
29914 * @cfg {String} fa font-awesomeicon
29915 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
29916 * @cfg {Boolean} close true to show a x closer
29920 * Create a new alert
29921 * @param {Object} config The config object
29925 Roo.bootstrap.Alert = function(config){
29926 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29930 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
29936 faicon: false, // BC
29940 getAutoCreate : function()
29952 style : this.close ? '' : 'display:none'
29956 cls : 'roo-alert-icon'
29961 cls : 'roo-alert-title',
29966 cls : 'roo-alert-text',
29973 cfg.cn[0].cls += ' fa ' + this.faicon;
29976 cfg.cn[0].cls += ' fa ' + this.fa;
29980 cfg.cls += ' alert-' + this.weight;
29986 initEvents: function()
29988 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29989 this.titleEl = this.el.select('.roo-alert-title',true).first();
29990 this.iconEl = this.el.select('.roo-alert-icon',true).first();
29991 this.htmlEl = this.el.select('.roo-alert-text',true).first();
29992 if (this.seconds > 0) {
29993 this.hide.defer(this.seconds, this);
29997 * Set the Title Message HTML
29998 * @param {String} html
30000 setTitle : function(str)
30002 this.titleEl.dom.innerHTML = str;
30006 * Set the Body Message HTML
30007 * @param {String} html
30009 setHtml : function(str)
30011 this.htmlEl.dom.innerHTML = str;
30014 * Set the Weight of the alert
30015 * @param {String} (success|info|warning|danger) weight
30018 setWeight : function(weight)
30021 this.el.removeClass('alert-' + this.weight);
30024 this.weight = weight;
30026 this.el.addClass('alert-' + this.weight);
30029 * Set the Icon of the alert
30030 * @param {String} see fontawsome names (name without the 'fa-' bit)
30032 setIcon : function(icon)
30035 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30038 this.faicon = icon;
30040 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30065 * @class Roo.bootstrap.UploadCropbox
30066 * @extends Roo.bootstrap.Component
30067 * Bootstrap UploadCropbox class
30068 * @cfg {String} emptyText show when image has been loaded
30069 * @cfg {String} rotateNotify show when image too small to rotate
30070 * @cfg {Number} errorTimeout default 3000
30071 * @cfg {Number} minWidth default 300
30072 * @cfg {Number} minHeight default 300
30073 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30074 * @cfg {Boolean} isDocument (true|false) default false
30075 * @cfg {String} url action url
30076 * @cfg {String} paramName default 'imageUpload'
30077 * @cfg {String} method default POST
30078 * @cfg {Boolean} loadMask (true|false) default true
30079 * @cfg {Boolean} loadingText default 'Loading...'
30082 * Create a new UploadCropbox
30083 * @param {Object} config The config object
30086 Roo.bootstrap.UploadCropbox = function(config){
30087 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30091 * @event beforeselectfile
30092 * Fire before select file
30093 * @param {Roo.bootstrap.UploadCropbox} this
30095 "beforeselectfile" : true,
30098 * Fire after initEvent
30099 * @param {Roo.bootstrap.UploadCropbox} this
30104 * Fire after initEvent
30105 * @param {Roo.bootstrap.UploadCropbox} this
30106 * @param {String} data
30111 * Fire when preparing the file data
30112 * @param {Roo.bootstrap.UploadCropbox} this
30113 * @param {Object} file
30118 * Fire when get exception
30119 * @param {Roo.bootstrap.UploadCropbox} this
30120 * @param {XMLHttpRequest} xhr
30122 "exception" : true,
30124 * @event beforeloadcanvas
30125 * Fire before load the canvas
30126 * @param {Roo.bootstrap.UploadCropbox} this
30127 * @param {String} src
30129 "beforeloadcanvas" : true,
30132 * Fire when trash image
30133 * @param {Roo.bootstrap.UploadCropbox} this
30138 * Fire when download the image
30139 * @param {Roo.bootstrap.UploadCropbox} this
30143 * @event footerbuttonclick
30144 * Fire when footerbuttonclick
30145 * @param {Roo.bootstrap.UploadCropbox} this
30146 * @param {String} type
30148 "footerbuttonclick" : true,
30152 * @param {Roo.bootstrap.UploadCropbox} this
30157 * Fire when rotate the image
30158 * @param {Roo.bootstrap.UploadCropbox} this
30159 * @param {String} pos
30164 * Fire when inspect the file
30165 * @param {Roo.bootstrap.UploadCropbox} this
30166 * @param {Object} file
30171 * Fire when xhr upload the file
30172 * @param {Roo.bootstrap.UploadCropbox} this
30173 * @param {Object} data
30178 * Fire when arrange the file data
30179 * @param {Roo.bootstrap.UploadCropbox} this
30180 * @param {Object} formData
30185 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30188 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
30190 emptyText : 'Click to upload image',
30191 rotateNotify : 'Image is too small to rotate',
30192 errorTimeout : 3000,
30206 cropType : 'image/jpeg',
30208 canvasLoaded : false,
30209 isDocument : false,
30211 paramName : 'imageUpload',
30213 loadingText : 'Loading...',
30216 getAutoCreate : function()
30220 cls : 'roo-upload-cropbox',
30224 cls : 'roo-upload-cropbox-selector',
30229 cls : 'roo-upload-cropbox-body',
30230 style : 'cursor:pointer',
30234 cls : 'roo-upload-cropbox-preview'
30238 cls : 'roo-upload-cropbox-thumb'
30242 cls : 'roo-upload-cropbox-empty-notify',
30243 html : this.emptyText
30247 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30248 html : this.rotateNotify
30254 cls : 'roo-upload-cropbox-footer',
30257 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30267 onRender : function(ct, position)
30269 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30271 if (this.buttons.length) {
30273 Roo.each(this.buttons, function(bb) {
30275 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30277 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30283 this.maskEl = this.el;
30287 initEvents : function()
30289 this.urlAPI = (window.createObjectURL && window) ||
30290 (window.URL && URL.revokeObjectURL && URL) ||
30291 (window.webkitURL && webkitURL);
30293 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30294 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30296 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30297 this.selectorEl.hide();
30299 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30300 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30302 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30303 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30304 this.thumbEl.hide();
30306 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30307 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30309 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30310 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30311 this.errorEl.hide();
30313 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30314 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30315 this.footerEl.hide();
30317 this.setThumbBoxSize();
30323 this.fireEvent('initial', this);
30330 window.addEventListener("resize", function() { _this.resize(); } );
30332 this.bodyEl.on('click', this.beforeSelectFile, this);
30335 this.bodyEl.on('touchstart', this.onTouchStart, this);
30336 this.bodyEl.on('touchmove', this.onTouchMove, this);
30337 this.bodyEl.on('touchend', this.onTouchEnd, this);
30341 this.bodyEl.on('mousedown', this.onMouseDown, this);
30342 this.bodyEl.on('mousemove', this.onMouseMove, this);
30343 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30344 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30345 Roo.get(document).on('mouseup', this.onMouseUp, this);
30348 this.selectorEl.on('change', this.onFileSelected, this);
30354 this.baseScale = 1;
30356 this.baseRotate = 1;
30357 this.dragable = false;
30358 this.pinching = false;
30361 this.cropData = false;
30362 this.notifyEl.dom.innerHTML = this.emptyText;
30364 this.selectorEl.dom.value = '';
30368 resize : function()
30370 if(this.fireEvent('resize', this) != false){
30371 this.setThumbBoxPosition();
30372 this.setCanvasPosition();
30376 onFooterButtonClick : function(e, el, o, type)
30379 case 'rotate-left' :
30380 this.onRotateLeft(e);
30382 case 'rotate-right' :
30383 this.onRotateRight(e);
30386 this.beforeSelectFile(e);
30401 this.fireEvent('footerbuttonclick', this, type);
30404 beforeSelectFile : function(e)
30406 e.preventDefault();
30408 if(this.fireEvent('beforeselectfile', this) != false){
30409 this.selectorEl.dom.click();
30413 onFileSelected : function(e)
30415 e.preventDefault();
30417 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30421 var file = this.selectorEl.dom.files[0];
30423 if(this.fireEvent('inspect', this, file) != false){
30424 this.prepare(file);
30429 trash : function(e)
30431 this.fireEvent('trash', this);
30434 download : function(e)
30436 this.fireEvent('download', this);
30439 loadCanvas : function(src)
30441 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30445 this.imageEl = document.createElement('img');
30449 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30451 this.imageEl.src = src;
30455 onLoadCanvas : function()
30457 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30458 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30460 this.bodyEl.un('click', this.beforeSelectFile, this);
30462 this.notifyEl.hide();
30463 this.thumbEl.show();
30464 this.footerEl.show();
30466 this.baseRotateLevel();
30468 if(this.isDocument){
30469 this.setThumbBoxSize();
30472 this.setThumbBoxPosition();
30474 this.baseScaleLevel();
30480 this.canvasLoaded = true;
30483 this.maskEl.unmask();
30488 setCanvasPosition : function()
30490 if(!this.canvasEl){
30494 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30495 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30497 this.previewEl.setLeft(pw);
30498 this.previewEl.setTop(ph);
30502 onMouseDown : function(e)
30506 this.dragable = true;
30507 this.pinching = false;
30509 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30510 this.dragable = false;
30514 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30515 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30519 onMouseMove : function(e)
30523 if(!this.canvasLoaded){
30527 if (!this.dragable){
30531 var minX = Math.ceil(this.thumbEl.getLeft(true));
30532 var minY = Math.ceil(this.thumbEl.getTop(true));
30534 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30535 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30537 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30538 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30540 x = x - this.mouseX;
30541 y = y - this.mouseY;
30543 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30544 var bgY = Math.ceil(y + this.previewEl.getTop(true));
30546 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30547 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30549 this.previewEl.setLeft(bgX);
30550 this.previewEl.setTop(bgY);
30552 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30553 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30556 onMouseUp : function(e)
30560 this.dragable = false;
30563 onMouseWheel : function(e)
30567 this.startScale = this.scale;
30569 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30571 if(!this.zoomable()){
30572 this.scale = this.startScale;
30581 zoomable : function()
30583 var minScale = this.thumbEl.getWidth() / this.minWidth;
30585 if(this.minWidth < this.minHeight){
30586 minScale = this.thumbEl.getHeight() / this.minHeight;
30589 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30590 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30594 (this.rotate == 0 || this.rotate == 180) &&
30596 width > this.imageEl.OriginWidth ||
30597 height > this.imageEl.OriginHeight ||
30598 (width < this.minWidth && height < this.minHeight)
30606 (this.rotate == 90 || this.rotate == 270) &&
30608 width > this.imageEl.OriginWidth ||
30609 height > this.imageEl.OriginHeight ||
30610 (width < this.minHeight && height < this.minWidth)
30617 !this.isDocument &&
30618 (this.rotate == 0 || this.rotate == 180) &&
30620 width < this.minWidth ||
30621 width > this.imageEl.OriginWidth ||
30622 height < this.minHeight ||
30623 height > this.imageEl.OriginHeight
30630 !this.isDocument &&
30631 (this.rotate == 90 || this.rotate == 270) &&
30633 width < this.minHeight ||
30634 width > this.imageEl.OriginWidth ||
30635 height < this.minWidth ||
30636 height > this.imageEl.OriginHeight
30646 onRotateLeft : function(e)
30648 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30650 var minScale = this.thumbEl.getWidth() / this.minWidth;
30652 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30653 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30655 this.startScale = this.scale;
30657 while (this.getScaleLevel() < minScale){
30659 this.scale = this.scale + 1;
30661 if(!this.zoomable()){
30666 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30667 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30672 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30679 this.scale = this.startScale;
30681 this.onRotateFail();
30686 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30688 if(this.isDocument){
30689 this.setThumbBoxSize();
30690 this.setThumbBoxPosition();
30691 this.setCanvasPosition();
30696 this.fireEvent('rotate', this, 'left');
30700 onRotateRight : function(e)
30702 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30704 var minScale = this.thumbEl.getWidth() / this.minWidth;
30706 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30707 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30709 this.startScale = this.scale;
30711 while (this.getScaleLevel() < minScale){
30713 this.scale = this.scale + 1;
30715 if(!this.zoomable()){
30720 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30721 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30726 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30733 this.scale = this.startScale;
30735 this.onRotateFail();
30740 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30742 if(this.isDocument){
30743 this.setThumbBoxSize();
30744 this.setThumbBoxPosition();
30745 this.setCanvasPosition();
30750 this.fireEvent('rotate', this, 'right');
30753 onRotateFail : function()
30755 this.errorEl.show(true);
30759 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30764 this.previewEl.dom.innerHTML = '';
30766 var canvasEl = document.createElement("canvas");
30768 var contextEl = canvasEl.getContext("2d");
30770 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30771 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30772 var center = this.imageEl.OriginWidth / 2;
30774 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30775 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30776 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30777 center = this.imageEl.OriginHeight / 2;
30780 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30782 contextEl.translate(center, center);
30783 contextEl.rotate(this.rotate * Math.PI / 180);
30785 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30787 this.canvasEl = document.createElement("canvas");
30789 this.contextEl = this.canvasEl.getContext("2d");
30791 switch (this.rotate) {
30794 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30795 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30797 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30802 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30803 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30805 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30806 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);
30810 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30815 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30816 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30818 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30819 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);
30823 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);
30828 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30829 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30831 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30832 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30836 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);
30843 this.previewEl.appendChild(this.canvasEl);
30845 this.setCanvasPosition();
30850 if(!this.canvasLoaded){
30854 var imageCanvas = document.createElement("canvas");
30856 var imageContext = imageCanvas.getContext("2d");
30858 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30859 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30861 var center = imageCanvas.width / 2;
30863 imageContext.translate(center, center);
30865 imageContext.rotate(this.rotate * Math.PI / 180);
30867 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30869 var canvas = document.createElement("canvas");
30871 var context = canvas.getContext("2d");
30873 canvas.width = this.minWidth;
30874 canvas.height = this.minHeight;
30876 switch (this.rotate) {
30879 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30880 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30882 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30883 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30885 var targetWidth = this.minWidth - 2 * x;
30886 var targetHeight = this.minHeight - 2 * y;
30890 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30891 scale = targetWidth / width;
30894 if(x > 0 && y == 0){
30895 scale = targetHeight / height;
30898 if(x > 0 && y > 0){
30899 scale = targetWidth / width;
30901 if(width < height){
30902 scale = targetHeight / height;
30906 context.scale(scale, scale);
30908 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30909 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30911 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30912 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30914 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30919 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30920 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30922 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30923 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30925 var targetWidth = this.minWidth - 2 * x;
30926 var targetHeight = this.minHeight - 2 * y;
30930 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30931 scale = targetWidth / width;
30934 if(x > 0 && y == 0){
30935 scale = targetHeight / height;
30938 if(x > 0 && y > 0){
30939 scale = targetWidth / width;
30941 if(width < height){
30942 scale = targetHeight / height;
30946 context.scale(scale, scale);
30948 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30949 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30951 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30952 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30954 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30956 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30961 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30962 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30964 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30965 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30967 var targetWidth = this.minWidth - 2 * x;
30968 var targetHeight = this.minHeight - 2 * y;
30972 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30973 scale = targetWidth / width;
30976 if(x > 0 && y == 0){
30977 scale = targetHeight / height;
30980 if(x > 0 && y > 0){
30981 scale = targetWidth / width;
30983 if(width < height){
30984 scale = targetHeight / height;
30988 context.scale(scale, scale);
30990 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30991 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30993 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30994 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30996 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30997 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30999 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31004 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31005 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31007 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31008 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31010 var targetWidth = this.minWidth - 2 * x;
31011 var targetHeight = this.minHeight - 2 * y;
31015 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31016 scale = targetWidth / width;
31019 if(x > 0 && y == 0){
31020 scale = targetHeight / height;
31023 if(x > 0 && y > 0){
31024 scale = targetWidth / width;
31026 if(width < height){
31027 scale = targetHeight / height;
31031 context.scale(scale, scale);
31033 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31034 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31036 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31037 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31039 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31041 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31048 this.cropData = canvas.toDataURL(this.cropType);
31050 if(this.fireEvent('crop', this, this.cropData) !== false){
31051 this.process(this.file, this.cropData);
31058 setThumbBoxSize : function()
31062 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31063 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31064 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31066 this.minWidth = width;
31067 this.minHeight = height;
31069 if(this.rotate == 90 || this.rotate == 270){
31070 this.minWidth = height;
31071 this.minHeight = width;
31076 width = Math.ceil(this.minWidth * height / this.minHeight);
31078 if(this.minWidth > this.minHeight){
31080 height = Math.ceil(this.minHeight * width / this.minWidth);
31083 this.thumbEl.setStyle({
31084 width : width + 'px',
31085 height : height + 'px'
31092 setThumbBoxPosition : function()
31094 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31095 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31097 this.thumbEl.setLeft(x);
31098 this.thumbEl.setTop(y);
31102 baseRotateLevel : function()
31104 this.baseRotate = 1;
31107 typeof(this.exif) != 'undefined' &&
31108 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31109 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31111 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31114 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31118 baseScaleLevel : function()
31122 if(this.isDocument){
31124 if(this.baseRotate == 6 || this.baseRotate == 8){
31126 height = this.thumbEl.getHeight();
31127 this.baseScale = height / this.imageEl.OriginWidth;
31129 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31130 width = this.thumbEl.getWidth();
31131 this.baseScale = width / this.imageEl.OriginHeight;
31137 height = this.thumbEl.getHeight();
31138 this.baseScale = height / this.imageEl.OriginHeight;
31140 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31141 width = this.thumbEl.getWidth();
31142 this.baseScale = width / this.imageEl.OriginWidth;
31148 if(this.baseRotate == 6 || this.baseRotate == 8){
31150 width = this.thumbEl.getHeight();
31151 this.baseScale = width / this.imageEl.OriginHeight;
31153 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31154 height = this.thumbEl.getWidth();
31155 this.baseScale = height / this.imageEl.OriginHeight;
31158 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31159 height = this.thumbEl.getWidth();
31160 this.baseScale = height / this.imageEl.OriginHeight;
31162 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31163 width = this.thumbEl.getHeight();
31164 this.baseScale = width / this.imageEl.OriginWidth;
31171 width = this.thumbEl.getWidth();
31172 this.baseScale = width / this.imageEl.OriginWidth;
31174 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31175 height = this.thumbEl.getHeight();
31176 this.baseScale = height / this.imageEl.OriginHeight;
31179 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31181 height = this.thumbEl.getHeight();
31182 this.baseScale = height / this.imageEl.OriginHeight;
31184 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31185 width = this.thumbEl.getWidth();
31186 this.baseScale = width / this.imageEl.OriginWidth;
31194 getScaleLevel : function()
31196 return this.baseScale * Math.pow(1.1, this.scale);
31199 onTouchStart : function(e)
31201 if(!this.canvasLoaded){
31202 this.beforeSelectFile(e);
31206 var touches = e.browserEvent.touches;
31212 if(touches.length == 1){
31213 this.onMouseDown(e);
31217 if(touches.length != 2){
31223 for(var i = 0, finger; finger = touches[i]; i++){
31224 coords.push(finger.pageX, finger.pageY);
31227 var x = Math.pow(coords[0] - coords[2], 2);
31228 var y = Math.pow(coords[1] - coords[3], 2);
31230 this.startDistance = Math.sqrt(x + y);
31232 this.startScale = this.scale;
31234 this.pinching = true;
31235 this.dragable = false;
31239 onTouchMove : function(e)
31241 if(!this.pinching && !this.dragable){
31245 var touches = e.browserEvent.touches;
31252 this.onMouseMove(e);
31258 for(var i = 0, finger; finger = touches[i]; i++){
31259 coords.push(finger.pageX, finger.pageY);
31262 var x = Math.pow(coords[0] - coords[2], 2);
31263 var y = Math.pow(coords[1] - coords[3], 2);
31265 this.endDistance = Math.sqrt(x + y);
31267 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31269 if(!this.zoomable()){
31270 this.scale = this.startScale;
31278 onTouchEnd : function(e)
31280 this.pinching = false;
31281 this.dragable = false;
31285 process : function(file, crop)
31288 this.maskEl.mask(this.loadingText);
31291 this.xhr = new XMLHttpRequest();
31293 file.xhr = this.xhr;
31295 this.xhr.open(this.method, this.url, true);
31298 "Accept": "application/json",
31299 "Cache-Control": "no-cache",
31300 "X-Requested-With": "XMLHttpRequest"
31303 for (var headerName in headers) {
31304 var headerValue = headers[headerName];
31306 this.xhr.setRequestHeader(headerName, headerValue);
31312 this.xhr.onload = function()
31314 _this.xhrOnLoad(_this.xhr);
31317 this.xhr.onerror = function()
31319 _this.xhrOnError(_this.xhr);
31322 var formData = new FormData();
31324 formData.append('returnHTML', 'NO');
31327 formData.append('crop', crop);
31330 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31331 formData.append(this.paramName, file, file.name);
31334 if(typeof(file.filename) != 'undefined'){
31335 formData.append('filename', file.filename);
31338 if(typeof(file.mimetype) != 'undefined'){
31339 formData.append('mimetype', file.mimetype);
31342 if(this.fireEvent('arrange', this, formData) != false){
31343 this.xhr.send(formData);
31347 xhrOnLoad : function(xhr)
31350 this.maskEl.unmask();
31353 if (xhr.readyState !== 4) {
31354 this.fireEvent('exception', this, xhr);
31358 var response = Roo.decode(xhr.responseText);
31360 if(!response.success){
31361 this.fireEvent('exception', this, xhr);
31365 var response = Roo.decode(xhr.responseText);
31367 this.fireEvent('upload', this, response);
31371 xhrOnError : function()
31374 this.maskEl.unmask();
31377 Roo.log('xhr on error');
31379 var response = Roo.decode(xhr.responseText);
31385 prepare : function(file)
31388 this.maskEl.mask(this.loadingText);
31394 if(typeof(file) === 'string'){
31395 this.loadCanvas(file);
31399 if(!file || !this.urlAPI){
31404 this.cropType = file.type;
31408 if(this.fireEvent('prepare', this, this.file) != false){
31410 var reader = new FileReader();
31412 reader.onload = function (e) {
31413 if (e.target.error) {
31414 Roo.log(e.target.error);
31418 var buffer = e.target.result,
31419 dataView = new DataView(buffer),
31421 maxOffset = dataView.byteLength - 4,
31425 if (dataView.getUint16(0) === 0xffd8) {
31426 while (offset < maxOffset) {
31427 markerBytes = dataView.getUint16(offset);
31429 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31430 markerLength = dataView.getUint16(offset + 2) + 2;
31431 if (offset + markerLength > dataView.byteLength) {
31432 Roo.log('Invalid meta data: Invalid segment size.');
31436 if(markerBytes == 0xffe1){
31437 _this.parseExifData(
31444 offset += markerLength;
31454 var url = _this.urlAPI.createObjectURL(_this.file);
31456 _this.loadCanvas(url);
31461 reader.readAsArrayBuffer(this.file);
31467 parseExifData : function(dataView, offset, length)
31469 var tiffOffset = offset + 10,
31473 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31474 // No Exif data, might be XMP data instead
31478 // Check for the ASCII code for "Exif" (0x45786966):
31479 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31480 // No Exif data, might be XMP data instead
31483 if (tiffOffset + 8 > dataView.byteLength) {
31484 Roo.log('Invalid Exif data: Invalid segment size.');
31487 // Check for the two null bytes:
31488 if (dataView.getUint16(offset + 8) !== 0x0000) {
31489 Roo.log('Invalid Exif data: Missing byte alignment offset.');
31492 // Check the byte alignment:
31493 switch (dataView.getUint16(tiffOffset)) {
31495 littleEndian = true;
31498 littleEndian = false;
31501 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31504 // Check for the TIFF tag marker (0x002A):
31505 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31506 Roo.log('Invalid Exif data: Missing TIFF marker.');
31509 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31510 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31512 this.parseExifTags(
31515 tiffOffset + dirOffset,
31520 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31525 if (dirOffset + 6 > dataView.byteLength) {
31526 Roo.log('Invalid Exif data: Invalid directory offset.');
31529 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31530 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31531 if (dirEndOffset + 4 > dataView.byteLength) {
31532 Roo.log('Invalid Exif data: Invalid directory size.');
31535 for (i = 0; i < tagsNumber; i += 1) {
31539 dirOffset + 2 + 12 * i, // tag offset
31543 // Return the offset to the next directory:
31544 return dataView.getUint32(dirEndOffset, littleEndian);
31547 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
31549 var tag = dataView.getUint16(offset, littleEndian);
31551 this.exif[tag] = this.getExifValue(
31555 dataView.getUint16(offset + 2, littleEndian), // tag type
31556 dataView.getUint32(offset + 4, littleEndian), // tag length
31561 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31563 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31572 Roo.log('Invalid Exif data: Invalid tag type.');
31576 tagSize = tagType.size * length;
31577 // Determine if the value is contained in the dataOffset bytes,
31578 // or if the value at the dataOffset is a pointer to the actual data:
31579 dataOffset = tagSize > 4 ?
31580 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31581 if (dataOffset + tagSize > dataView.byteLength) {
31582 Roo.log('Invalid Exif data: Invalid data offset.');
31585 if (length === 1) {
31586 return tagType.getValue(dataView, dataOffset, littleEndian);
31589 for (i = 0; i < length; i += 1) {
31590 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31593 if (tagType.ascii) {
31595 // Concatenate the chars:
31596 for (i = 0; i < values.length; i += 1) {
31598 // Ignore the terminating NULL byte(s):
31599 if (c === '\u0000') {
31611 Roo.apply(Roo.bootstrap.UploadCropbox, {
31613 'Orientation': 0x0112
31617 1: 0, //'top-left',
31619 3: 180, //'bottom-right',
31620 // 4: 'bottom-left',
31622 6: 90, //'right-top',
31623 // 7: 'right-bottom',
31624 8: 270 //'left-bottom'
31628 // byte, 8-bit unsigned int:
31630 getValue: function (dataView, dataOffset) {
31631 return dataView.getUint8(dataOffset);
31635 // ascii, 8-bit byte:
31637 getValue: function (dataView, dataOffset) {
31638 return String.fromCharCode(dataView.getUint8(dataOffset));
31643 // short, 16 bit int:
31645 getValue: function (dataView, dataOffset, littleEndian) {
31646 return dataView.getUint16(dataOffset, littleEndian);
31650 // long, 32 bit int:
31652 getValue: function (dataView, dataOffset, littleEndian) {
31653 return dataView.getUint32(dataOffset, littleEndian);
31657 // rational = two long values, first is numerator, second is denominator:
31659 getValue: function (dataView, dataOffset, littleEndian) {
31660 return dataView.getUint32(dataOffset, littleEndian) /
31661 dataView.getUint32(dataOffset + 4, littleEndian);
31665 // slong, 32 bit signed int:
31667 getValue: function (dataView, dataOffset, littleEndian) {
31668 return dataView.getInt32(dataOffset, littleEndian);
31672 // srational, two slongs, first is numerator, second is denominator:
31674 getValue: function (dataView, dataOffset, littleEndian) {
31675 return dataView.getInt32(dataOffset, littleEndian) /
31676 dataView.getInt32(dataOffset + 4, littleEndian);
31686 cls : 'btn-group roo-upload-cropbox-rotate-left',
31687 action : 'rotate-left',
31691 cls : 'btn btn-default',
31692 html : '<i class="fa fa-undo"></i>'
31698 cls : 'btn-group roo-upload-cropbox-picture',
31699 action : 'picture',
31703 cls : 'btn btn-default',
31704 html : '<i class="fa fa-picture-o"></i>'
31710 cls : 'btn-group roo-upload-cropbox-rotate-right',
31711 action : 'rotate-right',
31715 cls : 'btn btn-default',
31716 html : '<i class="fa fa-repeat"></i>'
31724 cls : 'btn-group roo-upload-cropbox-rotate-left',
31725 action : 'rotate-left',
31729 cls : 'btn btn-default',
31730 html : '<i class="fa fa-undo"></i>'
31736 cls : 'btn-group roo-upload-cropbox-download',
31737 action : 'download',
31741 cls : 'btn btn-default',
31742 html : '<i class="fa fa-download"></i>'
31748 cls : 'btn-group roo-upload-cropbox-crop',
31753 cls : 'btn btn-default',
31754 html : '<i class="fa fa-crop"></i>'
31760 cls : 'btn-group roo-upload-cropbox-trash',
31765 cls : 'btn btn-default',
31766 html : '<i class="fa fa-trash"></i>'
31772 cls : 'btn-group roo-upload-cropbox-rotate-right',
31773 action : 'rotate-right',
31777 cls : 'btn btn-default',
31778 html : '<i class="fa fa-repeat"></i>'
31786 cls : 'btn-group roo-upload-cropbox-rotate-left',
31787 action : 'rotate-left',
31791 cls : 'btn btn-default',
31792 html : '<i class="fa fa-undo"></i>'
31798 cls : 'btn-group roo-upload-cropbox-rotate-right',
31799 action : 'rotate-right',
31803 cls : 'btn btn-default',
31804 html : '<i class="fa fa-repeat"></i>'
31817 * @class Roo.bootstrap.DocumentManager
31818 * @extends Roo.bootstrap.Component
31819 * Bootstrap DocumentManager class
31820 * @cfg {String} paramName default 'imageUpload'
31821 * @cfg {String} toolTipName default 'filename'
31822 * @cfg {String} method default POST
31823 * @cfg {String} url action url
31824 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31825 * @cfg {Boolean} multiple multiple upload default true
31826 * @cfg {Number} thumbSize default 300
31827 * @cfg {String} fieldLabel
31828 * @cfg {Number} labelWidth default 4
31829 * @cfg {String} labelAlign (left|top) default left
31830 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31831 * @cfg {Number} labellg set the width of label (1-12)
31832 * @cfg {Number} labelmd set the width of label (1-12)
31833 * @cfg {Number} labelsm set the width of label (1-12)
31834 * @cfg {Number} labelxs set the width of label (1-12)
31837 * Create a new DocumentManager
31838 * @param {Object} config The config object
31841 Roo.bootstrap.DocumentManager = function(config){
31842 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31845 this.delegates = [];
31850 * Fire when initial the DocumentManager
31851 * @param {Roo.bootstrap.DocumentManager} this
31856 * inspect selected file
31857 * @param {Roo.bootstrap.DocumentManager} this
31858 * @param {File} file
31863 * Fire when xhr load exception
31864 * @param {Roo.bootstrap.DocumentManager} this
31865 * @param {XMLHttpRequest} xhr
31867 "exception" : true,
31869 * @event afterupload
31870 * Fire when xhr load exception
31871 * @param {Roo.bootstrap.DocumentManager} this
31872 * @param {XMLHttpRequest} xhr
31874 "afterupload" : true,
31877 * prepare the form data
31878 * @param {Roo.bootstrap.DocumentManager} this
31879 * @param {Object} formData
31884 * Fire when remove the file
31885 * @param {Roo.bootstrap.DocumentManager} this
31886 * @param {Object} file
31891 * Fire after refresh the file
31892 * @param {Roo.bootstrap.DocumentManager} this
31897 * Fire after click the image
31898 * @param {Roo.bootstrap.DocumentManager} this
31899 * @param {Object} file
31904 * Fire when upload a image and editable set to true
31905 * @param {Roo.bootstrap.DocumentManager} this
31906 * @param {Object} file
31910 * @event beforeselectfile
31911 * Fire before select file
31912 * @param {Roo.bootstrap.DocumentManager} this
31914 "beforeselectfile" : true,
31917 * Fire before process file
31918 * @param {Roo.bootstrap.DocumentManager} this
31919 * @param {Object} file
31923 * @event previewrendered
31924 * Fire when preview rendered
31925 * @param {Roo.bootstrap.DocumentManager} this
31926 * @param {Object} file
31928 "previewrendered" : true,
31931 "previewResize" : true
31936 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
31945 paramName : 'imageUpload',
31946 toolTipName : 'filename',
31949 labelAlign : 'left',
31959 getAutoCreate : function()
31961 var managerWidget = {
31963 cls : 'roo-document-manager',
31967 cls : 'roo-document-manager-selector',
31972 cls : 'roo-document-manager-uploader',
31976 cls : 'roo-document-manager-upload-btn',
31977 html : '<i class="fa fa-plus"></i>'
31988 cls : 'column col-md-12',
31993 if(this.fieldLabel.length){
31998 cls : 'column col-md-12',
31999 html : this.fieldLabel
32003 cls : 'column col-md-12',
32008 if(this.labelAlign == 'left'){
32013 html : this.fieldLabel
32022 if(this.labelWidth > 12){
32023 content[0].style = "width: " + this.labelWidth + 'px';
32026 if(this.labelWidth < 13 && this.labelmd == 0){
32027 this.labelmd = this.labelWidth;
32030 if(this.labellg > 0){
32031 content[0].cls += ' col-lg-' + this.labellg;
32032 content[1].cls += ' col-lg-' + (12 - this.labellg);
32035 if(this.labelmd > 0){
32036 content[0].cls += ' col-md-' + this.labelmd;
32037 content[1].cls += ' col-md-' + (12 - this.labelmd);
32040 if(this.labelsm > 0){
32041 content[0].cls += ' col-sm-' + this.labelsm;
32042 content[1].cls += ' col-sm-' + (12 - this.labelsm);
32045 if(this.labelxs > 0){
32046 content[0].cls += ' col-xs-' + this.labelxs;
32047 content[1].cls += ' col-xs-' + (12 - this.labelxs);
32055 cls : 'row clearfix',
32063 initEvents : function()
32065 this.managerEl = this.el.select('.roo-document-manager', true).first();
32066 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32068 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32069 this.selectorEl.hide();
32072 this.selectorEl.attr('multiple', 'multiple');
32075 this.selectorEl.on('change', this.onFileSelected, this);
32077 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32078 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32080 this.uploader.on('click', this.onUploaderClick, this);
32082 this.renderProgressDialog();
32086 window.addEventListener("resize", function() { _this.refresh(); } );
32088 this.fireEvent('initial', this);
32091 renderProgressDialog : function()
32095 this.progressDialog = new Roo.bootstrap.Modal({
32096 cls : 'roo-document-manager-progress-dialog',
32097 allow_close : false,
32108 btnclick : function() {
32109 _this.uploadCancel();
32115 this.progressDialog.render(Roo.get(document.body));
32117 this.progress = new Roo.bootstrap.Progress({
32118 cls : 'roo-document-manager-progress',
32123 this.progress.render(this.progressDialog.getChildContainer());
32125 this.progressBar = new Roo.bootstrap.ProgressBar({
32126 cls : 'roo-document-manager-progress-bar',
32129 aria_valuemax : 12,
32133 this.progressBar.render(this.progress.getChildContainer());
32136 onUploaderClick : function(e)
32138 e.preventDefault();
32140 if(this.fireEvent('beforeselectfile', this) != false){
32141 this.selectorEl.dom.click();
32146 onFileSelected : function(e)
32148 e.preventDefault();
32150 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32154 Roo.each(this.selectorEl.dom.files, function(file){
32155 if(this.fireEvent('inspect', this, file) != false){
32156 this.files.push(file);
32166 this.selectorEl.dom.value = '';
32168 if(!this.files || !this.files.length){
32172 if(this.boxes > 0 && this.files.length > this.boxes){
32173 this.files = this.files.slice(0, this.boxes);
32176 this.uploader.show();
32178 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32179 this.uploader.hide();
32188 Roo.each(this.files, function(file){
32190 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32191 var f = this.renderPreview(file);
32196 if(file.type.indexOf('image') != -1){
32197 this.delegates.push(
32199 _this.process(file);
32200 }).createDelegate(this)
32208 _this.process(file);
32209 }).createDelegate(this)
32214 this.files = files;
32216 this.delegates = this.delegates.concat(docs);
32218 if(!this.delegates.length){
32223 this.progressBar.aria_valuemax = this.delegates.length;
32230 arrange : function()
32232 if(!this.delegates.length){
32233 this.progressDialog.hide();
32238 var delegate = this.delegates.shift();
32240 this.progressDialog.show();
32242 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32244 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32249 refresh : function()
32251 this.uploader.show();
32253 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32254 this.uploader.hide();
32257 Roo.isTouch ? this.closable(false) : this.closable(true);
32259 this.fireEvent('refresh', this);
32262 onRemove : function(e, el, o)
32264 e.preventDefault();
32266 this.fireEvent('remove', this, o);
32270 remove : function(o)
32274 Roo.each(this.files, function(file){
32275 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32284 this.files = files;
32291 Roo.each(this.files, function(file){
32296 file.target.remove();
32305 onClick : function(e, el, o)
32307 e.preventDefault();
32309 this.fireEvent('click', this, o);
32313 closable : function(closable)
32315 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32317 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32329 xhrOnLoad : function(xhr)
32331 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32335 if (xhr.readyState !== 4) {
32337 this.fireEvent('exception', this, xhr);
32341 var response = Roo.decode(xhr.responseText);
32343 if(!response.success){
32345 this.fireEvent('exception', this, xhr);
32349 var file = this.renderPreview(response.data);
32351 this.files.push(file);
32355 this.fireEvent('afterupload', this, xhr);
32359 xhrOnError : function(xhr)
32361 Roo.log('xhr on error');
32363 var response = Roo.decode(xhr.responseText);
32370 process : function(file)
32372 if(this.fireEvent('process', this, file) !== false){
32373 if(this.editable && file.type.indexOf('image') != -1){
32374 this.fireEvent('edit', this, file);
32378 this.uploadStart(file, false);
32385 uploadStart : function(file, crop)
32387 this.xhr = new XMLHttpRequest();
32389 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32394 file.xhr = this.xhr;
32396 this.managerEl.createChild({
32398 cls : 'roo-document-manager-loading',
32402 tooltip : file.name,
32403 cls : 'roo-document-manager-thumb',
32404 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32410 this.xhr.open(this.method, this.url, true);
32413 "Accept": "application/json",
32414 "Cache-Control": "no-cache",
32415 "X-Requested-With": "XMLHttpRequest"
32418 for (var headerName in headers) {
32419 var headerValue = headers[headerName];
32421 this.xhr.setRequestHeader(headerName, headerValue);
32427 this.xhr.onload = function()
32429 _this.xhrOnLoad(_this.xhr);
32432 this.xhr.onerror = function()
32434 _this.xhrOnError(_this.xhr);
32437 var formData = new FormData();
32439 formData.append('returnHTML', 'NO');
32442 formData.append('crop', crop);
32445 formData.append(this.paramName, file, file.name);
32452 if(this.fireEvent('prepare', this, formData, options) != false){
32454 if(options.manually){
32458 this.xhr.send(formData);
32462 this.uploadCancel();
32465 uploadCancel : function()
32471 this.delegates = [];
32473 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32480 renderPreview : function(file)
32482 if(typeof(file.target) != 'undefined' && file.target){
32486 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32488 var previewEl = this.managerEl.createChild({
32490 cls : 'roo-document-manager-preview',
32494 tooltip : file[this.toolTipName],
32495 cls : 'roo-document-manager-thumb',
32496 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32501 html : '<i class="fa fa-times-circle"></i>'
32506 var close = previewEl.select('button.close', true).first();
32508 close.on('click', this.onRemove, this, file);
32510 file.target = previewEl;
32512 var image = previewEl.select('img', true).first();
32516 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32518 image.on('click', this.onClick, this, file);
32520 this.fireEvent('previewrendered', this, file);
32526 onPreviewLoad : function(file, image)
32528 if(typeof(file.target) == 'undefined' || !file.target){
32532 var width = image.dom.naturalWidth || image.dom.width;
32533 var height = image.dom.naturalHeight || image.dom.height;
32535 if(!this.previewResize) {
32539 if(width > height){
32540 file.target.addClass('wide');
32544 file.target.addClass('tall');
32549 uploadFromSource : function(file, crop)
32551 this.xhr = new XMLHttpRequest();
32553 this.managerEl.createChild({
32555 cls : 'roo-document-manager-loading',
32559 tooltip : file.name,
32560 cls : 'roo-document-manager-thumb',
32561 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32567 this.xhr.open(this.method, this.url, true);
32570 "Accept": "application/json",
32571 "Cache-Control": "no-cache",
32572 "X-Requested-With": "XMLHttpRequest"
32575 for (var headerName in headers) {
32576 var headerValue = headers[headerName];
32578 this.xhr.setRequestHeader(headerName, headerValue);
32584 this.xhr.onload = function()
32586 _this.xhrOnLoad(_this.xhr);
32589 this.xhr.onerror = function()
32591 _this.xhrOnError(_this.xhr);
32594 var formData = new FormData();
32596 formData.append('returnHTML', 'NO');
32598 formData.append('crop', crop);
32600 if(typeof(file.filename) != 'undefined'){
32601 formData.append('filename', file.filename);
32604 if(typeof(file.mimetype) != 'undefined'){
32605 formData.append('mimetype', file.mimetype);
32610 if(this.fireEvent('prepare', this, formData) != false){
32611 this.xhr.send(formData);
32621 * @class Roo.bootstrap.DocumentViewer
32622 * @extends Roo.bootstrap.Component
32623 * Bootstrap DocumentViewer class
32624 * @cfg {Boolean} showDownload (true|false) show download button (default true)
32625 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32628 * Create a new DocumentViewer
32629 * @param {Object} config The config object
32632 Roo.bootstrap.DocumentViewer = function(config){
32633 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32638 * Fire after initEvent
32639 * @param {Roo.bootstrap.DocumentViewer} this
32645 * @param {Roo.bootstrap.DocumentViewer} this
32650 * Fire after download button
32651 * @param {Roo.bootstrap.DocumentViewer} this
32656 * Fire after trash button
32657 * @param {Roo.bootstrap.DocumentViewer} this
32664 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
32666 showDownload : true,
32670 getAutoCreate : function()
32674 cls : 'roo-document-viewer',
32678 cls : 'roo-document-viewer-body',
32682 cls : 'roo-document-viewer-thumb',
32686 cls : 'roo-document-viewer-image'
32694 cls : 'roo-document-viewer-footer',
32697 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32701 cls : 'btn-group roo-document-viewer-download',
32705 cls : 'btn btn-default',
32706 html : '<i class="fa fa-download"></i>'
32712 cls : 'btn-group roo-document-viewer-trash',
32716 cls : 'btn btn-default',
32717 html : '<i class="fa fa-trash"></i>'
32730 initEvents : function()
32732 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32733 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32735 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32736 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32738 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32739 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32741 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32742 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32744 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32745 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32747 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32748 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32750 this.bodyEl.on('click', this.onClick, this);
32751 this.downloadBtn.on('click', this.onDownload, this);
32752 this.trashBtn.on('click', this.onTrash, this);
32754 this.downloadBtn.hide();
32755 this.trashBtn.hide();
32757 if(this.showDownload){
32758 this.downloadBtn.show();
32761 if(this.showTrash){
32762 this.trashBtn.show();
32765 if(!this.showDownload && !this.showTrash) {
32766 this.footerEl.hide();
32771 initial : function()
32773 this.fireEvent('initial', this);
32777 onClick : function(e)
32779 e.preventDefault();
32781 this.fireEvent('click', this);
32784 onDownload : function(e)
32786 e.preventDefault();
32788 this.fireEvent('download', this);
32791 onTrash : function(e)
32793 e.preventDefault();
32795 this.fireEvent('trash', this);
32807 * @class Roo.bootstrap.NavProgressBar
32808 * @extends Roo.bootstrap.Component
32809 * Bootstrap NavProgressBar class
32812 * Create a new nav progress bar
32813 * @param {Object} config The config object
32816 Roo.bootstrap.NavProgressBar = function(config){
32817 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32819 this.bullets = this.bullets || [];
32821 // Roo.bootstrap.NavProgressBar.register(this);
32825 * Fires when the active item changes
32826 * @param {Roo.bootstrap.NavProgressBar} this
32827 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32828 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
32835 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
32840 getAutoCreate : function()
32842 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32846 cls : 'roo-navigation-bar-group',
32850 cls : 'roo-navigation-top-bar'
32854 cls : 'roo-navigation-bullets-bar',
32858 cls : 'roo-navigation-bar'
32865 cls : 'roo-navigation-bottom-bar'
32875 initEvents: function()
32880 onRender : function(ct, position)
32882 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32884 if(this.bullets.length){
32885 Roo.each(this.bullets, function(b){
32894 addItem : function(cfg)
32896 var item = new Roo.bootstrap.NavProgressItem(cfg);
32898 item.parentId = this.id;
32899 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32902 var top = new Roo.bootstrap.Element({
32904 cls : 'roo-navigation-bar-text'
32907 var bottom = new Roo.bootstrap.Element({
32909 cls : 'roo-navigation-bar-text'
32912 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32913 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32915 var topText = new Roo.bootstrap.Element({
32917 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32920 var bottomText = new Roo.bootstrap.Element({
32922 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32925 topText.onRender(top.el, null);
32926 bottomText.onRender(bottom.el, null);
32929 item.bottomEl = bottom;
32932 this.barItems.push(item);
32937 getActive : function()
32939 var active = false;
32941 Roo.each(this.barItems, function(v){
32943 if (!v.isActive()) {
32955 setActiveItem : function(item)
32959 Roo.each(this.barItems, function(v){
32960 if (v.rid == item.rid) {
32964 if (v.isActive()) {
32965 v.setActive(false);
32970 item.setActive(true);
32972 this.fireEvent('changed', this, item, prev);
32975 getBarItem: function(rid)
32979 Roo.each(this.barItems, function(e) {
32980 if (e.rid != rid) {
32991 indexOfItem : function(item)
32995 Roo.each(this.barItems, function(v, i){
32997 if (v.rid != item.rid) {
33008 setActiveNext : function()
33010 var i = this.indexOfItem(this.getActive());
33012 if (i > this.barItems.length) {
33016 this.setActiveItem(this.barItems[i+1]);
33019 setActivePrev : function()
33021 var i = this.indexOfItem(this.getActive());
33027 this.setActiveItem(this.barItems[i-1]);
33030 format : function()
33032 if(!this.barItems.length){
33036 var width = 100 / this.barItems.length;
33038 Roo.each(this.barItems, function(i){
33039 i.el.setStyle('width', width + '%');
33040 i.topEl.el.setStyle('width', width + '%');
33041 i.bottomEl.el.setStyle('width', width + '%');
33050 * Nav Progress Item
33055 * @class Roo.bootstrap.NavProgressItem
33056 * @extends Roo.bootstrap.Component
33057 * Bootstrap NavProgressItem class
33058 * @cfg {String} rid the reference id
33059 * @cfg {Boolean} active (true|false) Is item active default false
33060 * @cfg {Boolean} disabled (true|false) Is item active default false
33061 * @cfg {String} html
33062 * @cfg {String} position (top|bottom) text position default bottom
33063 * @cfg {String} icon show icon instead of number
33066 * Create a new NavProgressItem
33067 * @param {Object} config The config object
33069 Roo.bootstrap.NavProgressItem = function(config){
33070 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33075 * The raw click event for the entire grid.
33076 * @param {Roo.bootstrap.NavProgressItem} this
33077 * @param {Roo.EventObject} e
33084 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
33090 position : 'bottom',
33093 getAutoCreate : function()
33095 var iconCls = 'roo-navigation-bar-item-icon';
33097 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33101 cls: 'roo-navigation-bar-item',
33111 cfg.cls += ' active';
33114 cfg.cls += ' disabled';
33120 disable : function()
33122 this.setDisabled(true);
33125 enable : function()
33127 this.setDisabled(false);
33130 initEvents: function()
33132 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33134 this.iconEl.on('click', this.onClick, this);
33137 onClick : function(e)
33139 e.preventDefault();
33145 if(this.fireEvent('click', this, e) === false){
33149 this.parent().setActiveItem(this);
33152 isActive: function ()
33154 return this.active;
33157 setActive : function(state)
33159 if(this.active == state){
33163 this.active = state;
33166 this.el.addClass('active');
33170 this.el.removeClass('active');
33175 setDisabled : function(state)
33177 if(this.disabled == state){
33181 this.disabled = state;
33184 this.el.addClass('disabled');
33188 this.el.removeClass('disabled');
33191 tooltipEl : function()
33193 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33206 * @class Roo.bootstrap.FieldLabel
33207 * @extends Roo.bootstrap.Component
33208 * Bootstrap FieldLabel class
33209 * @cfg {String} html contents of the element
33210 * @cfg {String} tag tag of the element default label
33211 * @cfg {String} cls class of the element
33212 * @cfg {String} target label target
33213 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33214 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33215 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33216 * @cfg {String} iconTooltip default "This field is required"
33217 * @cfg {String} indicatorpos (left|right) default left
33220 * Create a new FieldLabel
33221 * @param {Object} config The config object
33224 Roo.bootstrap.FieldLabel = function(config){
33225 Roo.bootstrap.Element.superclass.constructor.call(this, config);
33230 * Fires after the field has been marked as invalid.
33231 * @param {Roo.form.FieldLabel} this
33232 * @param {String} msg The validation message
33237 * Fires after the field has been validated with no errors.
33238 * @param {Roo.form.FieldLabel} this
33244 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
33251 invalidClass : 'has-warning',
33252 validClass : 'has-success',
33253 iconTooltip : 'This field is required',
33254 indicatorpos : 'left',
33256 getAutoCreate : function(){
33259 if (!this.allowBlank) {
33265 cls : 'roo-bootstrap-field-label ' + this.cls,
33270 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33271 tooltip : this.iconTooltip
33280 if(this.indicatorpos == 'right'){
33283 cls : 'roo-bootstrap-field-label ' + this.cls,
33292 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33293 tooltip : this.iconTooltip
33302 initEvents: function()
33304 Roo.bootstrap.Element.superclass.initEvents.call(this);
33306 this.indicator = this.indicatorEl();
33308 if(this.indicator){
33309 this.indicator.removeClass('visible');
33310 this.indicator.addClass('invisible');
33313 Roo.bootstrap.FieldLabel.register(this);
33316 indicatorEl : function()
33318 var indicator = this.el.select('i.roo-required-indicator',true).first();
33329 * Mark this field as valid
33331 markValid : function()
33333 if(this.indicator){
33334 this.indicator.removeClass('visible');
33335 this.indicator.addClass('invisible');
33337 if (Roo.bootstrap.version == 3) {
33338 this.el.removeClass(this.invalidClass);
33339 this.el.addClass(this.validClass);
33341 this.el.removeClass('is-invalid');
33342 this.el.addClass('is-valid');
33346 this.fireEvent('valid', this);
33350 * Mark this field as invalid
33351 * @param {String} msg The validation message
33353 markInvalid : function(msg)
33355 if(this.indicator){
33356 this.indicator.removeClass('invisible');
33357 this.indicator.addClass('visible');
33359 if (Roo.bootstrap.version == 3) {
33360 this.el.removeClass(this.validClass);
33361 this.el.addClass(this.invalidClass);
33363 this.el.removeClass('is-valid');
33364 this.el.addClass('is-invalid');
33368 this.fireEvent('invalid', this, msg);
33374 Roo.apply(Roo.bootstrap.FieldLabel, {
33379 * register a FieldLabel Group
33380 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33382 register : function(label)
33384 if(this.groups.hasOwnProperty(label.target)){
33388 this.groups[label.target] = label;
33392 * fetch a FieldLabel Group based on the target
33393 * @param {string} target
33394 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33396 get: function(target) {
33397 if (typeof(this.groups[target]) == 'undefined') {
33401 return this.groups[target] ;
33410 * page DateSplitField.
33416 * @class Roo.bootstrap.DateSplitField
33417 * @extends Roo.bootstrap.Component
33418 * Bootstrap DateSplitField class
33419 * @cfg {string} fieldLabel - the label associated
33420 * @cfg {Number} labelWidth set the width of label (0-12)
33421 * @cfg {String} labelAlign (top|left)
33422 * @cfg {Boolean} dayAllowBlank (true|false) default false
33423 * @cfg {Boolean} monthAllowBlank (true|false) default false
33424 * @cfg {Boolean} yearAllowBlank (true|false) default false
33425 * @cfg {string} dayPlaceholder
33426 * @cfg {string} monthPlaceholder
33427 * @cfg {string} yearPlaceholder
33428 * @cfg {string} dayFormat default 'd'
33429 * @cfg {string} monthFormat default 'm'
33430 * @cfg {string} yearFormat default 'Y'
33431 * @cfg {Number} labellg set the width of label (1-12)
33432 * @cfg {Number} labelmd set the width of label (1-12)
33433 * @cfg {Number} labelsm set the width of label (1-12)
33434 * @cfg {Number} labelxs set the width of label (1-12)
33438 * Create a new DateSplitField
33439 * @param {Object} config The config object
33442 Roo.bootstrap.DateSplitField = function(config){
33443 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33449 * getting the data of years
33450 * @param {Roo.bootstrap.DateSplitField} this
33451 * @param {Object} years
33456 * getting the data of days
33457 * @param {Roo.bootstrap.DateSplitField} this
33458 * @param {Object} days
33463 * Fires after the field has been marked as invalid.
33464 * @param {Roo.form.Field} this
33465 * @param {String} msg The validation message
33470 * Fires after the field has been validated with no errors.
33471 * @param {Roo.form.Field} this
33477 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33480 labelAlign : 'top',
33482 dayAllowBlank : false,
33483 monthAllowBlank : false,
33484 yearAllowBlank : false,
33485 dayPlaceholder : '',
33486 monthPlaceholder : '',
33487 yearPlaceholder : '',
33491 isFormField : true,
33497 getAutoCreate : function()
33501 cls : 'row roo-date-split-field-group',
33506 cls : 'form-hidden-field roo-date-split-field-group-value',
33512 var labelCls = 'col-md-12';
33513 var contentCls = 'col-md-4';
33515 if(this.fieldLabel){
33519 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33523 html : this.fieldLabel
33528 if(this.labelAlign == 'left'){
33530 if(this.labelWidth > 12){
33531 label.style = "width: " + this.labelWidth + 'px';
33534 if(this.labelWidth < 13 && this.labelmd == 0){
33535 this.labelmd = this.labelWidth;
33538 if(this.labellg > 0){
33539 labelCls = ' col-lg-' + this.labellg;
33540 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33543 if(this.labelmd > 0){
33544 labelCls = ' col-md-' + this.labelmd;
33545 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33548 if(this.labelsm > 0){
33549 labelCls = ' col-sm-' + this.labelsm;
33550 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33553 if(this.labelxs > 0){
33554 labelCls = ' col-xs-' + this.labelxs;
33555 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33559 label.cls += ' ' + labelCls;
33561 cfg.cn.push(label);
33564 Roo.each(['day', 'month', 'year'], function(t){
33567 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33574 inputEl: function ()
33576 return this.el.select('.roo-date-split-field-group-value', true).first();
33579 onRender : function(ct, position)
33583 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33585 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33587 this.dayField = new Roo.bootstrap.ComboBox({
33588 allowBlank : this.dayAllowBlank,
33589 alwaysQuery : true,
33590 displayField : 'value',
33593 forceSelection : true,
33595 placeholder : this.dayPlaceholder,
33596 selectOnFocus : true,
33597 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33598 triggerAction : 'all',
33600 valueField : 'value',
33601 store : new Roo.data.SimpleStore({
33602 data : (function() {
33604 _this.fireEvent('days', _this, days);
33607 fields : [ 'value' ]
33610 select : function (_self, record, index)
33612 _this.setValue(_this.getValue());
33617 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33619 this.monthField = new Roo.bootstrap.MonthField({
33620 after : '<i class=\"fa fa-calendar\"></i>',
33621 allowBlank : this.monthAllowBlank,
33622 placeholder : this.monthPlaceholder,
33625 render : function (_self)
33627 this.el.select('span.input-group-addon', true).first().on('click', function(e){
33628 e.preventDefault();
33632 select : function (_self, oldvalue, newvalue)
33634 _this.setValue(_this.getValue());
33639 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33641 this.yearField = new Roo.bootstrap.ComboBox({
33642 allowBlank : this.yearAllowBlank,
33643 alwaysQuery : true,
33644 displayField : 'value',
33647 forceSelection : true,
33649 placeholder : this.yearPlaceholder,
33650 selectOnFocus : true,
33651 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33652 triggerAction : 'all',
33654 valueField : 'value',
33655 store : new Roo.data.SimpleStore({
33656 data : (function() {
33658 _this.fireEvent('years', _this, years);
33661 fields : [ 'value' ]
33664 select : function (_self, record, index)
33666 _this.setValue(_this.getValue());
33671 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33674 setValue : function(v, format)
33676 this.inputEl.dom.value = v;
33678 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33680 var d = Date.parseDate(v, f);
33687 this.setDay(d.format(this.dayFormat));
33688 this.setMonth(d.format(this.monthFormat));
33689 this.setYear(d.format(this.yearFormat));
33696 setDay : function(v)
33698 this.dayField.setValue(v);
33699 this.inputEl.dom.value = this.getValue();
33704 setMonth : function(v)
33706 this.monthField.setValue(v, true);
33707 this.inputEl.dom.value = this.getValue();
33712 setYear : function(v)
33714 this.yearField.setValue(v);
33715 this.inputEl.dom.value = this.getValue();
33720 getDay : function()
33722 return this.dayField.getValue();
33725 getMonth : function()
33727 return this.monthField.getValue();
33730 getYear : function()
33732 return this.yearField.getValue();
33735 getValue : function()
33737 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33739 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33749 this.inputEl.dom.value = '';
33754 validate : function()
33756 var d = this.dayField.validate();
33757 var m = this.monthField.validate();
33758 var y = this.yearField.validate();
33763 (!this.dayAllowBlank && !d) ||
33764 (!this.monthAllowBlank && !m) ||
33765 (!this.yearAllowBlank && !y)
33770 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33779 this.markInvalid();
33784 markValid : function()
33787 var label = this.el.select('label', true).first();
33788 var icon = this.el.select('i.fa-star', true).first();
33794 this.fireEvent('valid', this);
33798 * Mark this field as invalid
33799 * @param {String} msg The validation message
33801 markInvalid : function(msg)
33804 var label = this.el.select('label', true).first();
33805 var icon = this.el.select('i.fa-star', true).first();
33807 if(label && !icon){
33808 this.el.select('.roo-date-split-field-label', true).createChild({
33810 cls : 'text-danger fa fa-lg fa-star',
33811 tooltip : 'This field is required',
33812 style : 'margin-right:5px;'
33816 this.fireEvent('invalid', this, msg);
33819 clearInvalid : function()
33821 var label = this.el.select('label', true).first();
33822 var icon = this.el.select('i.fa-star', true).first();
33828 this.fireEvent('valid', this);
33831 getName: function()
33841 * http://masonry.desandro.com
33843 * The idea is to render all the bricks based on vertical width...
33845 * The original code extends 'outlayer' - we might need to use that....
33851 * @class Roo.bootstrap.LayoutMasonry
33852 * @extends Roo.bootstrap.Component
33853 * Bootstrap Layout Masonry class
33856 * Create a new Element
33857 * @param {Object} config The config object
33860 Roo.bootstrap.LayoutMasonry = function(config){
33862 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33866 Roo.bootstrap.LayoutMasonry.register(this);
33872 * Fire after layout the items
33873 * @param {Roo.bootstrap.LayoutMasonry} this
33874 * @param {Roo.EventObject} e
33881 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
33884 * @cfg {Boolean} isLayoutInstant = no animation?
33886 isLayoutInstant : false, // needed?
33889 * @cfg {Number} boxWidth width of the columns
33894 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
33899 * @cfg {Number} padWidth padding below box..
33904 * @cfg {Number} gutter gutter width..
33909 * @cfg {Number} maxCols maximum number of columns
33915 * @cfg {Boolean} isAutoInitial defalut true
33917 isAutoInitial : true,
33922 * @cfg {Boolean} isHorizontal defalut false
33924 isHorizontal : false,
33926 currentSize : null,
33932 bricks: null, //CompositeElement
33936 _isLayoutInited : false,
33938 // isAlternative : false, // only use for vertical layout...
33941 * @cfg {Number} alternativePadWidth padding below box..
33943 alternativePadWidth : 50,
33945 selectedBrick : [],
33947 getAutoCreate : function(){
33949 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33953 cls: 'blog-masonary-wrapper ' + this.cls,
33955 cls : 'mas-boxes masonary'
33962 getChildContainer: function( )
33964 if (this.boxesEl) {
33965 return this.boxesEl;
33968 this.boxesEl = this.el.select('.mas-boxes').first();
33970 return this.boxesEl;
33974 initEvents : function()
33978 if(this.isAutoInitial){
33979 Roo.log('hook children rendered');
33980 this.on('childrenrendered', function() {
33981 Roo.log('children rendered');
33987 initial : function()
33989 this.selectedBrick = [];
33991 this.currentSize = this.el.getBox(true);
33993 Roo.EventManager.onWindowResize(this.resize, this);
33995 if(!this.isAutoInitial){
34003 //this.layout.defer(500,this);
34007 resize : function()
34009 var cs = this.el.getBox(true);
34012 this.currentSize.width == cs.width &&
34013 this.currentSize.x == cs.x &&
34014 this.currentSize.height == cs.height &&
34015 this.currentSize.y == cs.y
34017 Roo.log("no change in with or X or Y");
34021 this.currentSize = cs;
34027 layout : function()
34029 this._resetLayout();
34031 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34033 this.layoutItems( isInstant );
34035 this._isLayoutInited = true;
34037 this.fireEvent('layout', this);
34041 _resetLayout : function()
34043 if(this.isHorizontal){
34044 this.horizontalMeasureColumns();
34048 this.verticalMeasureColumns();
34052 verticalMeasureColumns : function()
34054 this.getContainerWidth();
34056 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34057 // this.colWidth = Math.floor(this.containerWidth * 0.8);
34061 var boxWidth = this.boxWidth + this.padWidth;
34063 if(this.containerWidth < this.boxWidth){
34064 boxWidth = this.containerWidth
34067 var containerWidth = this.containerWidth;
34069 var cols = Math.floor(containerWidth / boxWidth);
34071 this.cols = Math.max( cols, 1 );
34073 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34075 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34077 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34079 this.colWidth = boxWidth + avail - this.padWidth;
34081 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34082 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
34085 horizontalMeasureColumns : function()
34087 this.getContainerWidth();
34089 var boxWidth = this.boxWidth;
34091 if(this.containerWidth < boxWidth){
34092 boxWidth = this.containerWidth;
34095 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34097 this.el.setHeight(boxWidth);
34101 getContainerWidth : function()
34103 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34106 layoutItems : function( isInstant )
34108 Roo.log(this.bricks);
34110 var items = Roo.apply([], this.bricks);
34112 if(this.isHorizontal){
34113 this._horizontalLayoutItems( items , isInstant );
34117 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34118 // this._verticalAlternativeLayoutItems( items , isInstant );
34122 this._verticalLayoutItems( items , isInstant );
34126 _verticalLayoutItems : function ( items , isInstant)
34128 if ( !items || !items.length ) {
34133 ['xs', 'xs', 'xs', 'tall'],
34134 ['xs', 'xs', 'tall'],
34135 ['xs', 'xs', 'sm'],
34136 ['xs', 'xs', 'xs'],
34142 ['sm', 'xs', 'xs'],
34146 ['tall', 'xs', 'xs', 'xs'],
34147 ['tall', 'xs', 'xs'],
34159 Roo.each(items, function(item, k){
34161 switch (item.size) {
34162 // these layouts take up a full box,
34173 boxes.push([item]);
34196 var filterPattern = function(box, length)
34204 var pattern = box.slice(0, length);
34208 Roo.each(pattern, function(i){
34209 format.push(i.size);
34212 Roo.each(standard, function(s){
34214 if(String(s) != String(format)){
34223 if(!match && length == 1){
34228 filterPattern(box, length - 1);
34232 queue.push(pattern);
34234 box = box.slice(length, box.length);
34236 filterPattern(box, 4);
34242 Roo.each(boxes, function(box, k){
34248 if(box.length == 1){
34253 filterPattern(box, 4);
34257 this._processVerticalLayoutQueue( queue, isInstant );
34261 // _verticalAlternativeLayoutItems : function( items , isInstant )
34263 // if ( !items || !items.length ) {
34267 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
34271 _horizontalLayoutItems : function ( items , isInstant)
34273 if ( !items || !items.length || items.length < 3) {
34279 var eItems = items.slice(0, 3);
34281 items = items.slice(3, items.length);
34284 ['xs', 'xs', 'xs', 'wide'],
34285 ['xs', 'xs', 'wide'],
34286 ['xs', 'xs', 'sm'],
34287 ['xs', 'xs', 'xs'],
34293 ['sm', 'xs', 'xs'],
34297 ['wide', 'xs', 'xs', 'xs'],
34298 ['wide', 'xs', 'xs'],
34311 Roo.each(items, function(item, k){
34313 switch (item.size) {
34324 boxes.push([item]);
34348 var filterPattern = function(box, length)
34356 var pattern = box.slice(0, length);
34360 Roo.each(pattern, function(i){
34361 format.push(i.size);
34364 Roo.each(standard, function(s){
34366 if(String(s) != String(format)){
34375 if(!match && length == 1){
34380 filterPattern(box, length - 1);
34384 queue.push(pattern);
34386 box = box.slice(length, box.length);
34388 filterPattern(box, 4);
34394 Roo.each(boxes, function(box, k){
34400 if(box.length == 1){
34405 filterPattern(box, 4);
34412 var pos = this.el.getBox(true);
34416 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34418 var hit_end = false;
34420 Roo.each(queue, function(box){
34424 Roo.each(box, function(b){
34426 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34436 Roo.each(box, function(b){
34438 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34441 mx = Math.max(mx, b.x);
34445 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34449 Roo.each(box, function(b){
34451 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34465 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34468 /** Sets position of item in DOM
34469 * @param {Element} item
34470 * @param {Number} x - horizontal position
34471 * @param {Number} y - vertical position
34472 * @param {Boolean} isInstant - disables transitions
34474 _processVerticalLayoutQueue : function( queue, isInstant )
34476 var pos = this.el.getBox(true);
34481 for (var i = 0; i < this.cols; i++){
34485 Roo.each(queue, function(box, k){
34487 var col = k % this.cols;
34489 Roo.each(box, function(b,kk){
34491 b.el.position('absolute');
34493 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34494 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34496 if(b.size == 'md-left' || b.size == 'md-right'){
34497 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34498 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34501 b.el.setWidth(width);
34502 b.el.setHeight(height);
34504 b.el.select('iframe',true).setSize(width,height);
34508 for (var i = 0; i < this.cols; i++){
34510 if(maxY[i] < maxY[col]){
34515 col = Math.min(col, i);
34519 x = pos.x + col * (this.colWidth + this.padWidth);
34523 var positions = [];
34525 switch (box.length){
34527 positions = this.getVerticalOneBoxColPositions(x, y, box);
34530 positions = this.getVerticalTwoBoxColPositions(x, y, box);
34533 positions = this.getVerticalThreeBoxColPositions(x, y, box);
34536 positions = this.getVerticalFourBoxColPositions(x, y, box);
34542 Roo.each(box, function(b,kk){
34544 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34546 var sz = b.el.getSize();
34548 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34556 for (var i = 0; i < this.cols; i++){
34557 mY = Math.max(mY, maxY[i]);
34560 this.el.setHeight(mY - pos.y);
34564 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34566 // var pos = this.el.getBox(true);
34569 // var maxX = pos.right;
34571 // var maxHeight = 0;
34573 // Roo.each(items, function(item, k){
34577 // item.el.position('absolute');
34579 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34581 // item.el.setWidth(width);
34583 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34585 // item.el.setHeight(height);
34588 // item.el.setXY([x, y], isInstant ? false : true);
34590 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34593 // y = y + height + this.alternativePadWidth;
34595 // maxHeight = maxHeight + height + this.alternativePadWidth;
34599 // this.el.setHeight(maxHeight);
34603 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34605 var pos = this.el.getBox(true);
34610 var maxX = pos.right;
34612 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34614 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34616 Roo.each(queue, function(box, k){
34618 Roo.each(box, function(b, kk){
34620 b.el.position('absolute');
34622 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34623 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34625 if(b.size == 'md-left' || b.size == 'md-right'){
34626 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34627 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34630 b.el.setWidth(width);
34631 b.el.setHeight(height);
34639 var positions = [];
34641 switch (box.length){
34643 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34646 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34649 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34652 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34658 Roo.each(box, function(b,kk){
34660 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34662 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34670 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34672 Roo.each(eItems, function(b,k){
34674 b.size = (k == 0) ? 'sm' : 'xs';
34675 b.x = (k == 0) ? 2 : 1;
34676 b.y = (k == 0) ? 2 : 1;
34678 b.el.position('absolute');
34680 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34682 b.el.setWidth(width);
34684 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34686 b.el.setHeight(height);
34690 var positions = [];
34693 x : maxX - this.unitWidth * 2 - this.gutter,
34698 x : maxX - this.unitWidth,
34699 y : minY + (this.unitWidth + this.gutter) * 2
34703 x : maxX - this.unitWidth * 3 - this.gutter * 2,
34707 Roo.each(eItems, function(b,k){
34709 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34715 getVerticalOneBoxColPositions : function(x, y, box)
34719 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34721 if(box[0].size == 'md-left'){
34725 if(box[0].size == 'md-right'){
34730 x : x + (this.unitWidth + this.gutter) * rand,
34737 getVerticalTwoBoxColPositions : function(x, y, box)
34741 if(box[0].size == 'xs'){
34745 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34749 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34763 x : x + (this.unitWidth + this.gutter) * 2,
34764 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34771 getVerticalThreeBoxColPositions : function(x, y, box)
34775 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34783 x : x + (this.unitWidth + this.gutter) * 1,
34788 x : x + (this.unitWidth + this.gutter) * 2,
34796 if(box[0].size == 'xs' && box[1].size == 'xs'){
34805 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34809 x : x + (this.unitWidth + this.gutter) * 1,
34823 x : x + (this.unitWidth + this.gutter) * 2,
34828 x : x + (this.unitWidth + this.gutter) * 2,
34829 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34836 getVerticalFourBoxColPositions : function(x, y, box)
34840 if(box[0].size == 'xs'){
34849 y : y + (this.unitHeight + this.gutter) * 1
34854 y : y + (this.unitHeight + this.gutter) * 2
34858 x : x + (this.unitWidth + this.gutter) * 1,
34872 x : x + (this.unitWidth + this.gutter) * 2,
34877 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34878 y : y + (this.unitHeight + this.gutter) * 1
34882 x : x + (this.unitWidth + this.gutter) * 2,
34883 y : y + (this.unitWidth + this.gutter) * 2
34890 getHorizontalOneBoxColPositions : function(maxX, minY, box)
34894 if(box[0].size == 'md-left'){
34896 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34903 if(box[0].size == 'md-right'){
34905 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34906 y : minY + (this.unitWidth + this.gutter) * 1
34912 var rand = Math.floor(Math.random() * (4 - box[0].y));
34915 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34916 y : minY + (this.unitWidth + this.gutter) * rand
34923 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34927 if(box[0].size == 'xs'){
34930 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34935 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34936 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34944 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34949 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34950 y : minY + (this.unitWidth + this.gutter) * 2
34957 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34961 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34964 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34969 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34970 y : minY + (this.unitWidth + this.gutter) * 1
34974 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34975 y : minY + (this.unitWidth + this.gutter) * 2
34982 if(box[0].size == 'xs' && box[1].size == 'xs'){
34985 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34990 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34995 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34996 y : minY + (this.unitWidth + this.gutter) * 1
35004 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35009 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35010 y : minY + (this.unitWidth + this.gutter) * 2
35014 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35015 y : minY + (this.unitWidth + this.gutter) * 2
35022 getHorizontalFourBoxColPositions : function(maxX, minY, box)
35026 if(box[0].size == 'xs'){
35029 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35034 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35039 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),
35044 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35045 y : minY + (this.unitWidth + this.gutter) * 1
35053 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35058 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35059 y : minY + (this.unitWidth + this.gutter) * 2
35063 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35064 y : minY + (this.unitWidth + this.gutter) * 2
35068 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),
35069 y : minY + (this.unitWidth + this.gutter) * 2
35077 * remove a Masonry Brick
35078 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35080 removeBrick : function(brick_id)
35086 for (var i = 0; i<this.bricks.length; i++) {
35087 if (this.bricks[i].id == brick_id) {
35088 this.bricks.splice(i,1);
35089 this.el.dom.removeChild(Roo.get(brick_id).dom);
35096 * adds a Masonry Brick
35097 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35099 addBrick : function(cfg)
35101 var cn = new Roo.bootstrap.MasonryBrick(cfg);
35102 //this.register(cn);
35103 cn.parentId = this.id;
35104 cn.render(this.el);
35109 * register a Masonry Brick
35110 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35113 register : function(brick)
35115 this.bricks.push(brick);
35116 brick.masonryId = this.id;
35120 * clear all the Masonry Brick
35122 clearAll : function()
35125 //this.getChildContainer().dom.innerHTML = "";
35126 this.el.dom.innerHTML = '';
35129 getSelected : function()
35131 if (!this.selectedBrick) {
35135 return this.selectedBrick;
35139 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35143 * register a Masonry Layout
35144 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35147 register : function(layout)
35149 this.groups[layout.id] = layout;
35152 * fetch a Masonry Layout based on the masonry layout ID
35153 * @param {string} the masonry layout to add
35154 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35157 get: function(layout_id) {
35158 if (typeof(this.groups[layout_id]) == 'undefined') {
35161 return this.groups[layout_id] ;
35173 * http://masonry.desandro.com
35175 * The idea is to render all the bricks based on vertical width...
35177 * The original code extends 'outlayer' - we might need to use that....
35183 * @class Roo.bootstrap.LayoutMasonryAuto
35184 * @extends Roo.bootstrap.Component
35185 * Bootstrap Layout Masonry class
35188 * Create a new Element
35189 * @param {Object} config The config object
35192 Roo.bootstrap.LayoutMasonryAuto = function(config){
35193 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35196 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
35199 * @cfg {Boolean} isFitWidth - resize the width..
35201 isFitWidth : false, // options..
35203 * @cfg {Boolean} isOriginLeft = left align?
35205 isOriginLeft : true,
35207 * @cfg {Boolean} isOriginTop = top align?
35209 isOriginTop : false,
35211 * @cfg {Boolean} isLayoutInstant = no animation?
35213 isLayoutInstant : false, // needed?
35215 * @cfg {Boolean} isResizingContainer = not sure if this is used..
35217 isResizingContainer : true,
35219 * @cfg {Number} columnWidth width of the columns
35225 * @cfg {Number} maxCols maximum number of columns
35230 * @cfg {Number} padHeight padding below box..
35236 * @cfg {Boolean} isAutoInitial defalut true
35239 isAutoInitial : true,
35245 initialColumnWidth : 0,
35246 currentSize : null,
35248 colYs : null, // array.
35255 bricks: null, //CompositeElement
35256 cols : 0, // array?
35257 // element : null, // wrapped now this.el
35258 _isLayoutInited : null,
35261 getAutoCreate : function(){
35265 cls: 'blog-masonary-wrapper ' + this.cls,
35267 cls : 'mas-boxes masonary'
35274 getChildContainer: function( )
35276 if (this.boxesEl) {
35277 return this.boxesEl;
35280 this.boxesEl = this.el.select('.mas-boxes').first();
35282 return this.boxesEl;
35286 initEvents : function()
35290 if(this.isAutoInitial){
35291 Roo.log('hook children rendered');
35292 this.on('childrenrendered', function() {
35293 Roo.log('children rendered');
35300 initial : function()
35302 this.reloadItems();
35304 this.currentSize = this.el.getBox(true);
35306 /// was window resize... - let's see if this works..
35307 Roo.EventManager.onWindowResize(this.resize, this);
35309 if(!this.isAutoInitial){
35314 this.layout.defer(500,this);
35317 reloadItems: function()
35319 this.bricks = this.el.select('.masonry-brick', true);
35321 this.bricks.each(function(b) {
35322 //Roo.log(b.getSize());
35323 if (!b.attr('originalwidth')) {
35324 b.attr('originalwidth', b.getSize().width);
35329 Roo.log(this.bricks.elements.length);
35332 resize : function()
35335 var cs = this.el.getBox(true);
35337 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35338 Roo.log("no change in with or X");
35341 this.currentSize = cs;
35345 layout : function()
35348 this._resetLayout();
35349 //this._manageStamps();
35351 // don't animate first layout
35352 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35353 this.layoutItems( isInstant );
35355 // flag for initalized
35356 this._isLayoutInited = true;
35359 layoutItems : function( isInstant )
35361 //var items = this._getItemsForLayout( this.items );
35362 // original code supports filtering layout items.. we just ignore it..
35364 this._layoutItems( this.bricks , isInstant );
35366 this._postLayout();
35368 _layoutItems : function ( items , isInstant)
35370 //this.fireEvent( 'layout', this, items );
35373 if ( !items || !items.elements.length ) {
35374 // no items, emit event with empty array
35379 items.each(function(item) {
35380 Roo.log("layout item");
35382 // get x/y object from method
35383 var position = this._getItemLayoutPosition( item );
35385 position.item = item;
35386 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35387 queue.push( position );
35390 this._processLayoutQueue( queue );
35392 /** Sets position of item in DOM
35393 * @param {Element} item
35394 * @param {Number} x - horizontal position
35395 * @param {Number} y - vertical position
35396 * @param {Boolean} isInstant - disables transitions
35398 _processLayoutQueue : function( queue )
35400 for ( var i=0, len = queue.length; i < len; i++ ) {
35401 var obj = queue[i];
35402 obj.item.position('absolute');
35403 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35409 * Any logic you want to do after each layout,
35410 * i.e. size the container
35412 _postLayout : function()
35414 this.resizeContainer();
35417 resizeContainer : function()
35419 if ( !this.isResizingContainer ) {
35422 var size = this._getContainerSize();
35424 this.el.setSize(size.width,size.height);
35425 this.boxesEl.setSize(size.width,size.height);
35431 _resetLayout : function()
35433 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35434 this.colWidth = this.el.getWidth();
35435 //this.gutter = this.el.getWidth();
35437 this.measureColumns();
35443 this.colYs.push( 0 );
35449 measureColumns : function()
35451 this.getContainerWidth();
35452 // if columnWidth is 0, default to outerWidth of first item
35453 if ( !this.columnWidth ) {
35454 var firstItem = this.bricks.first();
35455 Roo.log(firstItem);
35456 this.columnWidth = this.containerWidth;
35457 if (firstItem && firstItem.attr('originalwidth') ) {
35458 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35460 // columnWidth fall back to item of first element
35461 Roo.log("set column width?");
35462 this.initialColumnWidth = this.columnWidth ;
35464 // if first elem has no width, default to size of container
35469 if (this.initialColumnWidth) {
35470 this.columnWidth = this.initialColumnWidth;
35475 // column width is fixed at the top - however if container width get's smaller we should
35478 // this bit calcs how man columns..
35480 var columnWidth = this.columnWidth += this.gutter;
35482 // calculate columns
35483 var containerWidth = this.containerWidth + this.gutter;
35485 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35486 // fix rounding errors, typically with gutters
35487 var excess = columnWidth - containerWidth % columnWidth;
35490 // if overshoot is less than a pixel, round up, otherwise floor it
35491 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35492 cols = Math[ mathMethod ]( cols );
35493 this.cols = Math.max( cols, 1 );
35494 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35496 // padding positioning..
35497 var totalColWidth = this.cols * this.columnWidth;
35498 var padavail = this.containerWidth - totalColWidth;
35499 // so for 2 columns - we need 3 'pads'
35501 var padNeeded = (1+this.cols) * this.padWidth;
35503 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35505 this.columnWidth += padExtra
35506 //this.padWidth = Math.floor(padavail / ( this.cols));
35508 // adjust colum width so that padding is fixed??
35510 // we have 3 columns ... total = width * 3
35511 // we have X left over... that should be used by
35513 //if (this.expandC) {
35521 getContainerWidth : function()
35523 /* // container is parent if fit width
35524 var container = this.isFitWidth ? this.element.parentNode : this.element;
35525 // check that this.size and size are there
35526 // IE8 triggers resize on body size change, so they might not be
35528 var size = getSize( container ); //FIXME
35529 this.containerWidth = size && size.innerWidth; //FIXME
35532 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
35536 _getItemLayoutPosition : function( item ) // what is item?
35538 // we resize the item to our columnWidth..
35540 item.setWidth(this.columnWidth);
35541 item.autoBoxAdjust = false;
35543 var sz = item.getSize();
35545 // how many columns does this brick span
35546 var remainder = this.containerWidth % this.columnWidth;
35548 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35549 // round if off by 1 pixel, otherwise use ceil
35550 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
35551 colSpan = Math.min( colSpan, this.cols );
35553 // normally this should be '1' as we dont' currently allow multi width columns..
35555 var colGroup = this._getColGroup( colSpan );
35556 // get the minimum Y value from the columns
35557 var minimumY = Math.min.apply( Math, colGroup );
35558 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35560 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35562 // position the brick
35564 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35565 y: this.currentSize.y + minimumY + this.padHeight
35569 // apply setHeight to necessary columns
35570 var setHeight = minimumY + sz.height + this.padHeight;
35571 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35573 var setSpan = this.cols + 1 - colGroup.length;
35574 for ( var i = 0; i < setSpan; i++ ) {
35575 this.colYs[ shortColIndex + i ] = setHeight ;
35582 * @param {Number} colSpan - number of columns the element spans
35583 * @returns {Array} colGroup
35585 _getColGroup : function( colSpan )
35587 if ( colSpan < 2 ) {
35588 // if brick spans only one column, use all the column Ys
35593 // how many different places could this brick fit horizontally
35594 var groupCount = this.cols + 1 - colSpan;
35595 // for each group potential horizontal position
35596 for ( var i = 0; i < groupCount; i++ ) {
35597 // make an array of colY values for that one group
35598 var groupColYs = this.colYs.slice( i, i + colSpan );
35599 // and get the max value of the array
35600 colGroup[i] = Math.max.apply( Math, groupColYs );
35605 _manageStamp : function( stamp )
35607 var stampSize = stamp.getSize();
35608 var offset = stamp.getBox();
35609 // get the columns that this stamp affects
35610 var firstX = this.isOriginLeft ? offset.x : offset.right;
35611 var lastX = firstX + stampSize.width;
35612 var firstCol = Math.floor( firstX / this.columnWidth );
35613 firstCol = Math.max( 0, firstCol );
35615 var lastCol = Math.floor( lastX / this.columnWidth );
35616 // lastCol should not go over if multiple of columnWidth #425
35617 lastCol -= lastX % this.columnWidth ? 0 : 1;
35618 lastCol = Math.min( this.cols - 1, lastCol );
35620 // set colYs to bottom of the stamp
35621 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35624 for ( var i = firstCol; i <= lastCol; i++ ) {
35625 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35630 _getContainerSize : function()
35632 this.maxY = Math.max.apply( Math, this.colYs );
35637 if ( this.isFitWidth ) {
35638 size.width = this._getContainerFitWidth();
35644 _getContainerFitWidth : function()
35646 var unusedCols = 0;
35647 // count unused columns
35650 if ( this.colYs[i] !== 0 ) {
35655 // fit container to columns that have been used
35656 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35659 needsResizeLayout : function()
35661 var previousWidth = this.containerWidth;
35662 this.getContainerWidth();
35663 return previousWidth !== this.containerWidth;
35678 * @class Roo.bootstrap.MasonryBrick
35679 * @extends Roo.bootstrap.Component
35680 * Bootstrap MasonryBrick class
35683 * Create a new MasonryBrick
35684 * @param {Object} config The config object
35687 Roo.bootstrap.MasonryBrick = function(config){
35689 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35691 Roo.bootstrap.MasonryBrick.register(this);
35697 * When a MasonryBrick is clcik
35698 * @param {Roo.bootstrap.MasonryBrick} this
35699 * @param {Roo.EventObject} e
35705 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
35708 * @cfg {String} title
35712 * @cfg {String} html
35716 * @cfg {String} bgimage
35720 * @cfg {String} videourl
35724 * @cfg {String} cls
35728 * @cfg {String} href
35732 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35737 * @cfg {String} placetitle (center|bottom)
35742 * @cfg {Boolean} isFitContainer defalut true
35744 isFitContainer : true,
35747 * @cfg {Boolean} preventDefault defalut false
35749 preventDefault : false,
35752 * @cfg {Boolean} inverse defalut false
35754 maskInverse : false,
35756 getAutoCreate : function()
35758 if(!this.isFitContainer){
35759 return this.getSplitAutoCreate();
35762 var cls = 'masonry-brick masonry-brick-full';
35764 if(this.href.length){
35765 cls += ' masonry-brick-link';
35768 if(this.bgimage.length){
35769 cls += ' masonry-brick-image';
35772 if(this.maskInverse){
35773 cls += ' mask-inverse';
35776 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35777 cls += ' enable-mask';
35781 cls += ' masonry-' + this.size + '-brick';
35784 if(this.placetitle.length){
35786 switch (this.placetitle) {
35788 cls += ' masonry-center-title';
35791 cls += ' masonry-bottom-title';
35798 if(!this.html.length && !this.bgimage.length){
35799 cls += ' masonry-center-title';
35802 if(!this.html.length && this.bgimage.length){
35803 cls += ' masonry-bottom-title';
35808 cls += ' ' + this.cls;
35812 tag: (this.href.length) ? 'a' : 'div',
35817 cls: 'masonry-brick-mask'
35821 cls: 'masonry-brick-paragraph',
35827 if(this.href.length){
35828 cfg.href = this.href;
35831 var cn = cfg.cn[1].cn;
35833 if(this.title.length){
35836 cls: 'masonry-brick-title',
35841 if(this.html.length){
35844 cls: 'masonry-brick-text',
35849 if (!this.title.length && !this.html.length) {
35850 cfg.cn[1].cls += ' hide';
35853 if(this.bgimage.length){
35856 cls: 'masonry-brick-image-view',
35861 if(this.videourl.length){
35862 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35863 // youtube support only?
35866 cls: 'masonry-brick-image-view',
35869 allowfullscreen : true
35877 getSplitAutoCreate : function()
35879 var cls = 'masonry-brick masonry-brick-split';
35881 if(this.href.length){
35882 cls += ' masonry-brick-link';
35885 if(this.bgimage.length){
35886 cls += ' masonry-brick-image';
35890 cls += ' masonry-' + this.size + '-brick';
35893 switch (this.placetitle) {
35895 cls += ' masonry-center-title';
35898 cls += ' masonry-bottom-title';
35901 if(!this.bgimage.length){
35902 cls += ' masonry-center-title';
35905 if(this.bgimage.length){
35906 cls += ' masonry-bottom-title';
35912 cls += ' ' + this.cls;
35916 tag: (this.href.length) ? 'a' : 'div',
35921 cls: 'masonry-brick-split-head',
35925 cls: 'masonry-brick-paragraph',
35932 cls: 'masonry-brick-split-body',
35938 if(this.href.length){
35939 cfg.href = this.href;
35942 if(this.title.length){
35943 cfg.cn[0].cn[0].cn.push({
35945 cls: 'masonry-brick-title',
35950 if(this.html.length){
35951 cfg.cn[1].cn.push({
35953 cls: 'masonry-brick-text',
35958 if(this.bgimage.length){
35959 cfg.cn[0].cn.push({
35961 cls: 'masonry-brick-image-view',
35966 if(this.videourl.length){
35967 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35968 // youtube support only?
35969 cfg.cn[0].cn.cn.push({
35971 cls: 'masonry-brick-image-view',
35974 allowfullscreen : true
35981 initEvents: function()
35983 switch (this.size) {
36016 this.el.on('touchstart', this.onTouchStart, this);
36017 this.el.on('touchmove', this.onTouchMove, this);
36018 this.el.on('touchend', this.onTouchEnd, this);
36019 this.el.on('contextmenu', this.onContextMenu, this);
36021 this.el.on('mouseenter' ,this.enter, this);
36022 this.el.on('mouseleave', this.leave, this);
36023 this.el.on('click', this.onClick, this);
36026 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36027 this.parent().bricks.push(this);
36032 onClick: function(e, el)
36034 var time = this.endTimer - this.startTimer;
36035 // Roo.log(e.preventDefault());
36038 e.preventDefault();
36043 if(!this.preventDefault){
36047 e.preventDefault();
36049 if (this.activeClass != '') {
36050 this.selectBrick();
36053 this.fireEvent('click', this, e);
36056 enter: function(e, el)
36058 e.preventDefault();
36060 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36064 if(this.bgimage.length && this.html.length){
36065 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36069 leave: function(e, el)
36071 e.preventDefault();
36073 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36077 if(this.bgimage.length && this.html.length){
36078 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36082 onTouchStart: function(e, el)
36084 // e.preventDefault();
36086 this.touchmoved = false;
36088 if(!this.isFitContainer){
36092 if(!this.bgimage.length || !this.html.length){
36096 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36098 this.timer = new Date().getTime();
36102 onTouchMove: function(e, el)
36104 this.touchmoved = true;
36107 onContextMenu : function(e,el)
36109 e.preventDefault();
36110 e.stopPropagation();
36114 onTouchEnd: function(e, el)
36116 // e.preventDefault();
36118 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36125 if(!this.bgimage.length || !this.html.length){
36127 if(this.href.length){
36128 window.location.href = this.href;
36134 if(!this.isFitContainer){
36138 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36140 window.location.href = this.href;
36143 //selection on single brick only
36144 selectBrick : function() {
36146 if (!this.parentId) {
36150 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36151 var index = m.selectedBrick.indexOf(this.id);
36154 m.selectedBrick.splice(index,1);
36155 this.el.removeClass(this.activeClass);
36159 for(var i = 0; i < m.selectedBrick.length; i++) {
36160 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36161 b.el.removeClass(b.activeClass);
36164 m.selectedBrick = [];
36166 m.selectedBrick.push(this.id);
36167 this.el.addClass(this.activeClass);
36171 isSelected : function(){
36172 return this.el.hasClass(this.activeClass);
36177 Roo.apply(Roo.bootstrap.MasonryBrick, {
36180 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36182 * register a Masonry Brick
36183 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36186 register : function(brick)
36188 //this.groups[brick.id] = brick;
36189 this.groups.add(brick.id, brick);
36192 * fetch a masonry brick based on the masonry brick ID
36193 * @param {string} the masonry brick to add
36194 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36197 get: function(brick_id)
36199 // if (typeof(this.groups[brick_id]) == 'undefined') {
36202 // return this.groups[brick_id] ;
36204 if(this.groups.key(brick_id)) {
36205 return this.groups.key(brick_id);
36223 * @class Roo.bootstrap.Brick
36224 * @extends Roo.bootstrap.Component
36225 * Bootstrap Brick class
36228 * Create a new Brick
36229 * @param {Object} config The config object
36232 Roo.bootstrap.Brick = function(config){
36233 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36239 * When a Brick is click
36240 * @param {Roo.bootstrap.Brick} this
36241 * @param {Roo.EventObject} e
36247 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
36250 * @cfg {String} title
36254 * @cfg {String} html
36258 * @cfg {String} bgimage
36262 * @cfg {String} cls
36266 * @cfg {String} href
36270 * @cfg {String} video
36274 * @cfg {Boolean} square
36278 getAutoCreate : function()
36280 var cls = 'roo-brick';
36282 if(this.href.length){
36283 cls += ' roo-brick-link';
36286 if(this.bgimage.length){
36287 cls += ' roo-brick-image';
36290 if(!this.html.length && !this.bgimage.length){
36291 cls += ' roo-brick-center-title';
36294 if(!this.html.length && this.bgimage.length){
36295 cls += ' roo-brick-bottom-title';
36299 cls += ' ' + this.cls;
36303 tag: (this.href.length) ? 'a' : 'div',
36308 cls: 'roo-brick-paragraph',
36314 if(this.href.length){
36315 cfg.href = this.href;
36318 var cn = cfg.cn[0].cn;
36320 if(this.title.length){
36323 cls: 'roo-brick-title',
36328 if(this.html.length){
36331 cls: 'roo-brick-text',
36338 if(this.bgimage.length){
36341 cls: 'roo-brick-image-view',
36349 initEvents: function()
36351 if(this.title.length || this.html.length){
36352 this.el.on('mouseenter' ,this.enter, this);
36353 this.el.on('mouseleave', this.leave, this);
36356 Roo.EventManager.onWindowResize(this.resize, this);
36358 if(this.bgimage.length){
36359 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36360 this.imageEl.on('load', this.onImageLoad, this);
36367 onImageLoad : function()
36372 resize : function()
36374 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36376 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36378 if(this.bgimage.length){
36379 var image = this.el.select('.roo-brick-image-view', true).first();
36381 image.setWidth(paragraph.getWidth());
36384 image.setHeight(paragraph.getWidth());
36387 this.el.setHeight(image.getHeight());
36388 paragraph.setHeight(image.getHeight());
36394 enter: function(e, el)
36396 e.preventDefault();
36398 if(this.bgimage.length){
36399 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36400 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36404 leave: function(e, el)
36406 e.preventDefault();
36408 if(this.bgimage.length){
36409 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36410 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36425 * @class Roo.bootstrap.NumberField
36426 * @extends Roo.bootstrap.Input
36427 * Bootstrap NumberField class
36433 * Create a new NumberField
36434 * @param {Object} config The config object
36437 Roo.bootstrap.NumberField = function(config){
36438 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36441 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36444 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36446 allowDecimals : true,
36448 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36450 decimalSeparator : ".",
36452 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36454 decimalPrecision : 2,
36456 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36458 allowNegative : true,
36461 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36465 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36467 minValue : Number.NEGATIVE_INFINITY,
36469 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36471 maxValue : Number.MAX_VALUE,
36473 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36475 minText : "The minimum value for this field is {0}",
36477 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36479 maxText : "The maximum value for this field is {0}",
36481 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36482 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36484 nanText : "{0} is not a valid number",
36486 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36488 thousandsDelimiter : false,
36490 * @cfg {String} valueAlign alignment of value
36492 valueAlign : "left",
36494 getAutoCreate : function()
36496 var hiddenInput = {
36500 cls: 'hidden-number-input'
36504 hiddenInput.name = this.name;
36509 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36511 this.name = hiddenInput.name;
36513 if(cfg.cn.length > 0) {
36514 cfg.cn.push(hiddenInput);
36521 initEvents : function()
36523 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36525 var allowed = "0123456789";
36527 if(this.allowDecimals){
36528 allowed += this.decimalSeparator;
36531 if(this.allowNegative){
36535 if(this.thousandsDelimiter) {
36539 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36541 var keyPress = function(e){
36543 var k = e.getKey();
36545 var c = e.getCharCode();
36548 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36549 allowed.indexOf(String.fromCharCode(c)) === -1
36555 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36559 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36564 this.el.on("keypress", keyPress, this);
36567 validateValue : function(value)
36570 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36574 var num = this.parseValue(value);
36577 this.markInvalid(String.format(this.nanText, value));
36581 if(num < this.minValue){
36582 this.markInvalid(String.format(this.minText, this.minValue));
36586 if(num > this.maxValue){
36587 this.markInvalid(String.format(this.maxText, this.maxValue));
36594 getValue : function()
36596 var v = this.hiddenEl().getValue();
36598 return this.fixPrecision(this.parseValue(v));
36601 parseValue : function(value)
36603 if(this.thousandsDelimiter) {
36605 r = new RegExp(",", "g");
36606 value = value.replace(r, "");
36609 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36610 return isNaN(value) ? '' : value;
36613 fixPrecision : function(value)
36615 if(this.thousandsDelimiter) {
36617 r = new RegExp(",", "g");
36618 value = value.replace(r, "");
36621 var nan = isNaN(value);
36623 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36624 return nan ? '' : value;
36626 return parseFloat(value).toFixed(this.decimalPrecision);
36629 setValue : function(v)
36631 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36637 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36639 this.inputEl().dom.value = (v == '') ? '' :
36640 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36642 if(!this.allowZero && v === '0') {
36643 this.hiddenEl().dom.value = '';
36644 this.inputEl().dom.value = '';
36651 decimalPrecisionFcn : function(v)
36653 return Math.floor(v);
36656 beforeBlur : function()
36658 var v = this.parseValue(this.getRawValue());
36660 if(v || v === 0 || v === ''){
36665 hiddenEl : function()
36667 return this.el.select('input.hidden-number-input',true).first();
36679 * @class Roo.bootstrap.DocumentSlider
36680 * @extends Roo.bootstrap.Component
36681 * Bootstrap DocumentSlider class
36684 * Create a new DocumentViewer
36685 * @param {Object} config The config object
36688 Roo.bootstrap.DocumentSlider = function(config){
36689 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36696 * Fire after initEvent
36697 * @param {Roo.bootstrap.DocumentSlider} this
36702 * Fire after update
36703 * @param {Roo.bootstrap.DocumentSlider} this
36709 * @param {Roo.bootstrap.DocumentSlider} this
36715 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
36721 getAutoCreate : function()
36725 cls : 'roo-document-slider',
36729 cls : 'roo-document-slider-header',
36733 cls : 'roo-document-slider-header-title'
36739 cls : 'roo-document-slider-body',
36743 cls : 'roo-document-slider-prev',
36747 cls : 'fa fa-chevron-left'
36753 cls : 'roo-document-slider-thumb',
36757 cls : 'roo-document-slider-image'
36763 cls : 'roo-document-slider-next',
36767 cls : 'fa fa-chevron-right'
36779 initEvents : function()
36781 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36782 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36784 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36785 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36787 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36788 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36790 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36791 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36793 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36794 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36796 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36797 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36799 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36800 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36802 this.thumbEl.on('click', this.onClick, this);
36804 this.prevIndicator.on('click', this.prev, this);
36806 this.nextIndicator.on('click', this.next, this);
36810 initial : function()
36812 if(this.files.length){
36813 this.indicator = 1;
36817 this.fireEvent('initial', this);
36820 update : function()
36822 this.imageEl.attr('src', this.files[this.indicator - 1]);
36824 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36826 this.prevIndicator.show();
36828 if(this.indicator == 1){
36829 this.prevIndicator.hide();
36832 this.nextIndicator.show();
36834 if(this.indicator == this.files.length){
36835 this.nextIndicator.hide();
36838 this.thumbEl.scrollTo('top');
36840 this.fireEvent('update', this);
36843 onClick : function(e)
36845 e.preventDefault();
36847 this.fireEvent('click', this);
36852 e.preventDefault();
36854 this.indicator = Math.max(1, this.indicator - 1);
36861 e.preventDefault();
36863 this.indicator = Math.min(this.files.length, this.indicator + 1);
36877 * @class Roo.bootstrap.RadioSet
36878 * @extends Roo.bootstrap.Input
36879 * Bootstrap RadioSet class
36880 * @cfg {String} indicatorpos (left|right) default left
36881 * @cfg {Boolean} inline (true|false) inline the element (default true)
36882 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36884 * Create a new RadioSet
36885 * @param {Object} config The config object
36888 Roo.bootstrap.RadioSet = function(config){
36890 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36894 Roo.bootstrap.RadioSet.register(this);
36899 * Fires when the element is checked or unchecked.
36900 * @param {Roo.bootstrap.RadioSet} this This radio
36901 * @param {Roo.bootstrap.Radio} item The checked item
36906 * Fires when the element is click.
36907 * @param {Roo.bootstrap.RadioSet} this This radio set
36908 * @param {Roo.bootstrap.Radio} item The checked item
36909 * @param {Roo.EventObject} e The event object
36916 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
36924 indicatorpos : 'left',
36926 getAutoCreate : function()
36930 cls : 'roo-radio-set-label',
36934 html : this.fieldLabel
36938 if (Roo.bootstrap.version == 3) {
36941 if(this.indicatorpos == 'left'){
36944 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36945 tooltip : 'This field is required'
36950 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36951 tooltip : 'This field is required'
36957 cls : 'roo-radio-set-items'
36960 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36962 if (align === 'left' && this.fieldLabel.length) {
36965 cls : "roo-radio-set-right",
36971 if(this.labelWidth > 12){
36972 label.style = "width: " + this.labelWidth + 'px';
36975 if(this.labelWidth < 13 && this.labelmd == 0){
36976 this.labelmd = this.labelWidth;
36979 if(this.labellg > 0){
36980 label.cls += ' col-lg-' + this.labellg;
36981 items.cls += ' col-lg-' + (12 - this.labellg);
36984 if(this.labelmd > 0){
36985 label.cls += ' col-md-' + this.labelmd;
36986 items.cls += ' col-md-' + (12 - this.labelmd);
36989 if(this.labelsm > 0){
36990 label.cls += ' col-sm-' + this.labelsm;
36991 items.cls += ' col-sm-' + (12 - this.labelsm);
36994 if(this.labelxs > 0){
36995 label.cls += ' col-xs-' + this.labelxs;
36996 items.cls += ' col-xs-' + (12 - this.labelxs);
37002 cls : 'roo-radio-set',
37006 cls : 'roo-radio-set-input',
37009 value : this.value ? this.value : ''
37016 if(this.weight.length){
37017 cfg.cls += ' roo-radio-' + this.weight;
37021 cfg.cls += ' roo-radio-set-inline';
37025 ['xs','sm','md','lg'].map(function(size){
37026 if (settings[size]) {
37027 cfg.cls += ' col-' + size + '-' + settings[size];
37035 initEvents : function()
37037 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37038 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37040 if(!this.fieldLabel.length){
37041 this.labelEl.hide();
37044 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37045 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37047 this.indicator = this.indicatorEl();
37049 if(this.indicator){
37050 this.indicator.addClass('invisible');
37053 this.originalValue = this.getValue();
37057 inputEl: function ()
37059 return this.el.select('.roo-radio-set-input', true).first();
37062 getChildContainer : function()
37064 return this.itemsEl;
37067 register : function(item)
37069 this.radioes.push(item);
37073 validate : function()
37075 if(this.getVisibilityEl().hasClass('hidden')){
37081 Roo.each(this.radioes, function(i){
37090 if(this.allowBlank) {
37094 if(this.disabled || valid){
37099 this.markInvalid();
37104 markValid : function()
37106 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37107 this.indicatorEl().removeClass('visible');
37108 this.indicatorEl().addClass('invisible');
37112 if (Roo.bootstrap.version == 3) {
37113 this.el.removeClass([this.invalidClass, this.validClass]);
37114 this.el.addClass(this.validClass);
37116 this.el.removeClass(['is-invalid','is-valid']);
37117 this.el.addClass(['is-valid']);
37119 this.fireEvent('valid', this);
37122 markInvalid : function(msg)
37124 if(this.allowBlank || this.disabled){
37128 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37129 this.indicatorEl().removeClass('invisible');
37130 this.indicatorEl().addClass('visible');
37132 if (Roo.bootstrap.version == 3) {
37133 this.el.removeClass([this.invalidClass, this.validClass]);
37134 this.el.addClass(this.invalidClass);
37136 this.el.removeClass(['is-invalid','is-valid']);
37137 this.el.addClass(['is-invalid']);
37140 this.fireEvent('invalid', this, msg);
37144 setValue : function(v, suppressEvent)
37146 if(this.value === v){
37153 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37156 Roo.each(this.radioes, function(i){
37158 i.el.removeClass('checked');
37161 Roo.each(this.radioes, function(i){
37163 if(i.value === v || i.value.toString() === v.toString()){
37165 i.el.addClass('checked');
37167 if(suppressEvent !== true){
37168 this.fireEvent('check', this, i);
37179 clearInvalid : function(){
37181 if(!this.el || this.preventMark){
37185 this.el.removeClass([this.invalidClass]);
37187 this.fireEvent('valid', this);
37192 Roo.apply(Roo.bootstrap.RadioSet, {
37196 register : function(set)
37198 this.groups[set.name] = set;
37201 get: function(name)
37203 if (typeof(this.groups[name]) == 'undefined') {
37207 return this.groups[name] ;
37213 * Ext JS Library 1.1.1
37214 * Copyright(c) 2006-2007, Ext JS, LLC.
37216 * Originally Released Under LGPL - original licence link has changed is not relivant.
37219 * <script type="text/javascript">
37224 * @class Roo.bootstrap.SplitBar
37225 * @extends Roo.util.Observable
37226 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37230 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37231 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37232 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37233 split.minSize = 100;
37234 split.maxSize = 600;
37235 split.animate = true;
37236 split.on('moved', splitterMoved);
37239 * Create a new SplitBar
37240 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
37241 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
37242 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37243 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
37244 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37245 position of the SplitBar).
37247 Roo.bootstrap.SplitBar = function(cfg){
37252 // dragElement : elm
37253 // resizingElement: el,
37255 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37256 // placement : Roo.bootstrap.SplitBar.LEFT ,
37257 // existingProxy ???
37260 this.el = Roo.get(cfg.dragElement, true);
37261 this.el.dom.unselectable = "on";
37263 this.resizingEl = Roo.get(cfg.resizingElement, true);
37267 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37268 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37271 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37274 * The minimum size of the resizing element. (Defaults to 0)
37280 * The maximum size of the resizing element. (Defaults to 2000)
37283 this.maxSize = 2000;
37286 * Whether to animate the transition to the new size
37289 this.animate = false;
37292 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37295 this.useShim = false;
37300 if(!cfg.existingProxy){
37302 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37304 this.proxy = Roo.get(cfg.existingProxy).dom;
37307 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37310 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37313 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37316 this.dragSpecs = {};
37319 * @private The adapter to use to positon and resize elements
37321 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37322 this.adapter.init(this);
37324 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37326 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37327 this.el.addClass("roo-splitbar-h");
37330 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37331 this.el.addClass("roo-splitbar-v");
37337 * Fires when the splitter is moved (alias for {@link #event-moved})
37338 * @param {Roo.bootstrap.SplitBar} this
37339 * @param {Number} newSize the new width or height
37344 * Fires when the splitter is moved
37345 * @param {Roo.bootstrap.SplitBar} this
37346 * @param {Number} newSize the new width or height
37350 * @event beforeresize
37351 * Fires before the splitter is dragged
37352 * @param {Roo.bootstrap.SplitBar} this
37354 "beforeresize" : true,
37356 "beforeapply" : true
37359 Roo.util.Observable.call(this);
37362 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37363 onStartProxyDrag : function(x, y){
37364 this.fireEvent("beforeresize", this);
37366 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37368 o.enableDisplayMode("block");
37369 // all splitbars share the same overlay
37370 Roo.bootstrap.SplitBar.prototype.overlay = o;
37372 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37373 this.overlay.show();
37374 Roo.get(this.proxy).setDisplayed("block");
37375 var size = this.adapter.getElementSize(this);
37376 this.activeMinSize = this.getMinimumSize();;
37377 this.activeMaxSize = this.getMaximumSize();;
37378 var c1 = size - this.activeMinSize;
37379 var c2 = Math.max(this.activeMaxSize - size, 0);
37380 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37381 this.dd.resetConstraints();
37382 this.dd.setXConstraint(
37383 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37384 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37386 this.dd.setYConstraint(0, 0);
37388 this.dd.resetConstraints();
37389 this.dd.setXConstraint(0, 0);
37390 this.dd.setYConstraint(
37391 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37392 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37395 this.dragSpecs.startSize = size;
37396 this.dragSpecs.startPoint = [x, y];
37397 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37401 * @private Called after the drag operation by the DDProxy
37403 onEndProxyDrag : function(e){
37404 Roo.get(this.proxy).setDisplayed(false);
37405 var endPoint = Roo.lib.Event.getXY(e);
37407 this.overlay.hide();
37410 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37411 newSize = this.dragSpecs.startSize +
37412 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37413 endPoint[0] - this.dragSpecs.startPoint[0] :
37414 this.dragSpecs.startPoint[0] - endPoint[0]
37417 newSize = this.dragSpecs.startSize +
37418 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37419 endPoint[1] - this.dragSpecs.startPoint[1] :
37420 this.dragSpecs.startPoint[1] - endPoint[1]
37423 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37424 if(newSize != this.dragSpecs.startSize){
37425 if(this.fireEvent('beforeapply', this, newSize) !== false){
37426 this.adapter.setElementSize(this, newSize);
37427 this.fireEvent("moved", this, newSize);
37428 this.fireEvent("resize", this, newSize);
37434 * Get the adapter this SplitBar uses
37435 * @return The adapter object
37437 getAdapter : function(){
37438 return this.adapter;
37442 * Set the adapter this SplitBar uses
37443 * @param {Object} adapter A SplitBar adapter object
37445 setAdapter : function(adapter){
37446 this.adapter = adapter;
37447 this.adapter.init(this);
37451 * Gets the minimum size for the resizing element
37452 * @return {Number} The minimum size
37454 getMinimumSize : function(){
37455 return this.minSize;
37459 * Sets the minimum size for the resizing element
37460 * @param {Number} minSize The minimum size
37462 setMinimumSize : function(minSize){
37463 this.minSize = minSize;
37467 * Gets the maximum size for the resizing element
37468 * @return {Number} The maximum size
37470 getMaximumSize : function(){
37471 return this.maxSize;
37475 * Sets the maximum size for the resizing element
37476 * @param {Number} maxSize The maximum size
37478 setMaximumSize : function(maxSize){
37479 this.maxSize = maxSize;
37483 * Sets the initialize size for the resizing element
37484 * @param {Number} size The initial size
37486 setCurrentSize : function(size){
37487 var oldAnimate = this.animate;
37488 this.animate = false;
37489 this.adapter.setElementSize(this, size);
37490 this.animate = oldAnimate;
37494 * Destroy this splitbar.
37495 * @param {Boolean} removeEl True to remove the element
37497 destroy : function(removeEl){
37499 this.shim.remove();
37502 this.proxy.parentNode.removeChild(this.proxy);
37510 * @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.
37512 Roo.bootstrap.SplitBar.createProxy = function(dir){
37513 var proxy = new Roo.Element(document.createElement("div"));
37514 proxy.unselectable();
37515 var cls = 'roo-splitbar-proxy';
37516 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37517 document.body.appendChild(proxy.dom);
37522 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37523 * Default Adapter. It assumes the splitter and resizing element are not positioned
37524 * elements and only gets/sets the width of the element. Generally used for table based layouts.
37526 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37529 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37530 // do nothing for now
37531 init : function(s){
37535 * Called before drag operations to get the current size of the resizing element.
37536 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37538 getElementSize : function(s){
37539 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37540 return s.resizingEl.getWidth();
37542 return s.resizingEl.getHeight();
37547 * Called after drag operations to set the size of the resizing element.
37548 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37549 * @param {Number} newSize The new size to set
37550 * @param {Function} onComplete A function to be invoked when resizing is complete
37552 setElementSize : function(s, newSize, onComplete){
37553 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37555 s.resizingEl.setWidth(newSize);
37557 onComplete(s, newSize);
37560 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37565 s.resizingEl.setHeight(newSize);
37567 onComplete(s, newSize);
37570 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37577 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37578 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37579 * Adapter that moves the splitter element to align with the resized sizing element.
37580 * Used with an absolute positioned SplitBar.
37581 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37582 * document.body, make sure you assign an id to the body element.
37584 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37585 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37586 this.container = Roo.get(container);
37589 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37590 init : function(s){
37591 this.basic.init(s);
37594 getElementSize : function(s){
37595 return this.basic.getElementSize(s);
37598 setElementSize : function(s, newSize, onComplete){
37599 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37602 moveSplitter : function(s){
37603 var yes = Roo.bootstrap.SplitBar;
37604 switch(s.placement){
37606 s.el.setX(s.resizingEl.getRight());
37609 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37612 s.el.setY(s.resizingEl.getBottom());
37615 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37622 * Orientation constant - Create a vertical SplitBar
37626 Roo.bootstrap.SplitBar.VERTICAL = 1;
37629 * Orientation constant - Create a horizontal SplitBar
37633 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37636 * Placement constant - The resizing element is to the left of the splitter element
37640 Roo.bootstrap.SplitBar.LEFT = 1;
37643 * Placement constant - The resizing element is to the right of the splitter element
37647 Roo.bootstrap.SplitBar.RIGHT = 2;
37650 * Placement constant - The resizing element is positioned above the splitter element
37654 Roo.bootstrap.SplitBar.TOP = 3;
37657 * Placement constant - The resizing element is positioned under splitter element
37661 Roo.bootstrap.SplitBar.BOTTOM = 4;
37662 Roo.namespace("Roo.bootstrap.layout");/*
37664 * Ext JS Library 1.1.1
37665 * Copyright(c) 2006-2007, Ext JS, LLC.
37667 * Originally Released Under LGPL - original licence link has changed is not relivant.
37670 * <script type="text/javascript">
37674 * @class Roo.bootstrap.layout.Manager
37675 * @extends Roo.bootstrap.Component
37676 * Base class for layout managers.
37678 Roo.bootstrap.layout.Manager = function(config)
37680 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37686 /** false to disable window resize monitoring @type Boolean */
37687 this.monitorWindowResize = true;
37692 * Fires when a layout is performed.
37693 * @param {Roo.LayoutManager} this
37697 * @event regionresized
37698 * Fires when the user resizes a region.
37699 * @param {Roo.LayoutRegion} region The resized region
37700 * @param {Number} newSize The new size (width for east/west, height for north/south)
37702 "regionresized" : true,
37704 * @event regioncollapsed
37705 * Fires when a region is collapsed.
37706 * @param {Roo.LayoutRegion} region The collapsed region
37708 "regioncollapsed" : true,
37710 * @event regionexpanded
37711 * Fires when a region is expanded.
37712 * @param {Roo.LayoutRegion} region The expanded region
37714 "regionexpanded" : true
37716 this.updating = false;
37719 this.el = Roo.get(config.el);
37725 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37730 monitorWindowResize : true,
37736 onRender : function(ct, position)
37739 this.el = Roo.get(ct);
37742 //this.fireEvent('render',this);
37746 initEvents: function()
37750 // ie scrollbar fix
37751 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37752 document.body.scroll = "no";
37753 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37754 this.el.position('relative');
37756 this.id = this.el.id;
37757 this.el.addClass("roo-layout-container");
37758 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37759 if(this.el.dom != document.body ) {
37760 this.el.on('resize', this.layout,this);
37761 this.el.on('show', this.layout,this);
37767 * Returns true if this layout is currently being updated
37768 * @return {Boolean}
37770 isUpdating : function(){
37771 return this.updating;
37775 * Suspend the LayoutManager from doing auto-layouts while
37776 * making multiple add or remove calls
37778 beginUpdate : function(){
37779 this.updating = true;
37783 * Restore auto-layouts and optionally disable the manager from performing a layout
37784 * @param {Boolean} noLayout true to disable a layout update
37786 endUpdate : function(noLayout){
37787 this.updating = false;
37793 layout: function(){
37797 onRegionResized : function(region, newSize){
37798 this.fireEvent("regionresized", region, newSize);
37802 onRegionCollapsed : function(region){
37803 this.fireEvent("regioncollapsed", region);
37806 onRegionExpanded : function(region){
37807 this.fireEvent("regionexpanded", region);
37811 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37812 * performs box-model adjustments.
37813 * @return {Object} The size as an object {width: (the width), height: (the height)}
37815 getViewSize : function()
37818 if(this.el.dom != document.body){
37819 size = this.el.getSize();
37821 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37823 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37824 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37829 * Returns the Element this layout is bound to.
37830 * @return {Roo.Element}
37832 getEl : function(){
37837 * Returns the specified region.
37838 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37839 * @return {Roo.LayoutRegion}
37841 getRegion : function(target){
37842 return this.regions[target.toLowerCase()];
37845 onWindowResize : function(){
37846 if(this.monitorWindowResize){
37853 * Ext JS Library 1.1.1
37854 * Copyright(c) 2006-2007, Ext JS, LLC.
37856 * Originally Released Under LGPL - original licence link has changed is not relivant.
37859 * <script type="text/javascript">
37862 * @class Roo.bootstrap.layout.Border
37863 * @extends Roo.bootstrap.layout.Manager
37864 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37865 * please see: examples/bootstrap/nested.html<br><br>
37867 <b>The container the layout is rendered into can be either the body element or any other element.
37868 If it is not the body element, the container needs to either be an absolute positioned element,
37869 or you will need to add "position:relative" to the css of the container. You will also need to specify
37870 the container size if it is not the body element.</b>
37873 * Create a new Border
37874 * @param {Object} config Configuration options
37876 Roo.bootstrap.layout.Border = function(config){
37877 config = config || {};
37878 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37882 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37883 if(config[region]){
37884 config[region].region = region;
37885 this.addRegion(config[region]);
37891 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
37893 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37895 parent : false, // this might point to a 'nest' or a ???
37898 * Creates and adds a new region if it doesn't already exist.
37899 * @param {String} target The target region key (north, south, east, west or center).
37900 * @param {Object} config The regions config object
37901 * @return {BorderLayoutRegion} The new region
37903 addRegion : function(config)
37905 if(!this.regions[config.region]){
37906 var r = this.factory(config);
37907 this.bindRegion(r);
37909 return this.regions[config.region];
37913 bindRegion : function(r){
37914 this.regions[r.config.region] = r;
37916 r.on("visibilitychange", this.layout, this);
37917 r.on("paneladded", this.layout, this);
37918 r.on("panelremoved", this.layout, this);
37919 r.on("invalidated", this.layout, this);
37920 r.on("resized", this.onRegionResized, this);
37921 r.on("collapsed", this.onRegionCollapsed, this);
37922 r.on("expanded", this.onRegionExpanded, this);
37926 * Performs a layout update.
37928 layout : function()
37930 if(this.updating) {
37934 // render all the rebions if they have not been done alreayd?
37935 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37936 if(this.regions[region] && !this.regions[region].bodyEl){
37937 this.regions[region].onRender(this.el)
37941 var size = this.getViewSize();
37942 var w = size.width;
37943 var h = size.height;
37948 //var x = 0, y = 0;
37950 var rs = this.regions;
37951 var north = rs["north"];
37952 var south = rs["south"];
37953 var west = rs["west"];
37954 var east = rs["east"];
37955 var center = rs["center"];
37956 //if(this.hideOnLayout){ // not supported anymore
37957 //c.el.setStyle("display", "none");
37959 if(north && north.isVisible()){
37960 var b = north.getBox();
37961 var m = north.getMargins();
37962 b.width = w - (m.left+m.right);
37965 centerY = b.height + b.y + m.bottom;
37966 centerH -= centerY;
37967 north.updateBox(this.safeBox(b));
37969 if(south && south.isVisible()){
37970 var b = south.getBox();
37971 var m = south.getMargins();
37972 b.width = w - (m.left+m.right);
37974 var totalHeight = (b.height + m.top + m.bottom);
37975 b.y = h - totalHeight + m.top;
37976 centerH -= totalHeight;
37977 south.updateBox(this.safeBox(b));
37979 if(west && west.isVisible()){
37980 var b = west.getBox();
37981 var m = west.getMargins();
37982 b.height = centerH - (m.top+m.bottom);
37984 b.y = centerY + m.top;
37985 var totalWidth = (b.width + m.left + m.right);
37986 centerX += totalWidth;
37987 centerW -= totalWidth;
37988 west.updateBox(this.safeBox(b));
37990 if(east && east.isVisible()){
37991 var b = east.getBox();
37992 var m = east.getMargins();
37993 b.height = centerH - (m.top+m.bottom);
37994 var totalWidth = (b.width + m.left + m.right);
37995 b.x = w - totalWidth + m.left;
37996 b.y = centerY + m.top;
37997 centerW -= totalWidth;
37998 east.updateBox(this.safeBox(b));
38001 var m = center.getMargins();
38003 x: centerX + m.left,
38004 y: centerY + m.top,
38005 width: centerW - (m.left+m.right),
38006 height: centerH - (m.top+m.bottom)
38008 //if(this.hideOnLayout){
38009 //center.el.setStyle("display", "block");
38011 center.updateBox(this.safeBox(centerBox));
38014 this.fireEvent("layout", this);
38018 safeBox : function(box){
38019 box.width = Math.max(0, box.width);
38020 box.height = Math.max(0, box.height);
38025 * Adds a ContentPanel (or subclass) to this layout.
38026 * @param {String} target The target region key (north, south, east, west or center).
38027 * @param {Roo.ContentPanel} panel The panel to add
38028 * @return {Roo.ContentPanel} The added panel
38030 add : function(target, panel){
38032 target = target.toLowerCase();
38033 return this.regions[target].add(panel);
38037 * Remove a ContentPanel (or subclass) to this layout.
38038 * @param {String} target The target region key (north, south, east, west or center).
38039 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38040 * @return {Roo.ContentPanel} The removed panel
38042 remove : function(target, panel){
38043 target = target.toLowerCase();
38044 return this.regions[target].remove(panel);
38048 * Searches all regions for a panel with the specified id
38049 * @param {String} panelId
38050 * @return {Roo.ContentPanel} The panel or null if it wasn't found
38052 findPanel : function(panelId){
38053 var rs = this.regions;
38054 for(var target in rs){
38055 if(typeof rs[target] != "function"){
38056 var p = rs[target].getPanel(panelId);
38066 * Searches all regions for a panel with the specified id and activates (shows) it.
38067 * @param {String/ContentPanel} panelId The panels id or the panel itself
38068 * @return {Roo.ContentPanel} The shown panel or null
38070 showPanel : function(panelId) {
38071 var rs = this.regions;
38072 for(var target in rs){
38073 var r = rs[target];
38074 if(typeof r != "function"){
38075 if(r.hasPanel(panelId)){
38076 return r.showPanel(panelId);
38084 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38085 * @param {Roo.state.Provider} provider (optional) An alternate state provider
38088 restoreState : function(provider){
38090 provider = Roo.state.Manager;
38092 var sm = new Roo.LayoutStateManager();
38093 sm.init(this, provider);
38099 * Adds a xtype elements to the layout.
38103 xtype : 'ContentPanel',
38110 xtype : 'NestedLayoutPanel',
38116 items : [ ... list of content panels or nested layout panels.. ]
38120 * @param {Object} cfg Xtype definition of item to add.
38122 addxtype : function(cfg)
38124 // basically accepts a pannel...
38125 // can accept a layout region..!?!?
38126 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38129 // theory? children can only be panels??
38131 //if (!cfg.xtype.match(/Panel$/)) {
38136 if (typeof(cfg.region) == 'undefined') {
38137 Roo.log("Failed to add Panel, region was not set");
38141 var region = cfg.region;
38147 xitems = cfg.items;
38152 if ( region == 'center') {
38153 Roo.log("Center: " + cfg.title);
38159 case 'Content': // ContentPanel (el, cfg)
38160 case 'Scroll': // ContentPanel (el, cfg)
38162 cfg.autoCreate = cfg.autoCreate || true;
38163 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38165 // var el = this.el.createChild();
38166 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38169 this.add(region, ret);
38173 case 'TreePanel': // our new panel!
38174 cfg.el = this.el.createChild();
38175 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38176 this.add(region, ret);
38181 // create a new Layout (which is a Border Layout...
38183 var clayout = cfg.layout;
38184 clayout.el = this.el.createChild();
38185 clayout.items = clayout.items || [];
38189 // replace this exitems with the clayout ones..
38190 xitems = clayout.items;
38192 // force background off if it's in center...
38193 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38194 cfg.background = false;
38196 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
38199 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38200 //console.log('adding nested layout panel ' + cfg.toSource());
38201 this.add(region, ret);
38202 nb = {}; /// find first...
38207 // needs grid and region
38209 //var el = this.getRegion(region).el.createChild();
38211 *var el = this.el.createChild();
38212 // create the grid first...
38213 cfg.grid.container = el;
38214 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38217 if (region == 'center' && this.active ) {
38218 cfg.background = false;
38221 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38223 this.add(region, ret);
38225 if (cfg.background) {
38226 // render grid on panel activation (if panel background)
38227 ret.on('activate', function(gp) {
38228 if (!gp.grid.rendered) {
38229 // gp.grid.render(el);
38233 // cfg.grid.render(el);
38239 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38240 // it was the old xcomponent building that caused this before.
38241 // espeically if border is the top element in the tree.
38251 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38253 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38254 this.add(region, ret);
38258 throw "Can not add '" + cfg.xtype + "' to Border";
38264 this.beginUpdate();
38268 Roo.each(xitems, function(i) {
38269 region = nb && i.region ? i.region : false;
38271 var add = ret.addxtype(i);
38274 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38275 if (!i.background) {
38276 abn[region] = nb[region] ;
38283 // make the last non-background panel active..
38284 //if (nb) { Roo.log(abn); }
38287 for(var r in abn) {
38288 region = this.getRegion(r);
38290 // tried using nb[r], but it does not work..
38292 region.showPanel(abn[r]);
38303 factory : function(cfg)
38306 var validRegions = Roo.bootstrap.layout.Border.regions;
38308 var target = cfg.region;
38311 var r = Roo.bootstrap.layout;
38315 return new r.North(cfg);
38317 return new r.South(cfg);
38319 return new r.East(cfg);
38321 return new r.West(cfg);
38323 return new r.Center(cfg);
38325 throw 'Layout region "'+target+'" not supported.';
38332 * Ext JS Library 1.1.1
38333 * Copyright(c) 2006-2007, Ext JS, LLC.
38335 * Originally Released Under LGPL - original licence link has changed is not relivant.
38338 * <script type="text/javascript">
38342 * @class Roo.bootstrap.layout.Basic
38343 * @extends Roo.util.Observable
38344 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38345 * and does not have a titlebar, tabs or any other features. All it does is size and position
38346 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38347 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38348 * @cfg {string} region the region that it inhabits..
38349 * @cfg {bool} skipConfig skip config?
38353 Roo.bootstrap.layout.Basic = function(config){
38355 this.mgr = config.mgr;
38357 this.position = config.region;
38359 var skipConfig = config.skipConfig;
38363 * @scope Roo.BasicLayoutRegion
38367 * @event beforeremove
38368 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38369 * @param {Roo.LayoutRegion} this
38370 * @param {Roo.ContentPanel} panel The panel
38371 * @param {Object} e The cancel event object
38373 "beforeremove" : true,
38375 * @event invalidated
38376 * Fires when the layout for this region is changed.
38377 * @param {Roo.LayoutRegion} this
38379 "invalidated" : true,
38381 * @event visibilitychange
38382 * Fires when this region is shown or hidden
38383 * @param {Roo.LayoutRegion} this
38384 * @param {Boolean} visibility true or false
38386 "visibilitychange" : true,
38388 * @event paneladded
38389 * Fires when a panel is added.
38390 * @param {Roo.LayoutRegion} this
38391 * @param {Roo.ContentPanel} panel The panel
38393 "paneladded" : true,
38395 * @event panelremoved
38396 * Fires when a panel is removed.
38397 * @param {Roo.LayoutRegion} this
38398 * @param {Roo.ContentPanel} panel The panel
38400 "panelremoved" : true,
38402 * @event beforecollapse
38403 * Fires when this region before collapse.
38404 * @param {Roo.LayoutRegion} this
38406 "beforecollapse" : true,
38409 * Fires when this region is collapsed.
38410 * @param {Roo.LayoutRegion} this
38412 "collapsed" : true,
38415 * Fires when this region is expanded.
38416 * @param {Roo.LayoutRegion} this
38421 * Fires when this region is slid into view.
38422 * @param {Roo.LayoutRegion} this
38424 "slideshow" : true,
38427 * Fires when this region slides out of view.
38428 * @param {Roo.LayoutRegion} this
38430 "slidehide" : true,
38432 * @event panelactivated
38433 * Fires when a panel is activated.
38434 * @param {Roo.LayoutRegion} this
38435 * @param {Roo.ContentPanel} panel The activated panel
38437 "panelactivated" : true,
38440 * Fires when the user resizes this region.
38441 * @param {Roo.LayoutRegion} this
38442 * @param {Number} newSize The new size (width for east/west, height for north/south)
38446 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38447 this.panels = new Roo.util.MixedCollection();
38448 this.panels.getKey = this.getPanelId.createDelegate(this);
38450 this.activePanel = null;
38451 // ensure listeners are added...
38453 if (config.listeners || config.events) {
38454 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38455 listeners : config.listeners || {},
38456 events : config.events || {}
38460 if(skipConfig !== true){
38461 this.applyConfig(config);
38465 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38467 getPanelId : function(p){
38471 applyConfig : function(config){
38472 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38473 this.config = config;
38478 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38479 * the width, for horizontal (north, south) the height.
38480 * @param {Number} newSize The new width or height
38482 resizeTo : function(newSize){
38483 var el = this.el ? this.el :
38484 (this.activePanel ? this.activePanel.getEl() : null);
38486 switch(this.position){
38489 el.setWidth(newSize);
38490 this.fireEvent("resized", this, newSize);
38494 el.setHeight(newSize);
38495 this.fireEvent("resized", this, newSize);
38501 getBox : function(){
38502 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38505 getMargins : function(){
38506 return this.margins;
38509 updateBox : function(box){
38511 var el = this.activePanel.getEl();
38512 el.dom.style.left = box.x + "px";
38513 el.dom.style.top = box.y + "px";
38514 this.activePanel.setSize(box.width, box.height);
38518 * Returns the container element for this region.
38519 * @return {Roo.Element}
38521 getEl : function(){
38522 return this.activePanel;
38526 * Returns true if this region is currently visible.
38527 * @return {Boolean}
38529 isVisible : function(){
38530 return this.activePanel ? true : false;
38533 setActivePanel : function(panel){
38534 panel = this.getPanel(panel);
38535 if(this.activePanel && this.activePanel != panel){
38536 this.activePanel.setActiveState(false);
38537 this.activePanel.getEl().setLeftTop(-10000,-10000);
38539 this.activePanel = panel;
38540 panel.setActiveState(true);
38542 panel.setSize(this.box.width, this.box.height);
38544 this.fireEvent("panelactivated", this, panel);
38545 this.fireEvent("invalidated");
38549 * Show the specified panel.
38550 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38551 * @return {Roo.ContentPanel} The shown panel or null
38553 showPanel : function(panel){
38554 panel = this.getPanel(panel);
38556 this.setActivePanel(panel);
38562 * Get the active panel for this region.
38563 * @return {Roo.ContentPanel} The active panel or null
38565 getActivePanel : function(){
38566 return this.activePanel;
38570 * Add the passed ContentPanel(s)
38571 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38572 * @return {Roo.ContentPanel} The panel added (if only one was added)
38574 add : function(panel){
38575 if(arguments.length > 1){
38576 for(var i = 0, len = arguments.length; i < len; i++) {
38577 this.add(arguments[i]);
38581 if(this.hasPanel(panel)){
38582 this.showPanel(panel);
38585 var el = panel.getEl();
38586 if(el.dom.parentNode != this.mgr.el.dom){
38587 this.mgr.el.dom.appendChild(el.dom);
38589 if(panel.setRegion){
38590 panel.setRegion(this);
38592 this.panels.add(panel);
38593 el.setStyle("position", "absolute");
38594 if(!panel.background){
38595 this.setActivePanel(panel);
38596 if(this.config.initialSize && this.panels.getCount()==1){
38597 this.resizeTo(this.config.initialSize);
38600 this.fireEvent("paneladded", this, panel);
38605 * Returns true if the panel is in this region.
38606 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38607 * @return {Boolean}
38609 hasPanel : function(panel){
38610 if(typeof panel == "object"){ // must be panel obj
38611 panel = panel.getId();
38613 return this.getPanel(panel) ? true : false;
38617 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38618 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38619 * @param {Boolean} preservePanel Overrides the config preservePanel option
38620 * @return {Roo.ContentPanel} The panel that was removed
38622 remove : function(panel, preservePanel){
38623 panel = this.getPanel(panel);
38628 this.fireEvent("beforeremove", this, panel, e);
38629 if(e.cancel === true){
38632 var panelId = panel.getId();
38633 this.panels.removeKey(panelId);
38638 * Returns the panel specified or null if it's not in this region.
38639 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38640 * @return {Roo.ContentPanel}
38642 getPanel : function(id){
38643 if(typeof id == "object"){ // must be panel obj
38646 return this.panels.get(id);
38650 * Returns this regions position (north/south/east/west/center).
38653 getPosition: function(){
38654 return this.position;
38658 * Ext JS Library 1.1.1
38659 * Copyright(c) 2006-2007, Ext JS, LLC.
38661 * Originally Released Under LGPL - original licence link has changed is not relivant.
38664 * <script type="text/javascript">
38668 * @class Roo.bootstrap.layout.Region
38669 * @extends Roo.bootstrap.layout.Basic
38670 * This class represents a region in a layout manager.
38672 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38673 * @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})
38674 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
38675 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
38676 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
38677 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
38678 * @cfg {String} title The title for the region (overrides panel titles)
38679 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
38680 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38681 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
38682 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38683 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
38684 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38685 * the space available, similar to FireFox 1.5 tabs (defaults to false)
38686 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
38687 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
38688 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
38690 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
38691 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
38692 * @cfg {Boolean} disableTabTips True to disable tab tooltips
38693 * @cfg {Number} width For East/West panels
38694 * @cfg {Number} height For North/South panels
38695 * @cfg {Boolean} split To show the splitter
38696 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
38698 * @cfg {string} cls Extra CSS classes to add to region
38700 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38701 * @cfg {string} region the region that it inhabits..
38704 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
38705 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
38707 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
38708 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
38709 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
38711 Roo.bootstrap.layout.Region = function(config)
38713 this.applyConfig(config);
38715 var mgr = config.mgr;
38716 var pos = config.region;
38717 config.skipConfig = true;
38718 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38721 this.onRender(mgr.el);
38724 this.visible = true;
38725 this.collapsed = false;
38726 this.unrendered_panels = [];
38729 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38731 position: '', // set by wrapper (eg. north/south etc..)
38732 unrendered_panels : null, // unrendered panels.
38734 tabPosition : false,
38736 mgr: false, // points to 'Border'
38739 createBody : function(){
38740 /** This region's body element
38741 * @type Roo.Element */
38742 this.bodyEl = this.el.createChild({
38744 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38748 onRender: function(ctr, pos)
38750 var dh = Roo.DomHelper;
38751 /** This region's container element
38752 * @type Roo.Element */
38753 this.el = dh.append(ctr.dom, {
38755 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38757 /** This region's title element
38758 * @type Roo.Element */
38760 this.titleEl = dh.append(this.el.dom, {
38762 unselectable: "on",
38763 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38765 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38766 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38770 this.titleEl.enableDisplayMode();
38771 /** This region's title text element
38772 * @type HTMLElement */
38773 this.titleTextEl = this.titleEl.dom.firstChild;
38774 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38776 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38777 this.closeBtn.enableDisplayMode();
38778 this.closeBtn.on("click", this.closeClicked, this);
38779 this.closeBtn.hide();
38781 this.createBody(this.config);
38782 if(this.config.hideWhenEmpty){
38784 this.on("paneladded", this.validateVisibility, this);
38785 this.on("panelremoved", this.validateVisibility, this);
38787 if(this.autoScroll){
38788 this.bodyEl.setStyle("overflow", "auto");
38790 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38792 //if(c.titlebar !== false){
38793 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38794 this.titleEl.hide();
38796 this.titleEl.show();
38797 if(this.config.title){
38798 this.titleTextEl.innerHTML = this.config.title;
38802 if(this.config.collapsed){
38803 this.collapse(true);
38805 if(this.config.hidden){
38809 if (this.unrendered_panels && this.unrendered_panels.length) {
38810 for (var i =0;i< this.unrendered_panels.length; i++) {
38811 this.add(this.unrendered_panels[i]);
38813 this.unrendered_panels = null;
38819 applyConfig : function(c)
38822 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38823 var dh = Roo.DomHelper;
38824 if(c.titlebar !== false){
38825 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38826 this.collapseBtn.on("click", this.collapse, this);
38827 this.collapseBtn.enableDisplayMode();
38829 if(c.showPin === true || this.showPin){
38830 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38831 this.stickBtn.enableDisplayMode();
38832 this.stickBtn.on("click", this.expand, this);
38833 this.stickBtn.hide();
38838 /** This region's collapsed element
38839 * @type Roo.Element */
38842 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38843 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38846 if(c.floatable !== false){
38847 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38848 this.collapsedEl.on("click", this.collapseClick, this);
38851 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38852 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38853 id: "message", unselectable: "on", style:{"float":"left"}});
38854 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38856 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38857 this.expandBtn.on("click", this.expand, this);
38861 if(this.collapseBtn){
38862 this.collapseBtn.setVisible(c.collapsible == true);
38865 this.cmargins = c.cmargins || this.cmargins ||
38866 (this.position == "west" || this.position == "east" ?
38867 {top: 0, left: 2, right:2, bottom: 0} :
38868 {top: 2, left: 0, right:0, bottom: 2});
38870 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38873 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38875 this.autoScroll = c.autoScroll || false;
38880 this.duration = c.duration || .30;
38881 this.slideDuration = c.slideDuration || .45;
38886 * Returns true if this region is currently visible.
38887 * @return {Boolean}
38889 isVisible : function(){
38890 return this.visible;
38894 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38895 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
38897 //setCollapsedTitle : function(title){
38898 // title = title || " ";
38899 // if(this.collapsedTitleTextEl){
38900 // this.collapsedTitleTextEl.innerHTML = title;
38904 getBox : function(){
38906 // if(!this.collapsed){
38907 b = this.el.getBox(false, true);
38909 // b = this.collapsedEl.getBox(false, true);
38914 getMargins : function(){
38915 return this.margins;
38916 //return this.collapsed ? this.cmargins : this.margins;
38919 highlight : function(){
38920 this.el.addClass("x-layout-panel-dragover");
38923 unhighlight : function(){
38924 this.el.removeClass("x-layout-panel-dragover");
38927 updateBox : function(box)
38929 if (!this.bodyEl) {
38930 return; // not rendered yet..
38934 if(!this.collapsed){
38935 this.el.dom.style.left = box.x + "px";
38936 this.el.dom.style.top = box.y + "px";
38937 this.updateBody(box.width, box.height);
38939 this.collapsedEl.dom.style.left = box.x + "px";
38940 this.collapsedEl.dom.style.top = box.y + "px";
38941 this.collapsedEl.setSize(box.width, box.height);
38944 this.tabs.autoSizeTabs();
38948 updateBody : function(w, h)
38951 this.el.setWidth(w);
38952 w -= this.el.getBorderWidth("rl");
38953 if(this.config.adjustments){
38954 w += this.config.adjustments[0];
38957 if(h !== null && h > 0){
38958 this.el.setHeight(h);
38959 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38960 h -= this.el.getBorderWidth("tb");
38961 if(this.config.adjustments){
38962 h += this.config.adjustments[1];
38964 this.bodyEl.setHeight(h);
38966 h = this.tabs.syncHeight(h);
38969 if(this.panelSize){
38970 w = w !== null ? w : this.panelSize.width;
38971 h = h !== null ? h : this.panelSize.height;
38973 if(this.activePanel){
38974 var el = this.activePanel.getEl();
38975 w = w !== null ? w : el.getWidth();
38976 h = h !== null ? h : el.getHeight();
38977 this.panelSize = {width: w, height: h};
38978 this.activePanel.setSize(w, h);
38980 if(Roo.isIE && this.tabs){
38981 this.tabs.el.repaint();
38986 * Returns the container element for this region.
38987 * @return {Roo.Element}
38989 getEl : function(){
38994 * Hides this region.
38997 //if(!this.collapsed){
38998 this.el.dom.style.left = "-2000px";
39001 // this.collapsedEl.dom.style.left = "-2000px";
39002 // this.collapsedEl.hide();
39004 this.visible = false;
39005 this.fireEvent("visibilitychange", this, false);
39009 * Shows this region if it was previously hidden.
39012 //if(!this.collapsed){
39015 // this.collapsedEl.show();
39017 this.visible = true;
39018 this.fireEvent("visibilitychange", this, true);
39021 closeClicked : function(){
39022 if(this.activePanel){
39023 this.remove(this.activePanel);
39027 collapseClick : function(e){
39029 e.stopPropagation();
39032 e.stopPropagation();
39038 * Collapses this region.
39039 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39042 collapse : function(skipAnim, skipCheck = false){
39043 if(this.collapsed) {
39047 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39049 this.collapsed = true;
39051 this.split.el.hide();
39053 if(this.config.animate && skipAnim !== true){
39054 this.fireEvent("invalidated", this);
39055 this.animateCollapse();
39057 this.el.setLocation(-20000,-20000);
39059 this.collapsedEl.show();
39060 this.fireEvent("collapsed", this);
39061 this.fireEvent("invalidated", this);
39067 animateCollapse : function(){
39072 * Expands this region if it was previously collapsed.
39073 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39074 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39077 expand : function(e, skipAnim){
39079 e.stopPropagation();
39081 if(!this.collapsed || this.el.hasActiveFx()) {
39085 this.afterSlideIn();
39088 this.collapsed = false;
39089 if(this.config.animate && skipAnim !== true){
39090 this.animateExpand();
39094 this.split.el.show();
39096 this.collapsedEl.setLocation(-2000,-2000);
39097 this.collapsedEl.hide();
39098 this.fireEvent("invalidated", this);
39099 this.fireEvent("expanded", this);
39103 animateExpand : function(){
39107 initTabs : function()
39109 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39111 var ts = new Roo.bootstrap.panel.Tabs({
39112 el: this.bodyEl.dom,
39114 tabPosition: this.tabPosition ? this.tabPosition : 'top',
39115 disableTooltips: this.config.disableTabTips,
39116 toolbar : this.config.toolbar
39119 if(this.config.hideTabs){
39120 ts.stripWrap.setDisplayed(false);
39123 ts.resizeTabs = this.config.resizeTabs === true;
39124 ts.minTabWidth = this.config.minTabWidth || 40;
39125 ts.maxTabWidth = this.config.maxTabWidth || 250;
39126 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39127 ts.monitorResize = false;
39128 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39129 ts.bodyEl.addClass('roo-layout-tabs-body');
39130 this.panels.each(this.initPanelAsTab, this);
39133 initPanelAsTab : function(panel){
39134 var ti = this.tabs.addTab(
39138 this.config.closeOnTab && panel.isClosable(),
39141 if(panel.tabTip !== undefined){
39142 ti.setTooltip(panel.tabTip);
39144 ti.on("activate", function(){
39145 this.setActivePanel(panel);
39148 if(this.config.closeOnTab){
39149 ti.on("beforeclose", function(t, e){
39151 this.remove(panel);
39155 panel.tabItem = ti;
39160 updatePanelTitle : function(panel, title)
39162 if(this.activePanel == panel){
39163 this.updateTitle(title);
39166 var ti = this.tabs.getTab(panel.getEl().id);
39168 if(panel.tabTip !== undefined){
39169 ti.setTooltip(panel.tabTip);
39174 updateTitle : function(title){
39175 if(this.titleTextEl && !this.config.title){
39176 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
39180 setActivePanel : function(panel)
39182 panel = this.getPanel(panel);
39183 if(this.activePanel && this.activePanel != panel){
39184 if(this.activePanel.setActiveState(false) === false){
39188 this.activePanel = panel;
39189 panel.setActiveState(true);
39190 if(this.panelSize){
39191 panel.setSize(this.panelSize.width, this.panelSize.height);
39194 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39196 this.updateTitle(panel.getTitle());
39198 this.fireEvent("invalidated", this);
39200 this.fireEvent("panelactivated", this, panel);
39204 * Shows the specified panel.
39205 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39206 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39208 showPanel : function(panel)
39210 panel = this.getPanel(panel);
39213 var tab = this.tabs.getTab(panel.getEl().id);
39214 if(tab.isHidden()){
39215 this.tabs.unhideTab(tab.id);
39219 this.setActivePanel(panel);
39226 * Get the active panel for this region.
39227 * @return {Roo.ContentPanel} The active panel or null
39229 getActivePanel : function(){
39230 return this.activePanel;
39233 validateVisibility : function(){
39234 if(this.panels.getCount() < 1){
39235 this.updateTitle(" ");
39236 this.closeBtn.hide();
39239 if(!this.isVisible()){
39246 * Adds the passed ContentPanel(s) to this region.
39247 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39248 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39250 add : function(panel)
39252 if(arguments.length > 1){
39253 for(var i = 0, len = arguments.length; i < len; i++) {
39254 this.add(arguments[i]);
39259 // if we have not been rendered yet, then we can not really do much of this..
39260 if (!this.bodyEl) {
39261 this.unrendered_panels.push(panel);
39268 if(this.hasPanel(panel)){
39269 this.showPanel(panel);
39272 panel.setRegion(this);
39273 this.panels.add(panel);
39274 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39275 // sinle panel - no tab...?? would it not be better to render it with the tabs,
39276 // and hide them... ???
39277 this.bodyEl.dom.appendChild(panel.getEl().dom);
39278 if(panel.background !== true){
39279 this.setActivePanel(panel);
39281 this.fireEvent("paneladded", this, panel);
39288 this.initPanelAsTab(panel);
39292 if(panel.background !== true){
39293 this.tabs.activate(panel.getEl().id);
39295 this.fireEvent("paneladded", this, panel);
39300 * Hides the tab for the specified panel.
39301 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39303 hidePanel : function(panel){
39304 if(this.tabs && (panel = this.getPanel(panel))){
39305 this.tabs.hideTab(panel.getEl().id);
39310 * Unhides the tab for a previously hidden panel.
39311 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39313 unhidePanel : function(panel){
39314 if(this.tabs && (panel = this.getPanel(panel))){
39315 this.tabs.unhideTab(panel.getEl().id);
39319 clearPanels : function(){
39320 while(this.panels.getCount() > 0){
39321 this.remove(this.panels.first());
39326 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39327 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39328 * @param {Boolean} preservePanel Overrides the config preservePanel option
39329 * @return {Roo.ContentPanel} The panel that was removed
39331 remove : function(panel, preservePanel)
39333 panel = this.getPanel(panel);
39338 this.fireEvent("beforeremove", this, panel, e);
39339 if(e.cancel === true){
39342 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39343 var panelId = panel.getId();
39344 this.panels.removeKey(panelId);
39346 document.body.appendChild(panel.getEl().dom);
39349 this.tabs.removeTab(panel.getEl().id);
39350 }else if (!preservePanel){
39351 this.bodyEl.dom.removeChild(panel.getEl().dom);
39353 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39354 var p = this.panels.first();
39355 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39356 tempEl.appendChild(p.getEl().dom);
39357 this.bodyEl.update("");
39358 this.bodyEl.dom.appendChild(p.getEl().dom);
39360 this.updateTitle(p.getTitle());
39362 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39363 this.setActivePanel(p);
39365 panel.setRegion(null);
39366 if(this.activePanel == panel){
39367 this.activePanel = null;
39369 if(this.config.autoDestroy !== false && preservePanel !== true){
39370 try{panel.destroy();}catch(e){}
39372 this.fireEvent("panelremoved", this, panel);
39377 * Returns the TabPanel component used by this region
39378 * @return {Roo.TabPanel}
39380 getTabs : function(){
39384 createTool : function(parentEl, className){
39385 var btn = Roo.DomHelper.append(parentEl, {
39387 cls: "x-layout-tools-button",
39390 cls: "roo-layout-tools-button-inner " + className,
39394 btn.addClassOnOver("roo-layout-tools-button-over");
39399 * Ext JS Library 1.1.1
39400 * Copyright(c) 2006-2007, Ext JS, LLC.
39402 * Originally Released Under LGPL - original licence link has changed is not relivant.
39405 * <script type="text/javascript">
39411 * @class Roo.SplitLayoutRegion
39412 * @extends Roo.LayoutRegion
39413 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39415 Roo.bootstrap.layout.Split = function(config){
39416 this.cursor = config.cursor;
39417 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39420 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39422 splitTip : "Drag to resize.",
39423 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39424 useSplitTips : false,
39426 applyConfig : function(config){
39427 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39430 onRender : function(ctr,pos) {
39432 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39433 if(!this.config.split){
39438 var splitEl = Roo.DomHelper.append(ctr.dom, {
39440 id: this.el.id + "-split",
39441 cls: "roo-layout-split roo-layout-split-"+this.position,
39444 /** The SplitBar for this region
39445 * @type Roo.SplitBar */
39446 // does not exist yet...
39447 Roo.log([this.position, this.orientation]);
39449 this.split = new Roo.bootstrap.SplitBar({
39450 dragElement : splitEl,
39451 resizingElement: this.el,
39452 orientation : this.orientation
39455 this.split.on("moved", this.onSplitMove, this);
39456 this.split.useShim = this.config.useShim === true;
39457 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39458 if(this.useSplitTips){
39459 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39461 //if(config.collapsible){
39462 // this.split.el.on("dblclick", this.collapse, this);
39465 if(typeof this.config.minSize != "undefined"){
39466 this.split.minSize = this.config.minSize;
39468 if(typeof this.config.maxSize != "undefined"){
39469 this.split.maxSize = this.config.maxSize;
39471 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39472 this.hideSplitter();
39477 getHMaxSize : function(){
39478 var cmax = this.config.maxSize || 10000;
39479 var center = this.mgr.getRegion("center");
39480 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39483 getVMaxSize : function(){
39484 var cmax = this.config.maxSize || 10000;
39485 var center = this.mgr.getRegion("center");
39486 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39489 onSplitMove : function(split, newSize){
39490 this.fireEvent("resized", this, newSize);
39494 * Returns the {@link Roo.SplitBar} for this region.
39495 * @return {Roo.SplitBar}
39497 getSplitBar : function(){
39502 this.hideSplitter();
39503 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39506 hideSplitter : function(){
39508 this.split.el.setLocation(-2000,-2000);
39509 this.split.el.hide();
39515 this.split.el.show();
39517 Roo.bootstrap.layout.Split.superclass.show.call(this);
39520 beforeSlide: function(){
39521 if(Roo.isGecko){// firefox overflow auto bug workaround
39522 this.bodyEl.clip();
39524 this.tabs.bodyEl.clip();
39526 if(this.activePanel){
39527 this.activePanel.getEl().clip();
39529 if(this.activePanel.beforeSlide){
39530 this.activePanel.beforeSlide();
39536 afterSlide : function(){
39537 if(Roo.isGecko){// firefox overflow auto bug workaround
39538 this.bodyEl.unclip();
39540 this.tabs.bodyEl.unclip();
39542 if(this.activePanel){
39543 this.activePanel.getEl().unclip();
39544 if(this.activePanel.afterSlide){
39545 this.activePanel.afterSlide();
39551 initAutoHide : function(){
39552 if(this.autoHide !== false){
39553 if(!this.autoHideHd){
39554 var st = new Roo.util.DelayedTask(this.slideIn, this);
39555 this.autoHideHd = {
39556 "mouseout": function(e){
39557 if(!e.within(this.el, true)){
39561 "mouseover" : function(e){
39567 this.el.on(this.autoHideHd);
39571 clearAutoHide : function(){
39572 if(this.autoHide !== false){
39573 this.el.un("mouseout", this.autoHideHd.mouseout);
39574 this.el.un("mouseover", this.autoHideHd.mouseover);
39578 clearMonitor : function(){
39579 Roo.get(document).un("click", this.slideInIf, this);
39582 // these names are backwards but not changed for compat
39583 slideOut : function(){
39584 if(this.isSlid || this.el.hasActiveFx()){
39587 this.isSlid = true;
39588 if(this.collapseBtn){
39589 this.collapseBtn.hide();
39591 this.closeBtnState = this.closeBtn.getStyle('display');
39592 this.closeBtn.hide();
39594 this.stickBtn.show();
39597 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39598 this.beforeSlide();
39599 this.el.setStyle("z-index", 10001);
39600 this.el.slideIn(this.getSlideAnchor(), {
39601 callback: function(){
39603 this.initAutoHide();
39604 Roo.get(document).on("click", this.slideInIf, this);
39605 this.fireEvent("slideshow", this);
39612 afterSlideIn : function(){
39613 this.clearAutoHide();
39614 this.isSlid = false;
39615 this.clearMonitor();
39616 this.el.setStyle("z-index", "");
39617 if(this.collapseBtn){
39618 this.collapseBtn.show();
39620 this.closeBtn.setStyle('display', this.closeBtnState);
39622 this.stickBtn.hide();
39624 this.fireEvent("slidehide", this);
39627 slideIn : function(cb){
39628 if(!this.isSlid || this.el.hasActiveFx()){
39632 this.isSlid = false;
39633 this.beforeSlide();
39634 this.el.slideOut(this.getSlideAnchor(), {
39635 callback: function(){
39636 this.el.setLeftTop(-10000, -10000);
39638 this.afterSlideIn();
39646 slideInIf : function(e){
39647 if(!e.within(this.el)){
39652 animateCollapse : function(){
39653 this.beforeSlide();
39654 this.el.setStyle("z-index", 20000);
39655 var anchor = this.getSlideAnchor();
39656 this.el.slideOut(anchor, {
39657 callback : function(){
39658 this.el.setStyle("z-index", "");
39659 this.collapsedEl.slideIn(anchor, {duration:.3});
39661 this.el.setLocation(-10000,-10000);
39663 this.fireEvent("collapsed", this);
39670 animateExpand : function(){
39671 this.beforeSlide();
39672 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39673 this.el.setStyle("z-index", 20000);
39674 this.collapsedEl.hide({
39677 this.el.slideIn(this.getSlideAnchor(), {
39678 callback : function(){
39679 this.el.setStyle("z-index", "");
39682 this.split.el.show();
39684 this.fireEvent("invalidated", this);
39685 this.fireEvent("expanded", this);
39713 getAnchor : function(){
39714 return this.anchors[this.position];
39717 getCollapseAnchor : function(){
39718 return this.canchors[this.position];
39721 getSlideAnchor : function(){
39722 return this.sanchors[this.position];
39725 getAlignAdj : function(){
39726 var cm = this.cmargins;
39727 switch(this.position){
39743 getExpandAdj : function(){
39744 var c = this.collapsedEl, cm = this.cmargins;
39745 switch(this.position){
39747 return [-(cm.right+c.getWidth()+cm.left), 0];
39750 return [cm.right+c.getWidth()+cm.left, 0];
39753 return [0, -(cm.top+cm.bottom+c.getHeight())];
39756 return [0, cm.top+cm.bottom+c.getHeight()];
39762 * Ext JS Library 1.1.1
39763 * Copyright(c) 2006-2007, Ext JS, LLC.
39765 * Originally Released Under LGPL - original licence link has changed is not relivant.
39768 * <script type="text/javascript">
39771 * These classes are private internal classes
39773 Roo.bootstrap.layout.Center = function(config){
39774 config.region = "center";
39775 Roo.bootstrap.layout.Region.call(this, config);
39776 this.visible = true;
39777 this.minWidth = config.minWidth || 20;
39778 this.minHeight = config.minHeight || 20;
39781 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39783 // center panel can't be hidden
39787 // center panel can't be hidden
39790 getMinWidth: function(){
39791 return this.minWidth;
39794 getMinHeight: function(){
39795 return this.minHeight;
39809 Roo.bootstrap.layout.North = function(config)
39811 config.region = 'north';
39812 config.cursor = 'n-resize';
39814 Roo.bootstrap.layout.Split.call(this, config);
39818 this.split.placement = Roo.bootstrap.SplitBar.TOP;
39819 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39820 this.split.el.addClass("roo-layout-split-v");
39822 //var size = config.initialSize || config.height;
39823 //if(this.el && typeof size != "undefined"){
39824 // this.el.setHeight(size);
39827 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39829 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39832 onRender : function(ctr, pos)
39834 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39835 var size = this.config.initialSize || this.config.height;
39836 if(this.el && typeof size != "undefined"){
39837 this.el.setHeight(size);
39842 getBox : function(){
39843 if(this.collapsed){
39844 return this.collapsedEl.getBox();
39846 var box = this.el.getBox();
39848 box.height += this.split.el.getHeight();
39853 updateBox : function(box){
39854 if(this.split && !this.collapsed){
39855 box.height -= this.split.el.getHeight();
39856 this.split.el.setLeft(box.x);
39857 this.split.el.setTop(box.y+box.height);
39858 this.split.el.setWidth(box.width);
39860 if(this.collapsed){
39861 this.updateBody(box.width, null);
39863 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39871 Roo.bootstrap.layout.South = function(config){
39872 config.region = 'south';
39873 config.cursor = 's-resize';
39874 Roo.bootstrap.layout.Split.call(this, config);
39876 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39877 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39878 this.split.el.addClass("roo-layout-split-v");
39883 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39884 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39886 onRender : function(ctr, pos)
39888 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39889 var size = this.config.initialSize || this.config.height;
39890 if(this.el && typeof size != "undefined"){
39891 this.el.setHeight(size);
39896 getBox : function(){
39897 if(this.collapsed){
39898 return this.collapsedEl.getBox();
39900 var box = this.el.getBox();
39902 var sh = this.split.el.getHeight();
39909 updateBox : function(box){
39910 if(this.split && !this.collapsed){
39911 var sh = this.split.el.getHeight();
39914 this.split.el.setLeft(box.x);
39915 this.split.el.setTop(box.y-sh);
39916 this.split.el.setWidth(box.width);
39918 if(this.collapsed){
39919 this.updateBody(box.width, null);
39921 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39925 Roo.bootstrap.layout.East = function(config){
39926 config.region = "east";
39927 config.cursor = "e-resize";
39928 Roo.bootstrap.layout.Split.call(this, config);
39930 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39931 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39932 this.split.el.addClass("roo-layout-split-h");
39936 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39937 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39939 onRender : function(ctr, pos)
39941 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39942 var size = this.config.initialSize || this.config.width;
39943 if(this.el && typeof size != "undefined"){
39944 this.el.setWidth(size);
39949 getBox : function(){
39950 if(this.collapsed){
39951 return this.collapsedEl.getBox();
39953 var box = this.el.getBox();
39955 var sw = this.split.el.getWidth();
39962 updateBox : function(box){
39963 if(this.split && !this.collapsed){
39964 var sw = this.split.el.getWidth();
39966 this.split.el.setLeft(box.x);
39967 this.split.el.setTop(box.y);
39968 this.split.el.setHeight(box.height);
39971 if(this.collapsed){
39972 this.updateBody(null, box.height);
39974 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39978 Roo.bootstrap.layout.West = function(config){
39979 config.region = "west";
39980 config.cursor = "w-resize";
39982 Roo.bootstrap.layout.Split.call(this, config);
39984 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39985 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39986 this.split.el.addClass("roo-layout-split-h");
39990 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39991 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39993 onRender: function(ctr, pos)
39995 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39996 var size = this.config.initialSize || this.config.width;
39997 if(typeof size != "undefined"){
39998 this.el.setWidth(size);
40002 getBox : function(){
40003 if(this.collapsed){
40004 return this.collapsedEl.getBox();
40006 var box = this.el.getBox();
40007 if (box.width == 0) {
40008 box.width = this.config.width; // kludge?
40011 box.width += this.split.el.getWidth();
40016 updateBox : function(box){
40017 if(this.split && !this.collapsed){
40018 var sw = this.split.el.getWidth();
40020 this.split.el.setLeft(box.x+box.width);
40021 this.split.el.setTop(box.y);
40022 this.split.el.setHeight(box.height);
40024 if(this.collapsed){
40025 this.updateBody(null, box.height);
40027 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40029 });Roo.namespace("Roo.bootstrap.panel");/*
40031 * Ext JS Library 1.1.1
40032 * Copyright(c) 2006-2007, Ext JS, LLC.
40034 * Originally Released Under LGPL - original licence link has changed is not relivant.
40037 * <script type="text/javascript">
40040 * @class Roo.ContentPanel
40041 * @extends Roo.util.Observable
40042 * A basic ContentPanel element.
40043 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
40044 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
40045 * @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
40046 * @cfg {Boolean} closable True if the panel can be closed/removed
40047 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
40048 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40049 * @cfg {Toolbar} toolbar A toolbar for this panel
40050 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
40051 * @cfg {String} title The title for this panel
40052 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40053 * @cfg {String} url Calls {@link #setUrl} with this value
40054 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40055 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
40056 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
40057 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
40058 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
40059 * @cfg {Boolean} badges render the badges
40060 * @cfg {String} cls extra classes to use
40061 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40064 * Create a new ContentPanel.
40065 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40066 * @param {String/Object} config A string to set only the title or a config object
40067 * @param {String} content (optional) Set the HTML content for this panel
40068 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40070 Roo.bootstrap.panel.Content = function( config){
40072 this.tpl = config.tpl || false;
40074 var el = config.el;
40075 var content = config.content;
40077 if(config.autoCreate){ // xtype is available if this is called from factory
40080 this.el = Roo.get(el);
40081 if(!this.el && config && config.autoCreate){
40082 if(typeof config.autoCreate == "object"){
40083 if(!config.autoCreate.id){
40084 config.autoCreate.id = config.id||el;
40086 this.el = Roo.DomHelper.append(document.body,
40087 config.autoCreate, true);
40091 cls: (config.cls || '') +
40092 (config.background ? ' bg-' + config.background : '') +
40093 " roo-layout-inactive-content",
40096 if (config.iframe) {
40100 style : 'border: 0px',
40101 src : 'about:blank'
40107 elcfg.html = config.html;
40111 this.el = Roo.DomHelper.append(document.body, elcfg , true);
40112 if (config.iframe) {
40113 this.iframeEl = this.el.select('iframe',true).first();
40118 this.closable = false;
40119 this.loaded = false;
40120 this.active = false;
40123 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40125 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40127 this.wrapEl = this.el; //this.el.wrap();
40129 if (config.toolbar.items) {
40130 ti = config.toolbar.items ;
40131 delete config.toolbar.items ;
40135 this.toolbar.render(this.wrapEl, 'before');
40136 for(var i =0;i < ti.length;i++) {
40137 // Roo.log(['add child', items[i]]);
40138 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40140 this.toolbar.items = nitems;
40141 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40142 delete config.toolbar;
40146 // xtype created footer. - not sure if will work as we normally have to render first..
40147 if (this.footer && !this.footer.el && this.footer.xtype) {
40148 if (!this.wrapEl) {
40149 this.wrapEl = this.el.wrap();
40152 this.footer.container = this.wrapEl.createChild();
40154 this.footer = Roo.factory(this.footer, Roo);
40159 if(typeof config == "string"){
40160 this.title = config;
40162 Roo.apply(this, config);
40166 this.resizeEl = Roo.get(this.resizeEl, true);
40168 this.resizeEl = this.el;
40170 // handle view.xtype
40178 * Fires when this panel is activated.
40179 * @param {Roo.ContentPanel} this
40183 * @event deactivate
40184 * Fires when this panel is activated.
40185 * @param {Roo.ContentPanel} this
40187 "deactivate" : true,
40191 * Fires when this panel is resized if fitToFrame is true.
40192 * @param {Roo.ContentPanel} this
40193 * @param {Number} width The width after any component adjustments
40194 * @param {Number} height The height after any component adjustments
40200 * Fires when this tab is created
40201 * @param {Roo.ContentPanel} this
40212 if(this.autoScroll && !this.iframe){
40213 this.resizeEl.setStyle("overflow", "auto");
40215 // fix randome scrolling
40216 //this.el.on('scroll', function() {
40217 // Roo.log('fix random scolling');
40218 // this.scrollTo('top',0);
40221 content = content || this.content;
40223 this.setContent(content);
40225 if(config && config.url){
40226 this.setUrl(this.url, this.params, this.loadOnce);
40231 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40233 if (this.view && typeof(this.view.xtype) != 'undefined') {
40234 this.view.el = this.el.appendChild(document.createElement("div"));
40235 this.view = Roo.factory(this.view);
40236 this.view.render && this.view.render(false, '');
40240 this.fireEvent('render', this);
40243 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40253 setRegion : function(region){
40254 this.region = region;
40255 this.setActiveClass(region && !this.background);
40259 setActiveClass: function(state)
40262 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40263 this.el.setStyle('position','relative');
40265 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40266 this.el.setStyle('position', 'absolute');
40271 * Returns the toolbar for this Panel if one was configured.
40272 * @return {Roo.Toolbar}
40274 getToolbar : function(){
40275 return this.toolbar;
40278 setActiveState : function(active)
40280 this.active = active;
40281 this.setActiveClass(active);
40283 if(this.fireEvent("deactivate", this) === false){
40288 this.fireEvent("activate", this);
40292 * Updates this panel's element (not for iframe)
40293 * @param {String} content The new content
40294 * @param {Boolean} loadScripts (optional) true to look for and process scripts
40296 setContent : function(content, loadScripts){
40301 this.el.update(content, loadScripts);
40304 ignoreResize : function(w, h){
40305 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40308 this.lastSize = {width: w, height: h};
40313 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40314 * @return {Roo.UpdateManager} The UpdateManager
40316 getUpdateManager : function(){
40320 return this.el.getUpdateManager();
40323 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40324 * Does not work with IFRAME contents
40325 * @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:
40328 url: "your-url.php",
40329 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40330 callback: yourFunction,
40331 scope: yourObject, //(optional scope)
40334 text: "Loading...",
40340 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40341 * 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.
40342 * @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}
40343 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40344 * @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.
40345 * @return {Roo.ContentPanel} this
40353 var um = this.el.getUpdateManager();
40354 um.update.apply(um, arguments);
40360 * 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.
40361 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40362 * @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)
40363 * @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)
40364 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40366 setUrl : function(url, params, loadOnce){
40368 this.iframeEl.dom.src = url;
40372 if(this.refreshDelegate){
40373 this.removeListener("activate", this.refreshDelegate);
40375 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40376 this.on("activate", this.refreshDelegate);
40377 return this.el.getUpdateManager();
40380 _handleRefresh : function(url, params, loadOnce){
40381 if(!loadOnce || !this.loaded){
40382 var updater = this.el.getUpdateManager();
40383 updater.update(url, params, this._setLoaded.createDelegate(this));
40387 _setLoaded : function(){
40388 this.loaded = true;
40392 * Returns this panel's id
40395 getId : function(){
40400 * Returns this panel's element - used by regiosn to add.
40401 * @return {Roo.Element}
40403 getEl : function(){
40404 return this.wrapEl || this.el;
40409 adjustForComponents : function(width, height)
40411 //Roo.log('adjustForComponents ');
40412 if(this.resizeEl != this.el){
40413 width -= this.el.getFrameWidth('lr');
40414 height -= this.el.getFrameWidth('tb');
40417 var te = this.toolbar.getEl();
40418 te.setWidth(width);
40419 height -= te.getHeight();
40422 var te = this.footer.getEl();
40423 te.setWidth(width);
40424 height -= te.getHeight();
40428 if(this.adjustments){
40429 width += this.adjustments[0];
40430 height += this.adjustments[1];
40432 return {"width": width, "height": height};
40435 setSize : function(width, height){
40436 if(this.fitToFrame && !this.ignoreResize(width, height)){
40437 if(this.fitContainer && this.resizeEl != this.el){
40438 this.el.setSize(width, height);
40440 var size = this.adjustForComponents(width, height);
40442 this.iframeEl.setSize(width,height);
40445 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40446 this.fireEvent('resize', this, size.width, size.height);
40453 * Returns this panel's title
40456 getTitle : function(){
40458 if (typeof(this.title) != 'object') {
40463 for (var k in this.title) {
40464 if (!this.title.hasOwnProperty(k)) {
40468 if (k.indexOf('-') >= 0) {
40469 var s = k.split('-');
40470 for (var i = 0; i<s.length; i++) {
40471 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40474 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40481 * Set this panel's title
40482 * @param {String} title
40484 setTitle : function(title){
40485 this.title = title;
40487 this.region.updatePanelTitle(this, title);
40492 * Returns true is this panel was configured to be closable
40493 * @return {Boolean}
40495 isClosable : function(){
40496 return this.closable;
40499 beforeSlide : function(){
40501 this.resizeEl.clip();
40504 afterSlide : function(){
40506 this.resizeEl.unclip();
40510 * Force a content refresh from the URL specified in the {@link #setUrl} method.
40511 * Will fail silently if the {@link #setUrl} method has not been called.
40512 * This does not activate the panel, just updates its content.
40514 refresh : function(){
40515 if(this.refreshDelegate){
40516 this.loaded = false;
40517 this.refreshDelegate();
40522 * Destroys this panel
40524 destroy : function(){
40525 this.el.removeAllListeners();
40526 var tempEl = document.createElement("span");
40527 tempEl.appendChild(this.el.dom);
40528 tempEl.innerHTML = "";
40534 * form - if the content panel contains a form - this is a reference to it.
40535 * @type {Roo.form.Form}
40539 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40540 * This contains a reference to it.
40546 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40556 * @param {Object} cfg Xtype definition of item to add.
40560 getChildContainer: function () {
40561 return this.getEl();
40566 var ret = new Roo.factory(cfg);
40571 if (cfg.xtype.match(/^Form$/)) {
40574 //if (this.footer) {
40575 // el = this.footer.container.insertSibling(false, 'before');
40577 el = this.el.createChild();
40580 this.form = new Roo.form.Form(cfg);
40583 if ( this.form.allItems.length) {
40584 this.form.render(el.dom);
40588 // should only have one of theses..
40589 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40590 // views.. should not be just added - used named prop 'view''
40592 cfg.el = this.el.appendChild(document.createElement("div"));
40595 var ret = new Roo.factory(cfg);
40597 ret.render && ret.render(false, ''); // render blank..
40607 * @class Roo.bootstrap.panel.Grid
40608 * @extends Roo.bootstrap.panel.Content
40610 * Create a new GridPanel.
40611 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40612 * @param {Object} config A the config object
40618 Roo.bootstrap.panel.Grid = function(config)
40622 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40623 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40625 config.el = this.wrapper;
40626 //this.el = this.wrapper;
40628 if (config.container) {
40629 // ctor'ed from a Border/panel.grid
40632 this.wrapper.setStyle("overflow", "hidden");
40633 this.wrapper.addClass('roo-grid-container');
40638 if(config.toolbar){
40639 var tool_el = this.wrapper.createChild();
40640 this.toolbar = Roo.factory(config.toolbar);
40642 if (config.toolbar.items) {
40643 ti = config.toolbar.items ;
40644 delete config.toolbar.items ;
40648 this.toolbar.render(tool_el);
40649 for(var i =0;i < ti.length;i++) {
40650 // Roo.log(['add child', items[i]]);
40651 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40653 this.toolbar.items = nitems;
40655 delete config.toolbar;
40658 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40659 config.grid.scrollBody = true;;
40660 config.grid.monitorWindowResize = false; // turn off autosizing
40661 config.grid.autoHeight = false;
40662 config.grid.autoWidth = false;
40664 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40666 if (config.background) {
40667 // render grid on panel activation (if panel background)
40668 this.on('activate', function(gp) {
40669 if (!gp.grid.rendered) {
40670 gp.grid.render(this.wrapper);
40671 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40676 this.grid.render(this.wrapper);
40677 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40680 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40681 // ??? needed ??? config.el = this.wrapper;
40686 // xtype created footer. - not sure if will work as we normally have to render first..
40687 if (this.footer && !this.footer.el && this.footer.xtype) {
40689 var ctr = this.grid.getView().getFooterPanel(true);
40690 this.footer.dataSource = this.grid.dataSource;
40691 this.footer = Roo.factory(this.footer, Roo);
40692 this.footer.render(ctr);
40702 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40703 getId : function(){
40704 return this.grid.id;
40708 * Returns the grid for this panel
40709 * @return {Roo.bootstrap.Table}
40711 getGrid : function(){
40715 setSize : function(width, height){
40716 if(!this.ignoreResize(width, height)){
40717 var grid = this.grid;
40718 var size = this.adjustForComponents(width, height);
40719 // tfoot is not a footer?
40722 var gridel = grid.getGridEl();
40723 gridel.setSize(size.width, size.height);
40725 var tbd = grid.getGridEl().select('tbody', true).first();
40726 var thd = grid.getGridEl().select('thead',true).first();
40727 var tbf= grid.getGridEl().select('tfoot', true).first();
40730 size.height -= tbf.getHeight();
40733 size.height -= thd.getHeight();
40736 tbd.setSize(size.width, size.height );
40737 // this is for the account management tab -seems to work there.
40738 var thd = grid.getGridEl().select('thead',true).first();
40740 // tbd.setSize(size.width, size.height - thd.getHeight());
40749 beforeSlide : function(){
40750 this.grid.getView().scroller.clip();
40753 afterSlide : function(){
40754 this.grid.getView().scroller.unclip();
40757 destroy : function(){
40758 this.grid.destroy();
40760 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
40765 * @class Roo.bootstrap.panel.Nest
40766 * @extends Roo.bootstrap.panel.Content
40768 * Create a new Panel, that can contain a layout.Border.
40771 * @param {Roo.BorderLayout} layout The layout for this panel
40772 * @param {String/Object} config A string to set only the title or a config object
40774 Roo.bootstrap.panel.Nest = function(config)
40776 // construct with only one argument..
40777 /* FIXME - implement nicer consturctors
40778 if (layout.layout) {
40780 layout = config.layout;
40781 delete config.layout;
40783 if (layout.xtype && !layout.getEl) {
40784 // then layout needs constructing..
40785 layout = Roo.factory(layout, Roo);
40789 config.el = config.layout.getEl();
40791 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40793 config.layout.monitorWindowResize = false; // turn off autosizing
40794 this.layout = config.layout;
40795 this.layout.getEl().addClass("roo-layout-nested-layout");
40796 this.layout.parent = this;
40803 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40805 setSize : function(width, height){
40806 if(!this.ignoreResize(width, height)){
40807 var size = this.adjustForComponents(width, height);
40808 var el = this.layout.getEl();
40809 if (size.height < 1) {
40810 el.setWidth(size.width);
40812 el.setSize(size.width, size.height);
40814 var touch = el.dom.offsetWidth;
40815 this.layout.layout();
40816 // ie requires a double layout on the first pass
40817 if(Roo.isIE && !this.initialized){
40818 this.initialized = true;
40819 this.layout.layout();
40824 // activate all subpanels if not currently active..
40826 setActiveState : function(active){
40827 this.active = active;
40828 this.setActiveClass(active);
40831 this.fireEvent("deactivate", this);
40835 this.fireEvent("activate", this);
40836 // not sure if this should happen before or after..
40837 if (!this.layout) {
40838 return; // should not happen..
40841 for (var r in this.layout.regions) {
40842 reg = this.layout.getRegion(r);
40843 if (reg.getActivePanel()) {
40844 //reg.showPanel(reg.getActivePanel()); // force it to activate..
40845 reg.setActivePanel(reg.getActivePanel());
40848 if (!reg.panels.length) {
40851 reg.showPanel(reg.getPanel(0));
40860 * Returns the nested BorderLayout for this panel
40861 * @return {Roo.BorderLayout}
40863 getLayout : function(){
40864 return this.layout;
40868 * Adds a xtype elements to the layout of the nested panel
40872 xtype : 'ContentPanel',
40879 xtype : 'NestedLayoutPanel',
40885 items : [ ... list of content panels or nested layout panels.. ]
40889 * @param {Object} cfg Xtype definition of item to add.
40891 addxtype : function(cfg) {
40892 return this.layout.addxtype(cfg);
40897 * Ext JS Library 1.1.1
40898 * Copyright(c) 2006-2007, Ext JS, LLC.
40900 * Originally Released Under LGPL - original licence link has changed is not relivant.
40903 * <script type="text/javascript">
40906 * @class Roo.TabPanel
40907 * @extends Roo.util.Observable
40908 * A lightweight tab container.
40912 // basic tabs 1, built from existing content
40913 var tabs = new Roo.TabPanel("tabs1");
40914 tabs.addTab("script", "View Script");
40915 tabs.addTab("markup", "View Markup");
40916 tabs.activate("script");
40918 // more advanced tabs, built from javascript
40919 var jtabs = new Roo.TabPanel("jtabs");
40920 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40922 // set up the UpdateManager
40923 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40924 var updater = tab2.getUpdateManager();
40925 updater.setDefaultUrl("ajax1.htm");
40926 tab2.on('activate', updater.refresh, updater, true);
40928 // Use setUrl for Ajax loading
40929 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40930 tab3.setUrl("ajax2.htm", null, true);
40933 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40936 jtabs.activate("jtabs-1");
40939 * Create a new TabPanel.
40940 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40941 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40943 Roo.bootstrap.panel.Tabs = function(config){
40945 * The container element for this TabPanel.
40946 * @type Roo.Element
40948 this.el = Roo.get(config.el);
40951 if(typeof config == "boolean"){
40952 this.tabPosition = config ? "bottom" : "top";
40954 Roo.apply(this, config);
40958 if(this.tabPosition == "bottom"){
40959 // if tabs are at the bottom = create the body first.
40960 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40961 this.el.addClass("roo-tabs-bottom");
40963 // next create the tabs holders
40965 if (this.tabPosition == "west"){
40967 var reg = this.region; // fake it..
40969 if (!reg.mgr.parent) {
40972 reg = reg.mgr.parent.region;
40974 Roo.log("got nest?");
40976 if (reg.mgr.getRegion('west')) {
40977 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40978 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40979 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40980 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40981 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40989 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40990 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40991 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40992 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40997 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41000 // finally - if tabs are at the top, then create the body last..
41001 if(this.tabPosition != "bottom"){
41002 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41003 * @type Roo.Element
41005 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41006 this.el.addClass("roo-tabs-top");
41010 this.bodyEl.setStyle("position", "relative");
41012 this.active = null;
41013 this.activateDelegate = this.activate.createDelegate(this);
41018 * Fires when the active tab changes
41019 * @param {Roo.TabPanel} this
41020 * @param {Roo.TabPanelItem} activePanel The new active tab
41024 * @event beforetabchange
41025 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41026 * @param {Roo.TabPanel} this
41027 * @param {Object} e Set cancel to true on this object to cancel the tab change
41028 * @param {Roo.TabPanelItem} tab The tab being changed to
41030 "beforetabchange" : true
41033 Roo.EventManager.onWindowResize(this.onResize, this);
41034 this.cpad = this.el.getPadding("lr");
41035 this.hiddenCount = 0;
41038 // toolbar on the tabbar support...
41039 if (this.toolbar) {
41040 alert("no toolbar support yet");
41041 this.toolbar = false;
41043 var tcfg = this.toolbar;
41044 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
41045 this.toolbar = new Roo.Toolbar(tcfg);
41046 if (Roo.isSafari) {
41047 var tbl = tcfg.container.child('table', true);
41048 tbl.setAttribute('width', '100%');
41056 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41059 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41061 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41063 tabPosition : "top",
41065 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41067 currentTabWidth : 0,
41069 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41073 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41077 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41079 preferredTabWidth : 175,
41081 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41083 resizeTabs : false,
41085 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41087 monitorResize : true,
41089 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
41091 toolbar : false, // set by caller..
41093 region : false, /// set by caller
41095 disableTooltips : true, // not used yet...
41098 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41099 * @param {String} id The id of the div to use <b>or create</b>
41100 * @param {String} text The text for the tab
41101 * @param {String} content (optional) Content to put in the TabPanelItem body
41102 * @param {Boolean} closable (optional) True to create a close icon on the tab
41103 * @return {Roo.TabPanelItem} The created TabPanelItem
41105 addTab : function(id, text, content, closable, tpl)
41107 var item = new Roo.bootstrap.panel.TabItem({
41111 closable : closable,
41114 this.addTabItem(item);
41116 item.setContent(content);
41122 * Returns the {@link Roo.TabPanelItem} with the specified id/index
41123 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41124 * @return {Roo.TabPanelItem}
41126 getTab : function(id){
41127 return this.items[id];
41131 * Hides the {@link Roo.TabPanelItem} with the specified id/index
41132 * @param {String/Number} id The id or index of the TabPanelItem to hide.
41134 hideTab : function(id){
41135 var t = this.items[id];
41138 this.hiddenCount++;
41139 this.autoSizeTabs();
41144 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41145 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41147 unhideTab : function(id){
41148 var t = this.items[id];
41150 t.setHidden(false);
41151 this.hiddenCount--;
41152 this.autoSizeTabs();
41157 * Adds an existing {@link Roo.TabPanelItem}.
41158 * @param {Roo.TabPanelItem} item The TabPanelItem to add
41160 addTabItem : function(item)
41162 this.items[item.id] = item;
41163 this.items.push(item);
41164 this.autoSizeTabs();
41165 // if(this.resizeTabs){
41166 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41167 // this.autoSizeTabs();
41169 // item.autoSize();
41174 * Removes a {@link Roo.TabPanelItem}.
41175 * @param {String/Number} id The id or index of the TabPanelItem to remove.
41177 removeTab : function(id){
41178 var items = this.items;
41179 var tab = items[id];
41180 if(!tab) { return; }
41181 var index = items.indexOf(tab);
41182 if(this.active == tab && items.length > 1){
41183 var newTab = this.getNextAvailable(index);
41188 this.stripEl.dom.removeChild(tab.pnode.dom);
41189 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41190 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41192 items.splice(index, 1);
41193 delete this.items[tab.id];
41194 tab.fireEvent("close", tab);
41195 tab.purgeListeners();
41196 this.autoSizeTabs();
41199 getNextAvailable : function(start){
41200 var items = this.items;
41202 // look for a next tab that will slide over to
41203 // replace the one being removed
41204 while(index < items.length){
41205 var item = items[++index];
41206 if(item && !item.isHidden()){
41210 // if one isn't found select the previous tab (on the left)
41213 var item = items[--index];
41214 if(item && !item.isHidden()){
41222 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41223 * @param {String/Number} id The id or index of the TabPanelItem to disable.
41225 disableTab : function(id){
41226 var tab = this.items[id];
41227 if(tab && this.active != tab){
41233 * Enables a {@link Roo.TabPanelItem} that is disabled.
41234 * @param {String/Number} id The id or index of the TabPanelItem to enable.
41236 enableTab : function(id){
41237 var tab = this.items[id];
41242 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41243 * @param {String/Number} id The id or index of the TabPanelItem to activate.
41244 * @return {Roo.TabPanelItem} The TabPanelItem.
41246 activate : function(id)
41248 //Roo.log('activite:' + id);
41250 var tab = this.items[id];
41254 if(tab == this.active || tab.disabled){
41258 this.fireEvent("beforetabchange", this, e, tab);
41259 if(e.cancel !== true && !tab.disabled){
41261 this.active.hide();
41263 this.active = this.items[id];
41264 this.active.show();
41265 this.fireEvent("tabchange", this, this.active);
41271 * Gets the active {@link Roo.TabPanelItem}.
41272 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41274 getActiveTab : function(){
41275 return this.active;
41279 * Updates the tab body element to fit the height of the container element
41280 * for overflow scrolling
41281 * @param {Number} targetHeight (optional) Override the starting height from the elements height
41283 syncHeight : function(targetHeight){
41284 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41285 var bm = this.bodyEl.getMargins();
41286 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41287 this.bodyEl.setHeight(newHeight);
41291 onResize : function(){
41292 if(this.monitorResize){
41293 this.autoSizeTabs();
41298 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41300 beginUpdate : function(){
41301 this.updating = true;
41305 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41307 endUpdate : function(){
41308 this.updating = false;
41309 this.autoSizeTabs();
41313 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41315 autoSizeTabs : function()
41317 var count = this.items.length;
41318 var vcount = count - this.hiddenCount;
41321 this.stripEl.hide();
41323 this.stripEl.show();
41326 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41331 var w = Math.max(this.el.getWidth() - this.cpad, 10);
41332 var availWidth = Math.floor(w / vcount);
41333 var b = this.stripBody;
41334 if(b.getWidth() > w){
41335 var tabs = this.items;
41336 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41337 if(availWidth < this.minTabWidth){
41338 /*if(!this.sleft){ // incomplete scrolling code
41339 this.createScrollButtons();
41342 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41345 if(this.currentTabWidth < this.preferredTabWidth){
41346 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41352 * Returns the number of tabs in this TabPanel.
41355 getCount : function(){
41356 return this.items.length;
41360 * Resizes all the tabs to the passed width
41361 * @param {Number} The new width
41363 setTabWidth : function(width){
41364 this.currentTabWidth = width;
41365 for(var i = 0, len = this.items.length; i < len; i++) {
41366 if(!this.items[i].isHidden()) {
41367 this.items[i].setWidth(width);
41373 * Destroys this TabPanel
41374 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41376 destroy : function(removeEl){
41377 Roo.EventManager.removeResizeListener(this.onResize, this);
41378 for(var i = 0, len = this.items.length; i < len; i++){
41379 this.items[i].purgeListeners();
41381 if(removeEl === true){
41382 this.el.update("");
41387 createStrip : function(container)
41389 var strip = document.createElement("nav");
41390 strip.className = Roo.bootstrap.version == 4 ?
41391 "navbar-light bg-light" :
41392 "navbar navbar-default"; //"x-tabs-wrap";
41393 container.appendChild(strip);
41397 createStripList : function(strip)
41399 // div wrapper for retard IE
41400 // returns the "tr" element.
41401 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41402 //'<div class="x-tabs-strip-wrap">'+
41403 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41404 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41405 return strip.firstChild; //.firstChild.firstChild.firstChild;
41407 createBody : function(container)
41409 var body = document.createElement("div");
41410 Roo.id(body, "tab-body");
41411 //Roo.fly(body).addClass("x-tabs-body");
41412 Roo.fly(body).addClass("tab-content");
41413 container.appendChild(body);
41416 createItemBody :function(bodyEl, id){
41417 var body = Roo.getDom(id);
41419 body = document.createElement("div");
41422 //Roo.fly(body).addClass("x-tabs-item-body");
41423 Roo.fly(body).addClass("tab-pane");
41424 bodyEl.insertBefore(body, bodyEl.firstChild);
41428 createStripElements : function(stripEl, text, closable, tpl)
41430 var td = document.createElement("li"); // was td..
41431 td.className = 'nav-item';
41433 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41436 stripEl.appendChild(td);
41438 td.className = "x-tabs-closable";
41439 if(!this.closeTpl){
41440 this.closeTpl = new Roo.Template(
41441 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41442 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41443 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41446 var el = this.closeTpl.overwrite(td, {"text": text});
41447 var close = el.getElementsByTagName("div")[0];
41448 var inner = el.getElementsByTagName("em")[0];
41449 return {"el": el, "close": close, "inner": inner};
41452 // not sure what this is..
41453 // if(!this.tabTpl){
41454 //this.tabTpl = new Roo.Template(
41455 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41456 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41458 // this.tabTpl = new Roo.Template(
41459 // '<a href="#">' +
41460 // '<span unselectable="on"' +
41461 // (this.disableTooltips ? '' : ' title="{text}"') +
41462 // ' >{text}</span></a>'
41468 var template = tpl || this.tabTpl || false;
41471 template = new Roo.Template(
41472 Roo.bootstrap.version == 4 ?
41474 '<a class="nav-link" href="#" unselectable="on"' +
41475 (this.disableTooltips ? '' : ' title="{text}"') +
41478 '<a class="nav-link" href="#">' +
41479 '<span unselectable="on"' +
41480 (this.disableTooltips ? '' : ' title="{text}"') +
41481 ' >{text}</span></a>'
41486 switch (typeof(template)) {
41490 template = new Roo.Template(template);
41496 var el = template.overwrite(td, {"text": text});
41498 var inner = el.getElementsByTagName("span")[0];
41500 return {"el": el, "inner": inner};
41508 * @class Roo.TabPanelItem
41509 * @extends Roo.util.Observable
41510 * Represents an individual item (tab plus body) in a TabPanel.
41511 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41512 * @param {String} id The id of this TabPanelItem
41513 * @param {String} text The text for the tab of this TabPanelItem
41514 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41516 Roo.bootstrap.panel.TabItem = function(config){
41518 * The {@link Roo.TabPanel} this TabPanelItem belongs to
41519 * @type Roo.TabPanel
41521 this.tabPanel = config.panel;
41523 * The id for this TabPanelItem
41526 this.id = config.id;
41528 this.disabled = false;
41530 this.text = config.text;
41532 this.loaded = false;
41533 this.closable = config.closable;
41536 * The body element for this TabPanelItem.
41537 * @type Roo.Element
41539 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41540 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41541 this.bodyEl.setStyle("display", "block");
41542 this.bodyEl.setStyle("zoom", "1");
41543 //this.hideAction();
41545 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41547 this.el = Roo.get(els.el);
41548 this.inner = Roo.get(els.inner, true);
41549 this.textEl = Roo.bootstrap.version == 4 ?
41550 this.el : Roo.get(this.el.dom.firstChild, true);
41552 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41553 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41556 // this.el.on("mousedown", this.onTabMouseDown, this);
41557 this.el.on("click", this.onTabClick, this);
41559 if(config.closable){
41560 var c = Roo.get(els.close, true);
41561 c.dom.title = this.closeText;
41562 c.addClassOnOver("close-over");
41563 c.on("click", this.closeClick, this);
41569 * Fires when this tab becomes the active tab.
41570 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41571 * @param {Roo.TabPanelItem} this
41575 * @event beforeclose
41576 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41577 * @param {Roo.TabPanelItem} this
41578 * @param {Object} e Set cancel to true on this object to cancel the close.
41580 "beforeclose": true,
41583 * Fires when this tab is closed.
41584 * @param {Roo.TabPanelItem} this
41588 * @event deactivate
41589 * Fires when this tab is no longer the active tab.
41590 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41591 * @param {Roo.TabPanelItem} this
41593 "deactivate" : true
41595 this.hidden = false;
41597 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41600 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41602 purgeListeners : function(){
41603 Roo.util.Observable.prototype.purgeListeners.call(this);
41604 this.el.removeAllListeners();
41607 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41610 this.status_node.addClass("active");
41613 this.tabPanel.stripWrap.repaint();
41615 this.fireEvent("activate", this.tabPanel, this);
41619 * Returns true if this tab is the active tab.
41620 * @return {Boolean}
41622 isActive : function(){
41623 return this.tabPanel.getActiveTab() == this;
41627 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41630 this.status_node.removeClass("active");
41632 this.fireEvent("deactivate", this.tabPanel, this);
41635 hideAction : function(){
41636 this.bodyEl.hide();
41637 this.bodyEl.setStyle("position", "absolute");
41638 this.bodyEl.setLeft("-20000px");
41639 this.bodyEl.setTop("-20000px");
41642 showAction : function(){
41643 this.bodyEl.setStyle("position", "relative");
41644 this.bodyEl.setTop("");
41645 this.bodyEl.setLeft("");
41646 this.bodyEl.show();
41650 * Set the tooltip for the tab.
41651 * @param {String} tooltip The tab's tooltip
41653 setTooltip : function(text){
41654 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41655 this.textEl.dom.qtip = text;
41656 this.textEl.dom.removeAttribute('title');
41658 this.textEl.dom.title = text;
41662 onTabClick : function(e){
41663 e.preventDefault();
41664 this.tabPanel.activate(this.id);
41667 onTabMouseDown : function(e){
41668 e.preventDefault();
41669 this.tabPanel.activate(this.id);
41672 getWidth : function(){
41673 return this.inner.getWidth();
41676 setWidth : function(width){
41677 var iwidth = width - this.linode.getPadding("lr");
41678 this.inner.setWidth(iwidth);
41679 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41680 this.linode.setWidth(width);
41684 * Show or hide the tab
41685 * @param {Boolean} hidden True to hide or false to show.
41687 setHidden : function(hidden){
41688 this.hidden = hidden;
41689 this.linode.setStyle("display", hidden ? "none" : "");
41693 * Returns true if this tab is "hidden"
41694 * @return {Boolean}
41696 isHidden : function(){
41697 return this.hidden;
41701 * Returns the text for this tab
41704 getText : function(){
41708 autoSize : function(){
41709 //this.el.beginMeasure();
41710 this.textEl.setWidth(1);
41712 * #2804 [new] Tabs in Roojs
41713 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41715 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41716 //this.el.endMeasure();
41720 * Sets the text for the tab (Note: this also sets the tooltip text)
41721 * @param {String} text The tab's text and tooltip
41723 setText : function(text){
41725 this.textEl.update(text);
41726 this.setTooltip(text);
41727 //if(!this.tabPanel.resizeTabs){
41728 // this.autoSize();
41732 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41734 activate : function(){
41735 this.tabPanel.activate(this.id);
41739 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41741 disable : function(){
41742 if(this.tabPanel.active != this){
41743 this.disabled = true;
41744 this.status_node.addClass("disabled");
41749 * Enables this TabPanelItem if it was previously disabled.
41751 enable : function(){
41752 this.disabled = false;
41753 this.status_node.removeClass("disabled");
41757 * Sets the content for this TabPanelItem.
41758 * @param {String} content The content
41759 * @param {Boolean} loadScripts true to look for and load scripts
41761 setContent : function(content, loadScripts){
41762 this.bodyEl.update(content, loadScripts);
41766 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41767 * @return {Roo.UpdateManager} The UpdateManager
41769 getUpdateManager : function(){
41770 return this.bodyEl.getUpdateManager();
41774 * Set a URL to be used to load the content for this TabPanelItem.
41775 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41776 * @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)
41777 * @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)
41778 * @return {Roo.UpdateManager} The UpdateManager
41780 setUrl : function(url, params, loadOnce){
41781 if(this.refreshDelegate){
41782 this.un('activate', this.refreshDelegate);
41784 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41785 this.on("activate", this.refreshDelegate);
41786 return this.bodyEl.getUpdateManager();
41790 _handleRefresh : function(url, params, loadOnce){
41791 if(!loadOnce || !this.loaded){
41792 var updater = this.bodyEl.getUpdateManager();
41793 updater.update(url, params, this._setLoaded.createDelegate(this));
41798 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
41799 * Will fail silently if the setUrl method has not been called.
41800 * This does not activate the panel, just updates its content.
41802 refresh : function(){
41803 if(this.refreshDelegate){
41804 this.loaded = false;
41805 this.refreshDelegate();
41810 _setLoaded : function(){
41811 this.loaded = true;
41815 closeClick : function(e){
41818 this.fireEvent("beforeclose", this, o);
41819 if(o.cancel !== true){
41820 this.tabPanel.removeTab(this.id);
41824 * The text displayed in the tooltip for the close icon.
41827 closeText : "Close this tab"
41830 * This script refer to:
41831 * Title: International Telephone Input
41832 * Author: Jack O'Connor
41833 * Code version: v12.1.12
41834 * Availability: https://github.com/jackocnr/intl-tel-input.git
41837 Roo.bootstrap.PhoneInputData = function() {
41840 "Afghanistan (افغانستان)",
41845 "Albania (Shqipëri)",
41850 "Algeria (الجزائر)",
41875 "Antigua and Barbuda",
41885 "Armenia (Հայաստան)",
41901 "Austria (Österreich)",
41906 "Azerbaijan (Azərbaycan)",
41916 "Bahrain (البحرين)",
41921 "Bangladesh (বাংলাদেশ)",
41931 "Belarus (Беларусь)",
41936 "Belgium (België)",
41966 "Bosnia and Herzegovina (Босна и Херцеговина)",
41981 "British Indian Ocean Territory",
41986 "British Virgin Islands",
41996 "Bulgaria (България)",
42006 "Burundi (Uburundi)",
42011 "Cambodia (កម្ពុជា)",
42016 "Cameroon (Cameroun)",
42025 ["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"]
42028 "Cape Verde (Kabu Verdi)",
42033 "Caribbean Netherlands",
42044 "Central African Republic (République centrafricaine)",
42064 "Christmas Island",
42070 "Cocos (Keeling) Islands",
42081 "Comoros (جزر القمر)",
42086 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42091 "Congo (Republic) (Congo-Brazzaville)",
42111 "Croatia (Hrvatska)",
42132 "Czech Republic (Česká republika)",
42137 "Denmark (Danmark)",
42152 "Dominican Republic (República Dominicana)",
42156 ["809", "829", "849"]
42174 "Equatorial Guinea (Guinea Ecuatorial)",
42194 "Falkland Islands (Islas Malvinas)",
42199 "Faroe Islands (Føroyar)",
42220 "French Guiana (Guyane française)",
42225 "French Polynesia (Polynésie française)",
42240 "Georgia (საქართველო)",
42245 "Germany (Deutschland)",
42265 "Greenland (Kalaallit Nunaat)",
42302 "Guinea-Bissau (Guiné Bissau)",
42327 "Hungary (Magyarország)",
42332 "Iceland (Ísland)",
42352 "Iraq (العراق)",
42368 "Israel (ישראל)",
42395 "Jordan (الأردن)",
42400 "Kazakhstan (Казахстан)",
42421 "Kuwait (الكويت)",
42426 "Kyrgyzstan (Кыргызстан)",
42436 "Latvia (Latvija)",
42441 "Lebanon (لبنان)",
42456 "Libya (ليبيا)",
42466 "Lithuania (Lietuva)",
42481 "Macedonia (FYROM) (Македонија)",
42486 "Madagascar (Madagasikara)",
42516 "Marshall Islands",
42526 "Mauritania (موريتانيا)",
42531 "Mauritius (Moris)",
42552 "Moldova (Republica Moldova)",
42562 "Mongolia (Монгол)",
42567 "Montenegro (Crna Gora)",
42577 "Morocco (المغرب)",
42583 "Mozambique (Moçambique)",
42588 "Myanmar (Burma) (မြန်မာ)",
42593 "Namibia (Namibië)",
42608 "Netherlands (Nederland)",
42613 "New Caledonia (Nouvelle-Calédonie)",
42648 "North Korea (조선 민주주의 인민 공화국)",
42653 "Northern Mariana Islands",
42669 "Pakistan (پاکستان)",
42679 "Palestine (فلسطين)",
42689 "Papua New Guinea",
42731 "Réunion (La Réunion)",
42737 "Romania (România)",
42753 "Saint Barthélemy",
42764 "Saint Kitts and Nevis",
42774 "Saint Martin (Saint-Martin (partie française))",
42780 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42785 "Saint Vincent and the Grenadines",
42800 "São Tomé and Príncipe (São Tomé e Príncipe)",
42805 "Saudi Arabia (المملكة العربية السعودية)",
42810 "Senegal (Sénégal)",
42840 "Slovakia (Slovensko)",
42845 "Slovenia (Slovenija)",
42855 "Somalia (Soomaaliya)",
42865 "South Korea (대한민국)",
42870 "South Sudan (جنوب السودان)",
42880 "Sri Lanka (ශ්රී ලංකාව)",
42885 "Sudan (السودان)",
42895 "Svalbard and Jan Mayen",
42906 "Sweden (Sverige)",
42911 "Switzerland (Schweiz)",
42916 "Syria (سوريا)",
42961 "Trinidad and Tobago",
42966 "Tunisia (تونس)",
42971 "Turkey (Türkiye)",
42981 "Turks and Caicos Islands",
42991 "U.S. Virgin Islands",
43001 "Ukraine (Україна)",
43006 "United Arab Emirates (الإمارات العربية المتحدة)",
43028 "Uzbekistan (Oʻzbekiston)",
43038 "Vatican City (Città del Vaticano)",
43049 "Vietnam (Việt Nam)",
43054 "Wallis and Futuna (Wallis-et-Futuna)",
43059 "Western Sahara (الصحراء الغربية)",
43065 "Yemen (اليمن)",
43089 * This script refer to:
43090 * Title: International Telephone Input
43091 * Author: Jack O'Connor
43092 * Code version: v12.1.12
43093 * Availability: https://github.com/jackocnr/intl-tel-input.git
43097 * @class Roo.bootstrap.PhoneInput
43098 * @extends Roo.bootstrap.TriggerField
43099 * An input with International dial-code selection
43101 * @cfg {String} defaultDialCode default '+852'
43102 * @cfg {Array} preferedCountries default []
43105 * Create a new PhoneInput.
43106 * @param {Object} config Configuration options
43109 Roo.bootstrap.PhoneInput = function(config) {
43110 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43113 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43115 listWidth: undefined,
43117 selectedClass: 'active',
43119 invalidClass : "has-warning",
43121 validClass: 'has-success',
43123 allowed: '0123456789',
43128 * @cfg {String} defaultDialCode The default dial code when initializing the input
43130 defaultDialCode: '+852',
43133 * @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
43135 preferedCountries: false,
43137 getAutoCreate : function()
43139 var data = Roo.bootstrap.PhoneInputData();
43140 var align = this.labelAlign || this.parentLabelAlign();
43143 this.allCountries = [];
43144 this.dialCodeMapping = [];
43146 for (var i = 0; i < data.length; i++) {
43148 this.allCountries[i] = {
43152 priority: c[3] || 0,
43153 areaCodes: c[4] || null
43155 this.dialCodeMapping[c[2]] = {
43158 priority: c[3] || 0,
43159 areaCodes: c[4] || null
43171 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43172 maxlength: this.max_length,
43173 cls : 'form-control tel-input',
43174 autocomplete: 'new-password'
43177 var hiddenInput = {
43180 cls: 'hidden-tel-input'
43184 hiddenInput.name = this.name;
43187 if (this.disabled) {
43188 input.disabled = true;
43191 var flag_container = {
43208 cls: this.hasFeedback ? 'has-feedback' : '',
43214 cls: 'dial-code-holder',
43221 cls: 'roo-select2-container input-group',
43228 if (this.fieldLabel.length) {
43231 tooltip: 'This field is required'
43237 cls: 'control-label',
43243 html: this.fieldLabel
43246 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43252 if(this.indicatorpos == 'right') {
43253 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43260 if(align == 'left') {
43268 if(this.labelWidth > 12){
43269 label.style = "width: " + this.labelWidth + 'px';
43271 if(this.labelWidth < 13 && this.labelmd == 0){
43272 this.labelmd = this.labelWidth;
43274 if(this.labellg > 0){
43275 label.cls += ' col-lg-' + this.labellg;
43276 input.cls += ' col-lg-' + (12 - this.labellg);
43278 if(this.labelmd > 0){
43279 label.cls += ' col-md-' + this.labelmd;
43280 container.cls += ' col-md-' + (12 - this.labelmd);
43282 if(this.labelsm > 0){
43283 label.cls += ' col-sm-' + this.labelsm;
43284 container.cls += ' col-sm-' + (12 - this.labelsm);
43286 if(this.labelxs > 0){
43287 label.cls += ' col-xs-' + this.labelxs;
43288 container.cls += ' col-xs-' + (12 - this.labelxs);
43298 var settings = this;
43300 ['xs','sm','md','lg'].map(function(size){
43301 if (settings[size]) {
43302 cfg.cls += ' col-' + size + '-' + settings[size];
43306 this.store = new Roo.data.Store({
43307 proxy : new Roo.data.MemoryProxy({}),
43308 reader : new Roo.data.JsonReader({
43319 'name' : 'dialCode',
43323 'name' : 'priority',
43327 'name' : 'areaCodes',
43334 if(!this.preferedCountries) {
43335 this.preferedCountries = [
43342 var p = this.preferedCountries.reverse();
43345 for (var i = 0; i < p.length; i++) {
43346 for (var j = 0; j < this.allCountries.length; j++) {
43347 if(this.allCountries[j].iso2 == p[i]) {
43348 var t = this.allCountries[j];
43349 this.allCountries.splice(j,1);
43350 this.allCountries.unshift(t);
43356 this.store.proxy.data = {
43358 data: this.allCountries
43364 initEvents : function()
43367 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43369 this.indicator = this.indicatorEl();
43370 this.flag = this.flagEl();
43371 this.dialCodeHolder = this.dialCodeHolderEl();
43373 this.trigger = this.el.select('div.flag-box',true).first();
43374 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43379 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43380 _this.list.setWidth(lw);
43383 this.list.on('mouseover', this.onViewOver, this);
43384 this.list.on('mousemove', this.onViewMove, this);
43385 this.inputEl().on("keyup", this.onKeyUp, this);
43386 this.inputEl().on("keypress", this.onKeyPress, this);
43388 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43390 this.view = new Roo.View(this.list, this.tpl, {
43391 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43394 this.view.on('click', this.onViewClick, this);
43395 this.setValue(this.defaultDialCode);
43398 onTriggerClick : function(e)
43400 Roo.log('trigger click');
43405 if(this.isExpanded()){
43407 this.hasFocus = false;
43409 this.store.load({});
43410 this.hasFocus = true;
43415 isExpanded : function()
43417 return this.list.isVisible();
43420 collapse : function()
43422 if(!this.isExpanded()){
43426 Roo.get(document).un('mousedown', this.collapseIf, this);
43427 Roo.get(document).un('mousewheel', this.collapseIf, this);
43428 this.fireEvent('collapse', this);
43432 expand : function()
43436 if(this.isExpanded() || !this.hasFocus){
43440 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43441 this.list.setWidth(lw);
43444 this.restrictHeight();
43446 Roo.get(document).on('mousedown', this.collapseIf, this);
43447 Roo.get(document).on('mousewheel', this.collapseIf, this);
43449 this.fireEvent('expand', this);
43452 restrictHeight : function()
43454 this.list.alignTo(this.inputEl(), this.listAlign);
43455 this.list.alignTo(this.inputEl(), this.listAlign);
43458 onViewOver : function(e, t)
43460 if(this.inKeyMode){
43463 var item = this.view.findItemFromChild(t);
43466 var index = this.view.indexOf(item);
43467 this.select(index, false);
43472 onViewClick : function(view, doFocus, el, e)
43474 var index = this.view.getSelectedIndexes()[0];
43476 var r = this.store.getAt(index);
43479 this.onSelect(r, index);
43481 if(doFocus !== false && !this.blockFocus){
43482 this.inputEl().focus();
43486 onViewMove : function(e, t)
43488 this.inKeyMode = false;
43491 select : function(index, scrollIntoView)
43493 this.selectedIndex = index;
43494 this.view.select(index);
43495 if(scrollIntoView !== false){
43496 var el = this.view.getNode(index);
43498 this.list.scrollChildIntoView(el, false);
43503 createList : function()
43505 this.list = Roo.get(document.body).createChild({
43507 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43508 style: 'display:none'
43511 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43514 collapseIf : function(e)
43516 var in_combo = e.within(this.el);
43517 var in_list = e.within(this.list);
43518 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43520 if (in_combo || in_list || is_list) {
43526 onSelect : function(record, index)
43528 if(this.fireEvent('beforeselect', this, record, index) !== false){
43530 this.setFlagClass(record.data.iso2);
43531 this.setDialCode(record.data.dialCode);
43532 this.hasFocus = false;
43534 this.fireEvent('select', this, record, index);
43538 flagEl : function()
43540 var flag = this.el.select('div.flag',true).first();
43547 dialCodeHolderEl : function()
43549 var d = this.el.select('input.dial-code-holder',true).first();
43556 setDialCode : function(v)
43558 this.dialCodeHolder.dom.value = '+'+v;
43561 setFlagClass : function(n)
43563 this.flag.dom.className = 'flag '+n;
43566 getValue : function()
43568 var v = this.inputEl().getValue();
43569 if(this.dialCodeHolder) {
43570 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43575 setValue : function(v)
43577 var d = this.getDialCode(v);
43579 //invalid dial code
43580 if(v.length == 0 || !d || d.length == 0) {
43582 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43583 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43589 this.setFlagClass(this.dialCodeMapping[d].iso2);
43590 this.setDialCode(d);
43591 this.inputEl().dom.value = v.replace('+'+d,'');
43592 this.hiddenEl().dom.value = this.getValue();
43597 getDialCode : function(v)
43601 if (v.length == 0) {
43602 return this.dialCodeHolder.dom.value;
43606 if (v.charAt(0) != "+") {
43609 var numericChars = "";
43610 for (var i = 1; i < v.length; i++) {
43611 var c = v.charAt(i);
43614 if (this.dialCodeMapping[numericChars]) {
43615 dialCode = v.substr(1, i);
43617 if (numericChars.length == 4) {
43627 this.setValue(this.defaultDialCode);
43631 hiddenEl : function()
43633 return this.el.select('input.hidden-tel-input',true).first();
43636 // after setting val
43637 onKeyUp : function(e){
43638 this.setValue(this.getValue());
43641 onKeyPress : function(e){
43642 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43649 * @class Roo.bootstrap.MoneyField
43650 * @extends Roo.bootstrap.ComboBox
43651 * Bootstrap MoneyField class
43654 * Create a new MoneyField.
43655 * @param {Object} config Configuration options
43658 Roo.bootstrap.MoneyField = function(config) {
43660 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43664 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43667 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43669 allowDecimals : true,
43671 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43673 decimalSeparator : ".",
43675 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43677 decimalPrecision : 0,
43679 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43681 allowNegative : true,
43683 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43687 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43689 minValue : Number.NEGATIVE_INFINITY,
43691 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43693 maxValue : Number.MAX_VALUE,
43695 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43697 minText : "The minimum value for this field is {0}",
43699 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43701 maxText : "The maximum value for this field is {0}",
43703 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
43704 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43706 nanText : "{0} is not a valid number",
43708 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43712 * @cfg {String} defaults currency of the MoneyField
43713 * value should be in lkey
43715 defaultCurrency : false,
43717 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43719 thousandsDelimiter : false,
43721 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43732 getAutoCreate : function()
43734 var align = this.labelAlign || this.parentLabelAlign();
43746 cls : 'form-control roo-money-amount-input',
43747 autocomplete: 'new-password'
43750 var hiddenInput = {
43754 cls: 'hidden-number-input'
43757 if(this.max_length) {
43758 input.maxlength = this.max_length;
43762 hiddenInput.name = this.name;
43765 if (this.disabled) {
43766 input.disabled = true;
43769 var clg = 12 - this.inputlg;
43770 var cmd = 12 - this.inputmd;
43771 var csm = 12 - this.inputsm;
43772 var cxs = 12 - this.inputxs;
43776 cls : 'row roo-money-field',
43780 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43784 cls: 'roo-select2-container input-group',
43788 cls : 'form-control roo-money-currency-input',
43789 autocomplete: 'new-password',
43791 name : this.currencyName
43795 cls : 'input-group-addon',
43809 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43813 cls: this.hasFeedback ? 'has-feedback' : '',
43824 if (this.fieldLabel.length) {
43827 tooltip: 'This field is required'
43833 cls: 'control-label',
43839 html: this.fieldLabel
43842 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43848 if(this.indicatorpos == 'right') {
43849 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43856 if(align == 'left') {
43864 if(this.labelWidth > 12){
43865 label.style = "width: " + this.labelWidth + 'px';
43867 if(this.labelWidth < 13 && this.labelmd == 0){
43868 this.labelmd = this.labelWidth;
43870 if(this.labellg > 0){
43871 label.cls += ' col-lg-' + this.labellg;
43872 input.cls += ' col-lg-' + (12 - this.labellg);
43874 if(this.labelmd > 0){
43875 label.cls += ' col-md-' + this.labelmd;
43876 container.cls += ' col-md-' + (12 - this.labelmd);
43878 if(this.labelsm > 0){
43879 label.cls += ' col-sm-' + this.labelsm;
43880 container.cls += ' col-sm-' + (12 - this.labelsm);
43882 if(this.labelxs > 0){
43883 label.cls += ' col-xs-' + this.labelxs;
43884 container.cls += ' col-xs-' + (12 - this.labelxs);
43895 var settings = this;
43897 ['xs','sm','md','lg'].map(function(size){
43898 if (settings[size]) {
43899 cfg.cls += ' col-' + size + '-' + settings[size];
43906 initEvents : function()
43908 this.indicator = this.indicatorEl();
43910 this.initCurrencyEvent();
43912 this.initNumberEvent();
43915 initCurrencyEvent : function()
43918 throw "can not find store for combo";
43921 this.store = Roo.factory(this.store, Roo.data);
43922 this.store.parent = this;
43926 this.triggerEl = this.el.select('.input-group-addon', true).first();
43928 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43933 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43934 _this.list.setWidth(lw);
43937 this.list.on('mouseover', this.onViewOver, this);
43938 this.list.on('mousemove', this.onViewMove, this);
43939 this.list.on('scroll', this.onViewScroll, this);
43942 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43945 this.view = new Roo.View(this.list, this.tpl, {
43946 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43949 this.view.on('click', this.onViewClick, this);
43951 this.store.on('beforeload', this.onBeforeLoad, this);
43952 this.store.on('load', this.onLoad, this);
43953 this.store.on('loadexception', this.onLoadException, this);
43955 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43956 "up" : function(e){
43957 this.inKeyMode = true;
43961 "down" : function(e){
43962 if(!this.isExpanded()){
43963 this.onTriggerClick();
43965 this.inKeyMode = true;
43970 "enter" : function(e){
43973 if(this.fireEvent("specialkey", this, e)){
43974 this.onViewClick(false);
43980 "esc" : function(e){
43984 "tab" : function(e){
43987 if(this.fireEvent("specialkey", this, e)){
43988 this.onViewClick(false);
43996 doRelay : function(foo, bar, hname){
43997 if(hname == 'down' || this.scope.isExpanded()){
43998 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44006 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44010 initNumberEvent : function(e)
44012 this.inputEl().on("keydown" , this.fireKey, this);
44013 this.inputEl().on("focus", this.onFocus, this);
44014 this.inputEl().on("blur", this.onBlur, this);
44016 this.inputEl().relayEvent('keyup', this);
44018 if(this.indicator){
44019 this.indicator.addClass('invisible');
44022 this.originalValue = this.getValue();
44024 if(this.validationEvent == 'keyup'){
44025 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44026 this.inputEl().on('keyup', this.filterValidation, this);
44028 else if(this.validationEvent !== false){
44029 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44032 if(this.selectOnFocus){
44033 this.on("focus", this.preFocus, this);
44036 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44037 this.inputEl().on("keypress", this.filterKeys, this);
44039 this.inputEl().relayEvent('keypress', this);
44042 var allowed = "0123456789";
44044 if(this.allowDecimals){
44045 allowed += this.decimalSeparator;
44048 if(this.allowNegative){
44052 if(this.thousandsDelimiter) {
44056 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44058 var keyPress = function(e){
44060 var k = e.getKey();
44062 var c = e.getCharCode();
44065 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44066 allowed.indexOf(String.fromCharCode(c)) === -1
44072 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44076 if(allowed.indexOf(String.fromCharCode(c)) === -1){
44081 this.inputEl().on("keypress", keyPress, this);
44085 onTriggerClick : function(e)
44092 this.loadNext = false;
44094 if(this.isExpanded()){
44099 this.hasFocus = true;
44101 if(this.triggerAction == 'all') {
44102 this.doQuery(this.allQuery, true);
44106 this.doQuery(this.getRawValue());
44109 getCurrency : function()
44111 var v = this.currencyEl().getValue();
44116 restrictHeight : function()
44118 this.list.alignTo(this.currencyEl(), this.listAlign);
44119 this.list.alignTo(this.currencyEl(), this.listAlign);
44122 onViewClick : function(view, doFocus, el, e)
44124 var index = this.view.getSelectedIndexes()[0];
44126 var r = this.store.getAt(index);
44129 this.onSelect(r, index);
44133 onSelect : function(record, index){
44135 if(this.fireEvent('beforeselect', this, record, index) !== false){
44137 this.setFromCurrencyData(index > -1 ? record.data : false);
44141 this.fireEvent('select', this, record, index);
44145 setFromCurrencyData : function(o)
44149 this.lastCurrency = o;
44151 if (this.currencyField) {
44152 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44154 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
44157 this.lastSelectionText = currency;
44159 //setting default currency
44160 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44161 this.setCurrency(this.defaultCurrency);
44165 this.setCurrency(currency);
44168 setFromData : function(o)
44172 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44174 this.setFromCurrencyData(c);
44179 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44181 Roo.log('no value set for '+ (this.name ? this.name : this.id));
44184 this.setValue(value);
44188 setCurrency : function(v)
44190 this.currencyValue = v;
44193 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44198 setValue : function(v)
44200 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44206 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44208 this.inputEl().dom.value = (v == '') ? '' :
44209 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44211 if(!this.allowZero && v === '0') {
44212 this.hiddenEl().dom.value = '';
44213 this.inputEl().dom.value = '';
44220 getRawValue : function()
44222 var v = this.inputEl().getValue();
44227 getValue : function()
44229 return this.fixPrecision(this.parseValue(this.getRawValue()));
44232 parseValue : function(value)
44234 if(this.thousandsDelimiter) {
44236 r = new RegExp(",", "g");
44237 value = value.replace(r, "");
44240 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44241 return isNaN(value) ? '' : value;
44245 fixPrecision : function(value)
44247 if(this.thousandsDelimiter) {
44249 r = new RegExp(",", "g");
44250 value = value.replace(r, "");
44253 var nan = isNaN(value);
44255 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44256 return nan ? '' : value;
44258 return parseFloat(value).toFixed(this.decimalPrecision);
44261 decimalPrecisionFcn : function(v)
44263 return Math.floor(v);
44266 validateValue : function(value)
44268 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44272 var num = this.parseValue(value);
44275 this.markInvalid(String.format(this.nanText, value));
44279 if(num < this.minValue){
44280 this.markInvalid(String.format(this.minText, this.minValue));
44284 if(num > this.maxValue){
44285 this.markInvalid(String.format(this.maxText, this.maxValue));
44292 validate : function()
44294 if(this.disabled || this.allowBlank){
44299 var currency = this.getCurrency();
44301 if(this.validateValue(this.getRawValue()) && currency.length){
44306 this.markInvalid();
44310 getName: function()
44315 beforeBlur : function()
44321 var v = this.parseValue(this.getRawValue());
44328 onBlur : function()
44332 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44333 //this.el.removeClass(this.focusClass);
44336 this.hasFocus = false;
44338 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44342 var v = this.getValue();
44344 if(String(v) !== String(this.startValue)){
44345 this.fireEvent('change', this, v, this.startValue);
44348 this.fireEvent("blur", this);
44351 inputEl : function()
44353 return this.el.select('.roo-money-amount-input', true).first();
44356 currencyEl : function()
44358 return this.el.select('.roo-money-currency-input', true).first();
44361 hiddenEl : function()
44363 return this.el.select('input.hidden-number-input',true).first();
44367 * @class Roo.bootstrap.BezierSignature
44368 * @extends Roo.bootstrap.Component
44369 * Bootstrap BezierSignature class
44370 * This script refer to:
44371 * Title: Signature Pad
44373 * Availability: https://github.com/szimek/signature_pad
44376 * Create a new BezierSignature
44377 * @param {Object} config The config object
44380 Roo.bootstrap.BezierSignature = function(config){
44381 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44387 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44394 mouse_btn_down: true,
44397 * @cfg {int} canvas height
44399 canvas_height: '200px',
44402 * @cfg {float|function} Radius of a single dot.
44407 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44412 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44417 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44422 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44427 * @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.
44429 bg_color: 'rgba(0, 0, 0, 0)',
44432 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44434 dot_color: 'black',
44437 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44439 velocity_filter_weight: 0.7,
44442 * @cfg {function} Callback when stroke begin.
44447 * @cfg {function} Callback when stroke end.
44451 getAutoCreate : function()
44453 var cls = 'roo-signature column';
44456 cls += ' ' + this.cls;
44466 for(var i = 0; i < col_sizes.length; i++) {
44467 if(this[col_sizes[i]]) {
44468 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44478 cls: 'roo-signature-body',
44482 cls: 'roo-signature-body-canvas',
44483 height: this.canvas_height,
44484 width: this.canvas_width
44491 style: 'display: none'
44499 initEvents: function()
44501 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44503 var canvas = this.canvasEl();
44505 // mouse && touch event swapping...
44506 canvas.dom.style.touchAction = 'none';
44507 canvas.dom.style.msTouchAction = 'none';
44509 this.mouse_btn_down = false;
44510 canvas.on('mousedown', this._handleMouseDown, this);
44511 canvas.on('mousemove', this._handleMouseMove, this);
44512 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44514 if (window.PointerEvent) {
44515 canvas.on('pointerdown', this._handleMouseDown, this);
44516 canvas.on('pointermove', this._handleMouseMove, this);
44517 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44520 if ('ontouchstart' in window) {
44521 canvas.on('touchstart', this._handleTouchStart, this);
44522 canvas.on('touchmove', this._handleTouchMove, this);
44523 canvas.on('touchend', this._handleTouchEnd, this);
44526 Roo.EventManager.onWindowResize(this.resize, this, true);
44528 // file input event
44529 this.fileEl().on('change', this.uploadImage, this);
44536 resize: function(){
44538 var canvas = this.canvasEl().dom;
44539 var ctx = this.canvasElCtx();
44540 var img_data = false;
44542 if(canvas.width > 0) {
44543 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44545 // setting canvas width will clean img data
44548 var style = window.getComputedStyle ?
44549 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44551 var padding_left = parseInt(style.paddingLeft) || 0;
44552 var padding_right = parseInt(style.paddingRight) || 0;
44554 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44557 ctx.putImageData(img_data, 0, 0);
44561 _handleMouseDown: function(e)
44563 if (e.browserEvent.which === 1) {
44564 this.mouse_btn_down = true;
44565 this.strokeBegin(e);
44569 _handleMouseMove: function (e)
44571 if (this.mouse_btn_down) {
44572 this.strokeMoveUpdate(e);
44576 _handleMouseUp: function (e)
44578 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44579 this.mouse_btn_down = false;
44584 _handleTouchStart: function (e) {
44586 e.preventDefault();
44587 if (e.browserEvent.targetTouches.length === 1) {
44588 // var touch = e.browserEvent.changedTouches[0];
44589 // this.strokeBegin(touch);
44591 this.strokeBegin(e); // assume e catching the correct xy...
44595 _handleTouchMove: function (e) {
44596 e.preventDefault();
44597 // var touch = event.targetTouches[0];
44598 // _this._strokeMoveUpdate(touch);
44599 this.strokeMoveUpdate(e);
44602 _handleTouchEnd: function (e) {
44603 var wasCanvasTouched = e.target === this.canvasEl().dom;
44604 if (wasCanvasTouched) {
44605 e.preventDefault();
44606 // var touch = event.changedTouches[0];
44607 // _this._strokeEnd(touch);
44612 reset: function () {
44613 this._lastPoints = [];
44614 this._lastVelocity = 0;
44615 this._lastWidth = (this.min_width + this.max_width) / 2;
44616 this.canvasElCtx().fillStyle = this.dot_color;
44619 strokeMoveUpdate: function(e)
44621 this.strokeUpdate(e);
44623 if (this.throttle) {
44624 this.throttleStroke(this.strokeUpdate, this.throttle);
44627 this.strokeUpdate(e);
44631 strokeBegin: function(e)
44633 var newPointGroup = {
44634 color: this.dot_color,
44638 if (typeof this.onBegin === 'function') {
44642 this.curve_data.push(newPointGroup);
44644 this.strokeUpdate(e);
44647 strokeUpdate: function(e)
44649 var rect = this.canvasEl().dom.getBoundingClientRect();
44650 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44651 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44652 var lastPoints = lastPointGroup.points;
44653 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44654 var isLastPointTooClose = lastPoint
44655 ? point.distanceTo(lastPoint) <= this.min_distance
44657 var color = lastPointGroup.color;
44658 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44659 var curve = this.addPoint(point);
44661 this.drawDot({color: color, point: point});
44664 this.drawCurve({color: color, curve: curve});
44674 strokeEnd: function(e)
44676 this.strokeUpdate(e);
44677 if (typeof this.onEnd === 'function') {
44682 addPoint: function (point) {
44683 var _lastPoints = this._lastPoints;
44684 _lastPoints.push(point);
44685 if (_lastPoints.length > 2) {
44686 if (_lastPoints.length === 3) {
44687 _lastPoints.unshift(_lastPoints[0]);
44689 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44690 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44691 _lastPoints.shift();
44697 calculateCurveWidths: function (startPoint, endPoint) {
44698 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44699 (1 - this.velocity_filter_weight) * this._lastVelocity;
44701 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44704 start: this._lastWidth
44707 this._lastVelocity = velocity;
44708 this._lastWidth = newWidth;
44712 drawDot: function (_a) {
44713 var color = _a.color, point = _a.point;
44714 var ctx = this.canvasElCtx();
44715 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44717 this.drawCurveSegment(point.x, point.y, width);
44719 ctx.fillStyle = color;
44723 drawCurve: function (_a) {
44724 var color = _a.color, curve = _a.curve;
44725 var ctx = this.canvasElCtx();
44726 var widthDelta = curve.endWidth - curve.startWidth;
44727 var drawSteps = Math.floor(curve.length()) * 2;
44729 ctx.fillStyle = color;
44730 for (var i = 0; i < drawSteps; i += 1) {
44731 var t = i / drawSteps;
44737 var x = uuu * curve.startPoint.x;
44738 x += 3 * uu * t * curve.control1.x;
44739 x += 3 * u * tt * curve.control2.x;
44740 x += ttt * curve.endPoint.x;
44741 var y = uuu * curve.startPoint.y;
44742 y += 3 * uu * t * curve.control1.y;
44743 y += 3 * u * tt * curve.control2.y;
44744 y += ttt * curve.endPoint.y;
44745 var width = curve.startWidth + ttt * widthDelta;
44746 this.drawCurveSegment(x, y, width);
44752 drawCurveSegment: function (x, y, width) {
44753 var ctx = this.canvasElCtx();
44755 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44756 this.is_empty = false;
44761 var ctx = this.canvasElCtx();
44762 var canvas = this.canvasEl().dom;
44763 ctx.fillStyle = this.bg_color;
44764 ctx.clearRect(0, 0, canvas.width, canvas.height);
44765 ctx.fillRect(0, 0, canvas.width, canvas.height);
44766 this.curve_data = [];
44768 this.is_empty = true;
44773 return this.el.select('input',true).first();
44776 canvasEl: function()
44778 return this.el.select('canvas',true).first();
44781 canvasElCtx: function()
44783 return this.el.select('canvas',true).first().dom.getContext('2d');
44786 getImage: function(type)
44788 if(this.is_empty) {
44793 return this.canvasEl().dom.toDataURL('image/'+type, 1);
44796 drawFromImage: function(img_src)
44798 var img = new Image();
44800 img.onload = function(){
44801 this.canvasElCtx().drawImage(img, 0, 0);
44806 this.is_empty = false;
44809 selectImage: function()
44811 this.fileEl().dom.click();
44814 uploadImage: function(e)
44816 var reader = new FileReader();
44818 reader.onload = function(e){
44819 var img = new Image();
44820 img.onload = function(){
44822 this.canvasElCtx().drawImage(img, 0, 0);
44824 img.src = e.target.result;
44827 reader.readAsDataURL(e.target.files[0]);
44830 // Bezier Point Constructor
44831 Point: (function () {
44832 function Point(x, y, time) {
44835 this.time = time || Date.now();
44837 Point.prototype.distanceTo = function (start) {
44838 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44840 Point.prototype.equals = function (other) {
44841 return this.x === other.x && this.y === other.y && this.time === other.time;
44843 Point.prototype.velocityFrom = function (start) {
44844 return this.time !== start.time
44845 ? this.distanceTo(start) / (this.time - start.time)
44852 // Bezier Constructor
44853 Bezier: (function () {
44854 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44855 this.startPoint = startPoint;
44856 this.control2 = control2;
44857 this.control1 = control1;
44858 this.endPoint = endPoint;
44859 this.startWidth = startWidth;
44860 this.endWidth = endWidth;
44862 Bezier.fromPoints = function (points, widths, scope) {
44863 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44864 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44865 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44867 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44868 var dx1 = s1.x - s2.x;
44869 var dy1 = s1.y - s2.y;
44870 var dx2 = s2.x - s3.x;
44871 var dy2 = s2.y - s3.y;
44872 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44873 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44874 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44875 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44876 var dxm = m1.x - m2.x;
44877 var dym = m1.y - m2.y;
44878 var k = l2 / (l1 + l2);
44879 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44880 var tx = s2.x - cm.x;
44881 var ty = s2.y - cm.y;
44883 c1: new scope.Point(m1.x + tx, m1.y + ty),
44884 c2: new scope.Point(m2.x + tx, m2.y + ty)
44887 Bezier.prototype.length = function () {
44892 for (var i = 0; i <= steps; i += 1) {
44894 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44895 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44897 var xdiff = cx - px;
44898 var ydiff = cy - py;
44899 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44906 Bezier.prototype.point = function (t, start, c1, c2, end) {
44907 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44908 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44909 + (3.0 * c2 * (1.0 - t) * t * t)
44910 + (end * t * t * t);
44915 throttleStroke: function(fn, wait) {
44916 if (wait === void 0) { wait = 250; }
44918 var timeout = null;
44922 var later = function () {
44923 previous = Date.now();
44925 result = fn.apply(storedContext, storedArgs);
44927 storedContext = null;
44931 return function wrapper() {
44933 for (var _i = 0; _i < arguments.length; _i++) {
44934 args[_i] = arguments[_i];
44936 var now = Date.now();
44937 var remaining = wait - (now - previous);
44938 storedContext = this;
44940 if (remaining <= 0 || remaining > wait) {
44942 clearTimeout(timeout);
44946 result = fn.apply(storedContext, storedArgs);
44948 storedContext = null;
44952 else if (!timeout) {
44953 timeout = window.setTimeout(later, remaining);