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()
366 * Fetch the element to display the tooltip on.
367 * @return {Roo.Element} defaults to this.el
369 tooltipEl : function()
374 addxtype : function(tree,cntr)
378 cn = Roo.factory(tree);
379 //Roo.log(['addxtype', cn]);
381 cn.parentType = this.xtype; //??
382 cn.parentId = this.id;
384 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
385 if (typeof(cn.container_method) == 'string') {
386 cntr = cn.container_method;
390 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
392 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
394 var build_from_html = Roo.XComponent.build_from_html;
396 var is_body = (tree.xtype == 'Body') ;
398 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
400 var self_cntr_el = Roo.get(this[cntr](false));
402 // do not try and build conditional elements
403 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
407 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
408 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
409 return this.addxtypeChild(tree,cntr, is_body);
412 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
415 return this.addxtypeChild(Roo.apply({}, tree),cntr);
418 Roo.log('skipping render');
424 if (!build_from_html) {
428 // this i think handles overlaying multiple children of the same type
429 // with the sam eelement.. - which might be buggy..
431 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
437 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
441 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
448 addxtypeChild : function (tree, cntr, is_body)
450 Roo.debug && Roo.log('addxtypeChild:' + cntr);
452 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
455 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
456 (typeof(tree['flexy:foreach']) != 'undefined');
460 skip_children = false;
461 // render the element if it's not BODY.
464 // if parent was disabled, then do not try and create the children..
465 if(!this[cntr](true)){
470 cn = Roo.factory(tree);
472 cn.parentType = this.xtype; //??
473 cn.parentId = this.id;
475 var build_from_html = Roo.XComponent.build_from_html;
478 // does the container contain child eleemnts with 'xtype' attributes.
479 // that match this xtype..
480 // note - when we render we create these as well..
481 // so we should check to see if body has xtype set.
482 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
484 var self_cntr_el = Roo.get(this[cntr](false));
485 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
487 //Roo.log(Roo.XComponent.build_from_html);
488 //Roo.log("got echild:");
491 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
492 // and are not displayed -this causes this to use up the wrong element when matching.
493 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
496 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
497 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
503 //echild.dom.removeAttribute('xtype');
505 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
506 Roo.debug && Roo.log(self_cntr_el);
507 Roo.debug && Roo.log(echild);
508 Roo.debug && Roo.log(cn);
514 // if object has flexy:if - then it may or may not be rendered.
515 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
516 // skip a flexy if element.
517 Roo.debug && Roo.log('skipping render');
518 Roo.debug && Roo.log(tree);
520 Roo.debug && Roo.log('skipping all children');
521 skip_children = true;
526 // actually if flexy:foreach is found, we really want to create
527 // multiple copies here...
529 //Roo.log(this[cntr]());
530 // some elements do not have render methods.. like the layouts...
532 if(this[cntr](true) === false){
537 cn.render && cn.render(this[cntr](true));
540 // then add the element..
547 if (typeof (tree.menu) != 'undefined') {
548 tree.menu.parentType = cn.xtype;
549 tree.menu.triggerEl = cn.el;
550 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
554 if (!tree.items || !tree.items.length) {
556 //Roo.log(["no children", this]);
561 var items = tree.items;
564 //Roo.log(items.length);
566 if (!skip_children) {
567 for(var i =0;i < items.length;i++) {
568 // Roo.log(['add child', items[i]]);
569 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
575 //Roo.log("fire childrenrendered");
577 cn.fireEvent('childrenrendered', this);
583 * Set the element that will be used to show or hide
585 setVisibilityEl : function(el)
587 this.visibilityEl = el;
591 * Get the element that will be used to show or hide
593 getVisibilityEl : function()
595 if (typeof(this.visibilityEl) == 'object') {
596 return this.visibilityEl;
599 if (typeof(this.visibilityEl) == 'string') {
600 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
607 * Show a component - removes 'hidden' class
611 if(!this.getVisibilityEl()){
615 this.getVisibilityEl().removeClass(['hidden','d-none']);
617 this.fireEvent('show', this);
622 * Hide a component - adds 'hidden' class
626 if(!this.getVisibilityEl()){
630 this.getVisibilityEl().addClass(['hidden','d-none']);
632 this.fireEvent('hide', this);
645 * @class Roo.bootstrap.Element
646 * @extends Roo.bootstrap.Component
647 * Bootstrap Element class
648 * @cfg {String} html contents of the element
649 * @cfg {String} tag tag of the element
650 * @cfg {String} cls class of the element
651 * @cfg {Boolean} preventDefault (true|false) default false
652 * @cfg {Boolean} clickable (true|false) default false
655 * Create a new Element
656 * @param {Object} config The config object
659 Roo.bootstrap.Element = function(config){
660 Roo.bootstrap.Element.superclass.constructor.call(this, config);
666 * When a element is chick
667 * @param {Roo.bootstrap.Element} this
668 * @param {Roo.EventObject} e
674 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
679 preventDefault: false,
682 getAutoCreate : function(){
686 // cls: this.cls, double assign in parent class Component.js :: onRender
693 initEvents: function()
695 Roo.bootstrap.Element.superclass.initEvents.call(this);
698 this.el.on('click', this.onClick, this);
703 onClick : function(e)
705 if(this.preventDefault){
709 this.fireEvent('click', this, e);
712 getValue : function()
714 return this.el.dom.innerHTML;
717 setValue : function(value)
719 this.el.dom.innerHTML = value;
734 * @class Roo.bootstrap.DropTarget
735 * @extends Roo.bootstrap.Element
736 * Bootstrap DropTarget class
738 * @cfg {string} name dropable name
741 * Create a new Dropable Area
742 * @param {Object} config The config object
745 Roo.bootstrap.DropTarget = function(config){
746 Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
752 * When a element is chick
753 * @param {Roo.bootstrap.Element} this
754 * @param {Roo.EventObject} e
760 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element, {
763 getAutoCreate : function(){
768 initEvents: function()
770 Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
771 this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
774 drop : this.dragDrop.createDelegate(this),
775 enter : this.dragEnter.createDelegate(this),
776 out : this.dragOut.createDelegate(this),
777 over : this.dragOver.createDelegate(this)
781 this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
784 dragDrop : function(source,e,data)
786 // user has to decide how to impliment this.
789 //this.fireEvent('drop', this, source, e ,data);
793 dragEnter : function(n, dd, e, data)
795 // probably want to resize the element to match the dropped element..
797 this.originalSize = this.el.getSize();
798 this.el.setSize( n.el.getSize());
799 this.dropZone.DDM.refreshCache(this.name);
800 Roo.log([n, dd, e, data]);
803 dragOut : function(value)
805 // resize back to normal
807 this.el.setSize(this.originalSize);
808 this.dropZone.resetConstraints();
811 dragOver : function()
828 * @class Roo.bootstrap.Body
829 * @extends Roo.bootstrap.Component
830 * Bootstrap Body class
834 * @param {Object} config The config object
837 Roo.bootstrap.Body = function(config){
839 config = config || {};
841 Roo.bootstrap.Body.superclass.constructor.call(this, config);
842 this.el = Roo.get(config.el ? config.el : document.body );
843 if (this.cls && this.cls.length) {
844 Roo.get(document.body).addClass(this.cls);
848 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
850 is_body : true,// just to make sure it's constructed?
855 onRender : function(ct, position)
857 /* Roo.log("Roo.bootstrap.Body - onRender");
858 if (this.cls && this.cls.length) {
859 Roo.get(document.body).addClass(this.cls);
878 * @class Roo.bootstrap.ButtonGroup
879 * @extends Roo.bootstrap.Component
880 * Bootstrap ButtonGroup class
881 * @cfg {String} size lg | sm | xs (default empty normal)
882 * @cfg {String} align vertical | justified (default none)
883 * @cfg {String} direction up | down (default down)
884 * @cfg {Boolean} toolbar false | true
885 * @cfg {Boolean} btn true | false
890 * @param {Object} config The config object
893 Roo.bootstrap.ButtonGroup = function(config){
894 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
897 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
905 getAutoCreate : function(){
911 cfg.html = this.html || cfg.html;
922 if (['vertical','justified'].indexOf(this.align)!==-1) {
923 cfg.cls = 'btn-group-' + this.align;
925 if (this.align == 'justified') {
926 console.log(this.items);
930 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
931 cfg.cls += ' btn-group-' + this.size;
934 if (this.direction == 'up') {
935 cfg.cls += ' dropup' ;
941 * Add a button to the group (similar to NavItem API.)
943 addItem : function(cfg)
945 var cn = new Roo.bootstrap.Button(cfg);
947 cn.parentId = this.id;
948 cn.onRender(this.el, null);
962 * @class Roo.bootstrap.Button
963 * @extends Roo.bootstrap.Component
964 * Bootstrap Button class
965 * @cfg {String} html The button content
966 * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
967 * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
968 * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
969 * @cfg {String} size (lg|sm|xs)
970 * @cfg {String} tag (a|input|submit)
971 * @cfg {String} href empty or href
972 * @cfg {Boolean} disabled default false;
973 * @cfg {Boolean} isClose default false;
974 * @cfg {String} glyphicon depricated - use fa
975 * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
976 * @cfg {String} badge text for badge
977 * @cfg {String} theme (default|glow)
978 * @cfg {Boolean} inverse dark themed version
979 * @cfg {Boolean} toggle is it a slidy toggle button
980 * @cfg {Boolean} pressed default null - if the button ahs active state
981 * @cfg {String} ontext text for on slidy toggle state
982 * @cfg {String} offtext text for off slidy toggle state
983 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
984 * @cfg {Boolean} removeClass remove the standard class..
985 * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href.
986 * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
989 * Create a new button
990 * @param {Object} config The config object
994 Roo.bootstrap.Button = function(config){
995 Roo.bootstrap.Button.superclass.constructor.call(this, config);
1001 * When a button is pressed
1002 * @param {Roo.bootstrap.Button} btn
1003 * @param {Roo.EventObject} e
1008 * When a button is double clicked
1009 * @param {Roo.bootstrap.Button} btn
1010 * @param {Roo.EventObject} e
1015 * After the button has been toggles
1016 * @param {Roo.bootstrap.Button} btn
1017 * @param {Roo.EventObject} e
1018 * @param {boolean} pressed (also available as button.pressed)
1024 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
1045 preventDefault: true,
1054 getAutoCreate : function(){
1062 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1063 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1064 this.tag = 'button';
1068 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1070 if (this.toggle == true) {
1073 cls: 'slider-frame roo-button',
1077 'data-on-text':'ON',
1078 'data-off-text':'OFF',
1079 cls: 'slider-button',
1084 // why are we validating the weights?
1085 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1086 cfg.cls += ' ' + this.weight;
1093 cfg.cls += ' close';
1095 cfg["aria-hidden"] = true;
1097 cfg.html = "×";
1103 if (this.theme==='default') {
1104 cfg.cls = 'btn roo-button';
1106 //if (this.parentType != 'Navbar') {
1107 this.weight = this.weight.length ? this.weight : 'default';
1109 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1111 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1112 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1113 cfg.cls += ' btn-' + outline + weight;
1114 if (this.weight == 'default') {
1116 cfg.cls += ' btn-' + this.weight;
1119 } else if (this.theme==='glow') {
1122 cfg.cls = 'btn-glow roo-button';
1124 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1126 cfg.cls += ' ' + this.weight;
1132 this.cls += ' inverse';
1136 if (this.active || this.pressed === true) {
1137 cfg.cls += ' active';
1140 if (this.disabled) {
1141 cfg.disabled = 'disabled';
1145 Roo.log('changing to ul' );
1147 this.glyphicon = 'caret';
1148 if (Roo.bootstrap.version == 4) {
1149 this.fa = 'caret-down';
1154 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1156 //gsRoo.log(this.parentType);
1157 if (this.parentType === 'Navbar' && !this.parent().bar) {
1158 Roo.log('changing to li?');
1167 href : this.href || '#'
1170 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
1171 cfg.cls += ' dropdown';
1178 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
1180 if (this.glyphicon) {
1181 cfg.html = ' ' + cfg.html;
1186 cls: 'glyphicon glyphicon-' + this.glyphicon
1191 cfg.html = ' ' + cfg.html;
1196 cls: 'fa fas fa-' + this.fa
1206 // cfg.cls='btn roo-button';
1210 var value = cfg.html;
1215 cls: 'glyphicon glyphicon-' + this.glyphicon,
1222 cls: 'fa fas fa-' + this.fa,
1227 var bw = this.badge_weight.length ? this.badge_weight :
1228 (this.weight.length ? this.weight : 'secondary');
1229 bw = bw == 'default' ? 'secondary' : bw;
1235 cls: 'badge badge-' + bw,
1244 cfg.cls += ' dropdown';
1245 cfg.html = typeof(cfg.html) != 'undefined' ?
1246 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1249 if (cfg.tag !== 'a' && this.href !== '') {
1250 throw "Tag must be a to set href.";
1251 } else if (this.href.length > 0) {
1252 cfg.href = this.href;
1255 if(this.removeClass){
1260 cfg.target = this.target;
1265 initEvents: function() {
1266 // Roo.log('init events?');
1267 // Roo.log(this.el.dom);
1270 if (typeof (this.menu) != 'undefined') {
1271 this.menu.parentType = this.xtype;
1272 this.menu.triggerEl = this.el;
1273 this.addxtype(Roo.apply({}, this.menu));
1277 if (this.el.hasClass('roo-button')) {
1278 this.el.on('click', this.onClick, this);
1279 this.el.on('dblclick', this.onDblClick, this);
1281 this.el.select('.roo-button').on('click', this.onClick, this);
1282 this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1286 if(this.removeClass){
1287 this.el.on('click', this.onClick, this);
1290 if (this.group === true) {
1291 if (this.pressed === false || this.pressed === true) {
1294 this.pressed = false;
1295 this.setActive(this.pressed);
1300 this.el.enableDisplayMode();
1303 onClick : function(e)
1305 if (this.disabled) {
1309 Roo.log('button on click ');
1310 if(this.preventDefault){
1319 this.setActive(true);
1320 var pi = this.parent().items;
1321 for (var i = 0;i < pi.length;i++) {
1322 if (this == pi[i]) {
1325 if (pi[i].el.hasClass('roo-button')) {
1326 pi[i].setActive(false);
1329 this.fireEvent('click', this, e);
1333 if (this.pressed === true || this.pressed === false) {
1334 this.toggleActive(e);
1338 this.fireEvent('click', this, e);
1340 onDblClick: function(e)
1342 if (this.disabled) {
1345 if(this.preventDefault){
1348 this.fireEvent('dblclick', this, e);
1351 * Enables this button
1355 this.disabled = false;
1356 this.el.removeClass('disabled');
1360 * Disable this button
1362 disable : function()
1364 this.disabled = true;
1365 this.el.addClass('disabled');
1368 * sets the active state on/off,
1369 * @param {Boolean} state (optional) Force a particular state
1371 setActive : function(v) {
1373 this.el[v ? 'addClass' : 'removeClass']('active');
1377 * toggles the current active state
1379 toggleActive : function(e)
1381 this.setActive(!this.pressed); // this modifies pressed...
1382 this.fireEvent('toggle', this, e, this.pressed);
1385 * get the current active state
1386 * @return {boolean} true if it's active
1388 isActive : function()
1390 return this.el.hasClass('active');
1393 * set the text of the first selected button
1395 setText : function(str)
1397 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1400 * get the text of the first selected button
1402 getText : function()
1404 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1407 setWeight : function(str)
1409 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1410 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1412 var outline = this.outline ? 'outline-' : '';
1413 if (str == 'default') {
1414 this.el.addClass('btn-default btn-outline-secondary');
1417 this.el.addClass('btn-' + outline + str);
1422 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1424 Roo.bootstrap.Button.weights = [
1444 * @class Roo.bootstrap.Column
1445 * @extends Roo.bootstrap.Component
1446 * Bootstrap Column class
1447 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1448 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1449 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1450 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1451 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1452 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1453 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1454 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1457 * @cfg {Boolean} hidden (true|false) hide the element
1458 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1459 * @cfg {String} fa (ban|check|...) font awesome icon
1460 * @cfg {Number} fasize (1|2|....) font awsome size
1462 * @cfg {String} icon (info-sign|check|...) glyphicon name
1464 * @cfg {String} html content of column.
1467 * Create a new Column
1468 * @param {Object} config The config object
1471 Roo.bootstrap.Column = function(config){
1472 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1475 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1493 getAutoCreate : function(){
1494 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1502 var sizes = ['xs','sm','md','lg'];
1503 sizes.map(function(size ,ix){
1504 //Roo.log( size + ':' + settings[size]);
1506 if (settings[size+'off'] !== false) {
1507 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1510 if (settings[size] === false) {
1514 if (!settings[size]) { // 0 = hidden
1515 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1517 for (var i = ix; i > -1; i--) {
1518 cfg.cls += ' d-' + sizes[i] + '-none';
1524 cfg.cls += ' col-' + size + '-' + settings[size] + (
1525 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1531 cfg.cls += ' hidden';
1534 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1535 cfg.cls +=' alert alert-' + this.alert;
1539 if (this.html.length) {
1540 cfg.html = this.html;
1544 if (this.fasize > 1) {
1545 fasize = ' fa-' + this.fasize + 'x';
1547 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1552 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1571 * @class Roo.bootstrap.Container
1572 * @extends Roo.bootstrap.Component
1573 * Bootstrap Container class
1574 * @cfg {Boolean} jumbotron is it a jumbotron element
1575 * @cfg {String} html content of element
1576 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1577 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1578 * @cfg {String} header content of header (for panel)
1579 * @cfg {String} footer content of footer (for panel)
1580 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1581 * @cfg {String} tag (header|aside|section) type of HTML tag.
1582 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1583 * @cfg {String} fa font awesome icon
1584 * @cfg {String} icon (info-sign|check|...) glyphicon name
1585 * @cfg {Boolean} hidden (true|false) hide the element
1586 * @cfg {Boolean} expandable (true|false) default false
1587 * @cfg {Boolean} expanded (true|false) default true
1588 * @cfg {String} rheader contet on the right of header
1589 * @cfg {Boolean} clickable (true|false) default false
1593 * Create a new Container
1594 * @param {Object} config The config object
1597 Roo.bootstrap.Container = function(config){
1598 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1604 * After the panel has been expand
1606 * @param {Roo.bootstrap.Container} this
1611 * After the panel has been collapsed
1613 * @param {Roo.bootstrap.Container} this
1618 * When a element is chick
1619 * @param {Roo.bootstrap.Container} this
1620 * @param {Roo.EventObject} e
1626 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1644 getChildContainer : function() {
1650 if (this.panel.length) {
1651 return this.el.select('.panel-body',true).first();
1658 getAutoCreate : function(){
1661 tag : this.tag || 'div',
1665 if (this.jumbotron) {
1666 cfg.cls = 'jumbotron';
1671 // - this is applied by the parent..
1673 // cfg.cls = this.cls + '';
1676 if (this.sticky.length) {
1678 var bd = Roo.get(document.body);
1679 if (!bd.hasClass('bootstrap-sticky')) {
1680 bd.addClass('bootstrap-sticky');
1681 Roo.select('html',true).setStyle('height', '100%');
1684 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1688 if (this.well.length) {
1689 switch (this.well) {
1692 cfg.cls +=' well well-' +this.well;
1701 cfg.cls += ' hidden';
1705 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1706 cfg.cls +=' alert alert-' + this.alert;
1711 if (this.panel.length) {
1712 cfg.cls += ' panel panel-' + this.panel;
1714 if (this.header.length) {
1718 if(this.expandable){
1720 cfg.cls = cfg.cls + ' expandable';
1724 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1732 cls : 'panel-title',
1733 html : (this.expandable ? ' ' : '') + this.header
1737 cls: 'panel-header-right',
1743 cls : 'panel-heading',
1744 style : this.expandable ? 'cursor: pointer' : '',
1752 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1757 if (this.footer.length) {
1759 cls : 'panel-footer',
1768 body.html = this.html || cfg.html;
1769 // prefix with the icons..
1771 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1774 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1779 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1780 cfg.cls = 'container';
1786 initEvents: function()
1788 if(this.expandable){
1789 var headerEl = this.headerEl();
1792 headerEl.on('click', this.onToggleClick, this);
1797 this.el.on('click', this.onClick, this);
1802 onToggleClick : function()
1804 var headerEl = this.headerEl();
1820 if(this.fireEvent('expand', this)) {
1822 this.expanded = true;
1824 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1826 this.el.select('.panel-body',true).first().removeClass('hide');
1828 var toggleEl = this.toggleEl();
1834 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1839 collapse : function()
1841 if(this.fireEvent('collapse', this)) {
1843 this.expanded = false;
1845 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1846 this.el.select('.panel-body',true).first().addClass('hide');
1848 var toggleEl = this.toggleEl();
1854 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1858 toggleEl : function()
1860 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1864 return this.el.select('.panel-heading .fa',true).first();
1867 headerEl : function()
1869 if(!this.el || !this.panel.length || !this.header.length){
1873 return this.el.select('.panel-heading',true).first()
1878 if(!this.el || !this.panel.length){
1882 return this.el.select('.panel-body',true).first()
1885 titleEl : function()
1887 if(!this.el || !this.panel.length || !this.header.length){
1891 return this.el.select('.panel-title',true).first();
1894 setTitle : function(v)
1896 var titleEl = this.titleEl();
1902 titleEl.dom.innerHTML = v;
1905 getTitle : function()
1908 var titleEl = this.titleEl();
1914 return titleEl.dom.innerHTML;
1917 setRightTitle : function(v)
1919 var t = this.el.select('.panel-header-right',true).first();
1925 t.dom.innerHTML = v;
1928 onClick : function(e)
1932 this.fireEvent('click', this, e);
1939 * This is BS4's Card element.. - similar to our containers probably..
1943 * @class Roo.bootstrap.Card
1944 * @extends Roo.bootstrap.Component
1945 * Bootstrap Card class
1948 * possible... may not be implemented..
1949 * @cfg {String} header_image src url of image.
1950 * @cfg {String|Object} header
1951 * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1953 * @cfg {String} title
1954 * @cfg {String} subtitle
1955 * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1956 * @cfg {String} footer
1958 * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1960 * @cfg {String} margin (0|1|2|3|4|5|auto)
1961 * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1962 * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1963 * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1964 * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1965 * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1966 * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1968 * @cfg {String} padding (0|1|2|3|4|5)
1969 * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1970 * @cfg {String} padding_bottom (0|1|2|3|4|5)
1971 * @cfg {String} padding_left (0|1|2|3|4|5)
1972 * @cfg {String} padding_right (0|1|2|3|4|5)
1973 * @cfg {String} padding_x (0|1|2|3|4|5)
1974 * @cfg {String} padding_y (0|1|2|3|4|5)
1976 * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1977 * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1978 * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1979 * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1980 * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1982 * @config {Boolean} dragable if this card can be dragged.
1983 * @config {String} drag_group group for drag
1984 * @config {Boolean} dropable if this card can recieve other cards being dropped onto it..
1985 * @config {String} drop_group group for drag
1987 * @config {Boolean} collapsable can the body be collapsed.
1988 * @config {Boolean} collapsed is the body collapsed when rendered...
1989 * @config {Boolean} rotateable can the body be rotated by clicking on it..
1990 * @config {Boolean} rotated is the body rotated when rendered...
1993 * Create a new Container
1994 * @param {Object} config The config object
1997 Roo.bootstrap.Card = function(config){
1998 Roo.bootstrap.Card.superclass.constructor.call(this, config);
2004 * When a element a card is dropped
2005 * @param {Roo.bootstrap.Card} this
2008 * @param {Roo.bootstrap.Card} move_card the card being dropped?
2009 * @param {String} position 'above' or 'below'
2010 * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2016 * When a element a card is rotate
2017 * @param {Roo.bootstrap.Element} this
2018 * @param {Roo.Element} n the node being dropped?
2019 * @param {Boolean} rotate status
2027 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component, {
2032 margin: '', /// may be better in component?
2062 collapsable : false,
2071 childContainer : false,
2072 dropEl : false, /// the dom placeholde element that indicates drop location.
2073 containerEl: false, // body container
2074 bodyEl: false, // card-body
2075 headerContainerEl : false, //
2078 layoutCls : function()
2082 Roo.log(this.margin_bottom.length);
2083 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2084 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2086 if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2087 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
2089 if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2090 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
2094 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2095 if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2096 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['margin' + (v.length ? '_' : '') + v]
2100 // more generic support?
2108 // Roo.log("Call onRender: " + this.xtype);
2109 /* We are looking at something like this.
2111 <img src="..." class="card-img-top" alt="...">
2112 <div class="card-body">
2113 <h5 class="card-title">Card title</h5>
2114 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2116 >> this bit is really the body...
2117 <div> << we will ad dthis in hopefully it will not break shit.
2119 ** card text does not actually have any styling...
2121 <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>
2124 <a href="#" class="card-link">Card link</a>
2127 <div class="card-footer">
2128 <small class="text-muted">Last updated 3 mins ago</small>
2132 getAutoCreate : function(){
2140 if (this.weight.length && this.weight != 'light') {
2141 cfg.cls += ' text-white';
2143 cfg.cls += ' text-dark'; // need as it's nested..
2145 if (this.weight.length) {
2146 cfg.cls += ' bg-' + this.weight;
2149 cfg.cls += this.layoutCls();
2152 var hdr_ctr = false;
2153 if (this.header.length) {
2155 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2156 cls : 'card-header',
2164 cls : 'card-header d-none',
2170 if (this.collapsable) {
2173 cls : 'd-block user-select-none',
2177 cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2182 hdr.cn.push(hdr_ctr);
2187 cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2192 if (this.header_image.length) {
2195 cls : 'card-img-top',
2196 src: this.header_image // escape?
2201 cls : 'card-img-top d-none'
2207 cls : 'card-body' + (this.html === false ? ' d-none' : ''),
2211 if (this.collapsable || this.rotateable) {
2214 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2221 if (this.title.length) {
2225 src: this.title // escape?
2229 if (this.subtitle.length) {
2233 src: this.subtitle // escape?
2239 cls : 'roo-card-body-ctr'
2242 if (this.html.length) {
2248 // fixme ? handle objects?
2250 if (this.footer.length) {
2253 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2258 cfg.cn.push({cls : 'card-footer d-none'});
2267 getCardHeader : function()
2269 var ret = this.el.select('.card-header',true).first();
2270 if (ret.hasClass('d-none')) {
2271 ret.removeClass('d-none');
2276 getCardFooter : function()
2278 var ret = this.el.select('.card-footer',true).first();
2279 if (ret.hasClass('d-none')) {
2280 ret.removeClass('d-none');
2285 getCardImageTop : function()
2287 var ret = this.el.select('.card-img-top',true).first();
2288 if (ret.hasClass('d-none')) {
2289 ret.removeClass('d-none');
2295 getChildContainer : function()
2301 return this.el.select('.roo-card-body-ctr',true).first();
2304 initEvents: function()
2306 this.bodyEl = this.el.select('.card-body',true).first();
2307 this.containerEl = this.getChildContainer();
2309 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2310 containerScroll: true,
2311 ddGroup: this.drag_group || 'default_card_drag_group'
2313 this.dragZone.getDragData = this.getDragData.createDelegate(this);
2315 if (this.dropable) {
2316 this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2317 containerScroll: true,
2318 ddGroup: this.drop_group || 'default_card_drag_group'
2320 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2321 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2322 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2323 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2324 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2327 if (this.collapsable) {
2328 this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2330 if (this.rotateable) {
2331 this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2333 this.collapsableEl = this.el.select('.roo-collapsable').first();
2335 this.footerEl = this.el.select('.card-footer').first();
2336 this.collapsableToggleEl = this.el.select('.roo-collapse-toggle');
2337 this.headerContainerEl = this.el.select('.roo-card-header-ctr').first();
2338 this.headerEl = this.el.select('.card-header',true).first();
2341 this.el.addClass('roo-card-rotated');
2342 this.fireEvent('rotate', this, true);
2346 getDragData : function(e)
2348 var target = this.getEl();
2350 //this.handleSelection(e);
2355 nodes: this.getEl(),
2360 dragData.ddel = target.dom ; // the div element
2361 Roo.log(target.getWidth( ));
2362 dragData.ddel.style.width = target.getWidth() + 'px';
2369 * Part of the Roo.dd.DropZone interface. If no target node is found, the
2370 * whole Element becomes the target, and this causes the drop gesture to append.
2372 getTargetFromEvent : function(e, dragged_card_el)
2374 var target = e.getTarget();
2375 while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2376 target = target.parentNode;
2387 //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2388 // see if target is one of the 'cards'...
2391 //Roo.log(this.items.length);
2394 var last_card_n = 0;
2396 for (var i = 0;i< this.items.length;i++) {
2398 if (!this.items[i].el.hasClass('card')) {
2401 pos = this.getDropPoint(e, this.items[i].el.dom);
2403 cards_len = ret.cards.length;
2404 //Roo.log(this.items[i].el.dom.id);
2405 ret.cards.push(this.items[i]);
2407 if (ret.card_n < 0 && pos == 'above') {
2408 ret.position = cards_len > 0 ? 'below' : pos;
2409 ret.items_n = i > 0 ? i - 1 : 0;
2410 ret.card_n = cards_len > 0 ? cards_len - 1 : 0;
2411 ret.card = ret.cards[ret.card_n];
2414 if (!ret.cards.length) {
2416 ret.position = 'below';
2420 // could not find a card.. stick it at the end..
2421 if (ret.card_n < 0) {
2422 ret.card_n = last_card_n;
2423 ret.card = ret.cards[last_card_n];
2424 ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2425 ret.position = 'below';
2428 if (this.items[ret.items_n].el == dragged_card_el) {
2432 if (ret.position == 'below') {
2433 var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2435 if (card_after && card_after.el == dragged_card_el) {
2442 var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2444 if (card_before && card_before.el == dragged_card_el) {
2451 onNodeEnter : function(n, dd, e, data){
2454 onNodeOver : function(n, dd, e, data)
2457 var target_info = this.getTargetFromEvent(e,data.source.el);
2458 if (target_info === false) {
2459 this.dropPlaceHolder('hide');
2462 Roo.log(['getTargetFromEvent', target_info ]);
2465 this.dropPlaceHolder('show', target_info,data);
2469 onNodeOut : function(n, dd, e, data){
2470 this.dropPlaceHolder('hide');
2473 onNodeDrop : function(n, dd, e, data)
2476 // call drop - return false if
2478 // this could actually fail - if the Network drops..
2479 // we will ignore this at present..- client should probably reload
2480 // the whole set of cards if stuff like that fails.
2483 var info = this.getTargetFromEvent(e,data.source.el);
2484 if (info === false) {
2487 this.dropPlaceHolder('hide');
2493 this.acceptCard(data.source, info.position, info.card, info.items_n);
2497 firstChildCard : function()
2499 for (var i = 0;i< this.items.length;i++) {
2501 if (!this.items[i].el.hasClass('card')) {
2504 return this.items[i];
2506 return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2511 * - card.acceptCard(move_card, info.position, info.card, info.items_n);
2513 acceptCard : function(move_card, position, next_to_card )
2515 if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2519 var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2522 var dom = move_card.el.dom;
2523 dom.parentNode.removeChild(dom);
2524 dom.style.width = ''; // clear with - which is set by drag.
2526 if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2527 var cardel = next_to_card.el.dom;
2529 if (position == 'above' ) {
2530 cardel.parentNode.insertBefore(dom, cardel);
2531 } else if (cardel.nextSibling) {
2532 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2534 cardel.parentNode.append(dom);
2537 // card container???
2538 this.containerEl.dom.append(dom);
2541 //FIXME HANDLE card = true
2543 // add this to the correct place in items.
2547 // remove Card from items.
2549 var old_parent = move_card.parent();
2551 old_parent.items = old_parent.items.filter(function(e) { return e != move_card });
2553 if (this.items.length) {
2555 //Roo.log([info.items_n, info.position, this.items.length]);
2556 for (var i =0; i < this.items.length; i++) {
2557 if (i == to_items_n && position == 'above') {
2558 nitems.push(move_card);
2560 nitems.push(this.items[i]);
2561 if (i == to_items_n && position == 'below') {
2562 nitems.push(move_card);
2565 this.items = nitems;
2566 Roo.log(this.items);
2568 this.items.push(move_card);
2571 move_card.parentId = this.id;
2579 /** Decide whether to drop above or below a View node. */
2580 getDropPoint : function(e, n, dd)
2585 if (n == this.containerEl.dom) {
2588 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2589 var c = t + (b - t) / 2;
2590 var y = Roo.lib.Event.getPageY(e);
2597 onToggleCollapse : function(e)
2599 if (this.collapsed) {
2600 this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2601 this.collapsableEl.addClass('show');
2602 this.collapsed = false;
2605 this.el.select('.roo-collapse-toggle').addClass('collapsed');
2606 this.collapsableEl.removeClass('show');
2607 this.collapsed = true;
2612 onToggleRotate : function(e)
2614 this.collapsableEl.removeClass('show');
2615 this.footerEl.removeClass('d-none');
2616 this.el.removeClass('roo-card-rotated');
2617 this.el.removeClass('d-none');
2620 this.collapsableEl.addClass('show');
2621 this.rotated = false;
2622 this.fireEvent('rotate', this, this.rotated);
2625 this.el.addClass('roo-card-rotated');
2626 this.footerEl.addClass('d-none');
2627 this.el.select('.roo-collapsable').removeClass('show');
2629 this.rotated = true;
2630 this.fireEvent('rotate', this, this.rotated);
2634 dropPlaceHolder: function (action, info, data)
2636 if (this.dropEl === false) {
2637 this.dropEl = Roo.DomHelper.append(this.containerEl, {
2641 this.dropEl.removeClass(['d-none', 'd-block']);
2642 if (action == 'hide') {
2644 this.dropEl.addClass('d-none');
2647 // FIXME - info.card == true!!!
2648 this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2650 if (info.card !== true) {
2651 var cardel = info.card.el.dom;
2653 if (info.position == 'above') {
2654 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2655 } else if (cardel.nextSibling) {
2656 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2658 cardel.parentNode.append(this.dropEl.dom);
2661 // card container???
2662 this.containerEl.dom.append(this.dropEl.dom);
2665 this.dropEl.addClass('d-block roo-card-dropzone');
2667 this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2674 setHeaderText: function(html)
2676 this.headerContainerEl.dom.innerHTML = html;
2685 * Card header - holder for the card header elements.
2690 * @class Roo.bootstrap.CardHeader
2691 * @extends Roo.bootstrap.Element
2692 * Bootstrap CardHeader class
2694 * Create a new Card Header - that you can embed children into
2695 * @param {Object} config The config object
2698 Roo.bootstrap.CardHeader = function(config){
2699 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2702 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2705 container_method : 'getCardHeader'
2718 * Card footer - holder for the card footer elements.
2723 * @class Roo.bootstrap.CardFooter
2724 * @extends Roo.bootstrap.Element
2725 * Bootstrap CardFooter class
2727 * Create a new Card Footer - that you can embed children into
2728 * @param {Object} config The config object
2731 Roo.bootstrap.CardFooter = function(config){
2732 Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2735 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element, {
2738 container_method : 'getCardFooter'
2751 * Card header - holder for the card header elements.
2756 * @class Roo.bootstrap.CardImageTop
2757 * @extends Roo.bootstrap.Element
2758 * Bootstrap CardImageTop class
2760 * Create a new Card Image Top container
2761 * @param {Object} config The config object
2764 Roo.bootstrap.CardImageTop = function(config){
2765 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2768 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2771 container_method : 'getCardImageTop'
2789 * @class Roo.bootstrap.Img
2790 * @extends Roo.bootstrap.Component
2791 * Bootstrap Img class
2792 * @cfg {Boolean} imgResponsive false | true
2793 * @cfg {String} border rounded | circle | thumbnail
2794 * @cfg {String} src image source
2795 * @cfg {String} alt image alternative text
2796 * @cfg {String} href a tag href
2797 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2798 * @cfg {String} xsUrl xs image source
2799 * @cfg {String} smUrl sm image source
2800 * @cfg {String} mdUrl md image source
2801 * @cfg {String} lgUrl lg image source
2804 * Create a new Input
2805 * @param {Object} config The config object
2808 Roo.bootstrap.Img = function(config){
2809 Roo.bootstrap.Img.superclass.constructor.call(this, config);
2815 * The img click event for the img.
2816 * @param {Roo.EventObject} e
2822 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
2824 imgResponsive: true,
2834 getAutoCreate : function()
2836 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2837 return this.createSingleImg();
2842 cls: 'roo-image-responsive-group',
2847 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2849 if(!_this[size + 'Url']){
2855 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2856 html: _this.html || cfg.html,
2857 src: _this[size + 'Url']
2860 img.cls += ' roo-image-responsive-' + size;
2862 var s = ['xs', 'sm', 'md', 'lg'];
2864 s.splice(s.indexOf(size), 1);
2866 Roo.each(s, function(ss){
2867 img.cls += ' hidden-' + ss;
2870 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2871 cfg.cls += ' img-' + _this.border;
2875 cfg.alt = _this.alt;
2888 a.target = _this.target;
2892 cfg.cn.push((_this.href) ? a : img);
2899 createSingleImg : function()
2903 cls: (this.imgResponsive) ? 'img-responsive' : '',
2905 src : 'about:blank' // just incase src get's set to undefined?!?
2908 cfg.html = this.html || cfg.html;
2910 cfg.src = this.src || cfg.src;
2912 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2913 cfg.cls += ' img-' + this.border;
2930 a.target = this.target;
2935 return (this.href) ? a : cfg;
2938 initEvents: function()
2941 this.el.on('click', this.onClick, this);
2946 onClick : function(e)
2948 Roo.log('img onclick');
2949 this.fireEvent('click', this, e);
2952 * Sets the url of the image - used to update it
2953 * @param {String} url the url of the image
2956 setSrc : function(url)
2960 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2961 this.el.dom.src = url;
2965 this.el.select('img', true).first().dom.src = url;
2981 * @class Roo.bootstrap.Link
2982 * @extends Roo.bootstrap.Component
2983 * Bootstrap Link Class
2984 * @cfg {String} alt image alternative text
2985 * @cfg {String} href a tag href
2986 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
2987 * @cfg {String} html the content of the link.
2988 * @cfg {String} anchor name for the anchor link
2989 * @cfg {String} fa - favicon
2991 * @cfg {Boolean} preventDefault (true | false) default false
2995 * Create a new Input
2996 * @param {Object} config The config object
2999 Roo.bootstrap.Link = function(config){
3000 Roo.bootstrap.Link.superclass.constructor.call(this, config);
3006 * The img click event for the img.
3007 * @param {Roo.EventObject} e
3013 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
3017 preventDefault: false,
3023 getAutoCreate : function()
3025 var html = this.html || '';
3027 if (this.fa !== false) {
3028 html = '<i class="fa fa-' + this.fa + '"></i>';
3033 // anchor's do not require html/href...
3034 if (this.anchor === false) {
3036 cfg.href = this.href || '#';
3038 cfg.name = this.anchor;
3039 if (this.html !== false || this.fa !== false) {
3042 if (this.href !== false) {
3043 cfg.href = this.href;
3047 if(this.alt !== false){
3052 if(this.target !== false) {
3053 cfg.target = this.target;
3059 initEvents: function() {
3061 if(!this.href || this.preventDefault){
3062 this.el.on('click', this.onClick, this);
3066 onClick : function(e)
3068 if(this.preventDefault){
3071 //Roo.log('img onclick');
3072 this.fireEvent('click', this, e);
3085 * @class Roo.bootstrap.Header
3086 * @extends Roo.bootstrap.Component
3087 * Bootstrap Header class
3088 * @cfg {String} html content of header
3089 * @cfg {Number} level (1|2|3|4|5|6) default 1
3092 * Create a new Header
3093 * @param {Object} config The config object
3097 Roo.bootstrap.Header = function(config){
3098 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3101 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3109 getAutoCreate : function(){
3114 tag: 'h' + (1 *this.level),
3115 html: this.html || ''
3127 * Ext JS Library 1.1.1
3128 * Copyright(c) 2006-2007, Ext JS, LLC.
3130 * Originally Released Under LGPL - original licence link has changed is not relivant.
3133 * <script type="text/javascript">
3137 * @class Roo.bootstrap.MenuMgr
3138 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3141 Roo.bootstrap.MenuMgr = function(){
3142 var menus, active, groups = {}, attached = false, lastShow = new Date();
3144 // private - called when first menu is created
3147 active = new Roo.util.MixedCollection();
3148 Roo.get(document).addKeyListener(27, function(){
3149 if(active.length > 0){
3157 if(active && active.length > 0){
3158 var c = active.clone();
3168 if(active.length < 1){
3169 Roo.get(document).un("mouseup", onMouseDown);
3177 var last = active.last();
3178 lastShow = new Date();
3181 Roo.get(document).on("mouseup", onMouseDown);
3186 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3187 m.parentMenu.activeChild = m;
3188 }else if(last && last.isVisible()){
3189 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3194 function onBeforeHide(m){
3196 m.activeChild.hide();
3198 if(m.autoHideTimer){
3199 clearTimeout(m.autoHideTimer);
3200 delete m.autoHideTimer;
3205 function onBeforeShow(m){
3206 var pm = m.parentMenu;
3207 if(!pm && !m.allowOtherMenus){
3209 }else if(pm && pm.activeChild && active != m){
3210 pm.activeChild.hide();
3214 // private this should really trigger on mouseup..
3215 function onMouseDown(e){
3216 Roo.log("on Mouse Up");
3218 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3219 Roo.log("MenuManager hideAll");
3228 function onBeforeCheck(mi, state){
3230 var g = groups[mi.group];
3231 for(var i = 0, l = g.length; i < l; i++){
3233 g[i].setChecked(false);
3242 * Hides all menus that are currently visible
3244 hideAll : function(){
3249 register : function(menu){
3253 menus[menu.id] = menu;
3254 menu.on("beforehide", onBeforeHide);
3255 menu.on("hide", onHide);
3256 menu.on("beforeshow", onBeforeShow);
3257 menu.on("show", onShow);
3259 if(g && menu.events["checkchange"]){
3263 groups[g].push(menu);
3264 menu.on("checkchange", onCheck);
3269 * Returns a {@link Roo.menu.Menu} object
3270 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3271 * be used to generate and return a new Menu instance.
3273 get : function(menu){
3274 if(typeof menu == "string"){ // menu id
3276 }else if(menu.events){ // menu instance
3279 /*else if(typeof menu.length == 'number'){ // array of menu items?
3280 return new Roo.bootstrap.Menu({items:menu});
3281 }else{ // otherwise, must be a config
3282 return new Roo.bootstrap.Menu(menu);
3289 unregister : function(menu){
3290 delete menus[menu.id];
3291 menu.un("beforehide", onBeforeHide);
3292 menu.un("hide", onHide);
3293 menu.un("beforeshow", onBeforeShow);
3294 menu.un("show", onShow);
3296 if(g && menu.events["checkchange"]){
3297 groups[g].remove(menu);
3298 menu.un("checkchange", onCheck);
3303 registerCheckable : function(menuItem){
3304 var g = menuItem.group;
3309 groups[g].push(menuItem);
3310 menuItem.on("beforecheckchange", onBeforeCheck);
3315 unregisterCheckable : function(menuItem){
3316 var g = menuItem.group;
3318 groups[g].remove(menuItem);
3319 menuItem.un("beforecheckchange", onBeforeCheck);
3331 * @class Roo.bootstrap.Menu
3332 * @extends Roo.bootstrap.Component
3333 * Bootstrap Menu class - container for MenuItems
3334 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3335 * @cfg {bool} hidden if the menu should be hidden when rendered.
3336 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3337 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3341 * @param {Object} config The config object
3345 Roo.bootstrap.Menu = function(config){
3346 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3347 if (this.registerMenu && this.type != 'treeview') {
3348 Roo.bootstrap.MenuMgr.register(this);
3355 * Fires before this menu is displayed (return false to block)
3356 * @param {Roo.menu.Menu} this
3361 * Fires before this menu is hidden (return false to block)
3362 * @param {Roo.menu.Menu} this
3367 * Fires after this menu is displayed
3368 * @param {Roo.menu.Menu} this
3373 * Fires after this menu is hidden
3374 * @param {Roo.menu.Menu} this
3379 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3380 * @param {Roo.menu.Menu} this
3381 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3382 * @param {Roo.EventObject} e
3387 * Fires when the mouse is hovering over this menu
3388 * @param {Roo.menu.Menu} this
3389 * @param {Roo.EventObject} e
3390 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3395 * Fires when the mouse exits this menu
3396 * @param {Roo.menu.Menu} this
3397 * @param {Roo.EventObject} e
3398 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3403 * Fires when a menu item contained in this menu is clicked
3404 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3405 * @param {Roo.EventObject} e
3409 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3412 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
3416 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3419 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3421 registerMenu : true,
3423 menuItems :false, // stores the menu items..
3433 getChildContainer : function() {
3437 getAutoCreate : function(){
3439 //if (['right'].indexOf(this.align)!==-1) {
3440 // cfg.cn[1].cls += ' pull-right'
3446 cls : 'dropdown-menu' ,
3447 style : 'z-index:1000'
3451 if (this.type === 'submenu') {
3452 cfg.cls = 'submenu active';
3454 if (this.type === 'treeview') {
3455 cfg.cls = 'treeview-menu';
3460 initEvents : function() {
3462 // Roo.log("ADD event");
3463 // Roo.log(this.triggerEl.dom);
3465 this.triggerEl.on('click', this.onTriggerClick, this);
3467 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3470 if (this.triggerEl.hasClass('nav-item')) {
3471 // dropdown toggle on the 'a' in BS4?
3472 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3474 this.triggerEl.addClass('dropdown-toggle');
3477 this.el.on('touchstart' , this.onTouch, this);
3479 this.el.on('click' , this.onClick, this);
3481 this.el.on("mouseover", this.onMouseOver, this);
3482 this.el.on("mouseout", this.onMouseOut, this);
3486 findTargetItem : function(e)
3488 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3492 //Roo.log(t); Roo.log(t.id);
3494 //Roo.log(this.menuitems);
3495 return this.menuitems.get(t.id);
3497 //return this.items.get(t.menuItemId);
3503 onTouch : function(e)
3505 Roo.log("menu.onTouch");
3506 //e.stopEvent(); this make the user popdown broken
3510 onClick : function(e)
3512 Roo.log("menu.onClick");
3514 var t = this.findTargetItem(e);
3515 if(!t || t.isContainer){
3520 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3521 if(t == this.activeItem && t.shouldDeactivate(e)){
3522 this.activeItem.deactivate();
3523 delete this.activeItem;
3527 this.setActiveItem(t, true);
3535 Roo.log('pass click event');
3539 this.fireEvent("click", this, t, e);
3543 if(!t.href.length || t.href == '#'){
3544 (function() { _this.hide(); }).defer(100);
3549 onMouseOver : function(e){
3550 var t = this.findTargetItem(e);
3553 // if(t.canActivate && !t.disabled){
3554 // this.setActiveItem(t, true);
3558 this.fireEvent("mouseover", this, e, t);
3560 isVisible : function(){
3561 return !this.hidden;
3563 onMouseOut : function(e){
3564 var t = this.findTargetItem(e);
3567 // if(t == this.activeItem && t.shouldDeactivate(e)){
3568 // this.activeItem.deactivate();
3569 // delete this.activeItem;
3572 this.fireEvent("mouseout", this, e, t);
3577 * Displays this menu relative to another element
3578 * @param {String/HTMLElement/Roo.Element} element The element to align to
3579 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3580 * the element (defaults to this.defaultAlign)
3581 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3583 show : function(el, pos, parentMenu)
3585 if (false === this.fireEvent("beforeshow", this)) {
3586 Roo.log("show canceled");
3589 this.parentMenu = parentMenu;
3594 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3597 * Displays this menu at a specific xy position
3598 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3599 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3601 showAt : function(xy, parentMenu, /* private: */_e){
3602 this.parentMenu = parentMenu;
3607 this.fireEvent("beforeshow", this);
3608 //xy = this.el.adjustForConstraints(xy);
3612 this.hideMenuItems();
3613 this.hidden = false;
3614 this.triggerEl.addClass('open');
3615 this.el.addClass('show');
3617 // reassign x when hitting right
3618 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3619 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3622 // reassign y when hitting bottom
3623 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3624 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3627 // but the list may align on trigger left or trigger top... should it be a properity?
3629 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3634 this.fireEvent("show", this);
3640 this.doFocus.defer(50, this);
3644 doFocus : function(){
3646 this.focusEl.focus();
3651 * Hides this menu and optionally all parent menus
3652 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3654 hide : function(deep)
3656 if (false === this.fireEvent("beforehide", this)) {
3657 Roo.log("hide canceled");
3660 this.hideMenuItems();
3661 if(this.el && this.isVisible()){
3663 if(this.activeItem){
3664 this.activeItem.deactivate();
3665 this.activeItem = null;
3667 this.triggerEl.removeClass('open');;
3668 this.el.removeClass('show');
3670 this.fireEvent("hide", this);
3672 if(deep === true && this.parentMenu){
3673 this.parentMenu.hide(true);
3677 onTriggerClick : function(e)
3679 Roo.log('trigger click');
3681 var target = e.getTarget();
3683 Roo.log(target.nodeName.toLowerCase());
3685 if(target.nodeName.toLowerCase() === 'i'){
3691 onTriggerPress : function(e)
3693 Roo.log('trigger press');
3694 //Roo.log(e.getTarget());
3695 // Roo.log(this.triggerEl.dom);
3697 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3698 var pel = Roo.get(e.getTarget());
3699 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3700 Roo.log('is treeview or dropdown?');
3704 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3708 if (this.isVisible()) {
3713 this.show(this.triggerEl, '?', false);
3716 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3723 hideMenuItems : function()
3725 Roo.log("hide Menu Items");
3730 this.el.select('.open',true).each(function(aa) {
3732 aa.removeClass('open');
3736 addxtypeChild : function (tree, cntr) {
3737 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3739 this.menuitems.add(comp);
3751 this.getEl().dom.innerHTML = '';
3752 this.menuitems.clear();
3766 * @class Roo.bootstrap.MenuItem
3767 * @extends Roo.bootstrap.Component
3768 * Bootstrap MenuItem class
3769 * @cfg {String} html the menu label
3770 * @cfg {String} href the link
3771 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3772 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3773 * @cfg {Boolean} active used on sidebars to highlight active itesm
3774 * @cfg {String} fa favicon to show on left of menu item.
3775 * @cfg {Roo.bootsrap.Menu} menu the child menu.
3779 * Create a new MenuItem
3780 * @param {Object} config The config object
3784 Roo.bootstrap.MenuItem = function(config){
3785 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3790 * The raw click event for the entire grid.
3791 * @param {Roo.bootstrap.MenuItem} this
3792 * @param {Roo.EventObject} e
3798 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
3802 preventDefault: false,
3803 isContainer : false,
3807 getAutoCreate : function(){
3809 if(this.isContainer){
3812 cls: 'dropdown-menu-item '
3822 cls : 'dropdown-item',
3827 if (this.fa !== false) {
3830 cls : 'fa fa-' + this.fa
3839 cls: 'dropdown-menu-item',
3842 if (this.parent().type == 'treeview') {
3843 cfg.cls = 'treeview-menu';
3846 cfg.cls += ' active';
3851 anc.href = this.href || cfg.cn[0].href ;
3852 ctag.html = this.html || cfg.cn[0].html ;
3856 initEvents: function()
3858 if (this.parent().type == 'treeview') {
3859 this.el.select('a').on('click', this.onClick, this);
3863 this.menu.parentType = this.xtype;
3864 this.menu.triggerEl = this.el;
3865 this.menu = this.addxtype(Roo.apply({}, this.menu));
3869 onClick : function(e)
3871 Roo.log('item on click ');
3873 if(this.preventDefault){
3876 //this.parent().hideMenuItems();
3878 this.fireEvent('click', this, e);
3897 * @class Roo.bootstrap.MenuSeparator
3898 * @extends Roo.bootstrap.Component
3899 * Bootstrap MenuSeparator class
3902 * Create a new MenuItem
3903 * @param {Object} config The config object
3907 Roo.bootstrap.MenuSeparator = function(config){
3908 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3911 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
3913 getAutoCreate : function(){
3932 * @class Roo.bootstrap.Modal
3933 * @extends Roo.bootstrap.Component
3934 * Bootstrap Modal class
3935 * @cfg {String} title Title of dialog
3936 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3937 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
3938 * @cfg {Boolean} specificTitle default false
3939 * @cfg {Array} buttons Array of buttons or standard button set..
3940 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3941 * @cfg {Boolean} animate default true
3942 * @cfg {Boolean} allow_close default true
3943 * @cfg {Boolean} fitwindow default false
3944 * @cfg {Number} width fixed width - usefull for chrome extension only really.
3945 * @cfg {Number} height fixed height - usefull for chrome extension only really.
3946 * @cfg {String} size (sm|lg|xl) default empty
3947 * @cfg {Number} max_width set the max width of modal
3948 * @cfg {Boolean} editableTitle can the title be edited
3953 * Create a new Modal Dialog
3954 * @param {Object} config The config object
3957 Roo.bootstrap.Modal = function(config){
3958 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3963 * The raw btnclick event for the button
3964 * @param {Roo.EventObject} e
3969 * Fire when dialog resize
3970 * @param {Roo.bootstrap.Modal} this
3971 * @param {Roo.EventObject} e
3975 * @event titlechanged
3976 * Fire when the editable title has been changed
3977 * @param {Roo.bootstrap.Modal} this
3978 * @param {Roo.EventObject} value
3980 "titlechanged" : true
3983 this.buttons = this.buttons || [];
3986 this.tmpl = Roo.factory(this.tmpl);
3991 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
3993 title : 'test dialog',
4003 specificTitle: false,
4005 buttonPosition: 'right',
4027 editableTitle : false,
4029 onRender : function(ct, position)
4031 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4034 var cfg = Roo.apply({}, this.getAutoCreate());
4037 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4039 //if (!cfg.name.length) {
4043 cfg.cls += ' ' + this.cls;
4046 cfg.style = this.style;
4048 this.el = Roo.get(document.body).createChild(cfg, position);
4050 //var type = this.el.dom.type;
4053 if(this.tabIndex !== undefined){
4054 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4057 this.dialogEl = this.el.select('.modal-dialog',true).first();
4058 this.bodyEl = this.el.select('.modal-body',true).first();
4059 this.closeEl = this.el.select('.modal-header .close', true).first();
4060 this.headerEl = this.el.select('.modal-header',true).first();
4061 this.titleEl = this.el.select('.modal-title',true).first();
4062 this.footerEl = this.el.select('.modal-footer',true).first();
4064 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4066 //this.el.addClass("x-dlg-modal");
4068 if (this.buttons.length) {
4069 Roo.each(this.buttons, function(bb) {
4070 var b = Roo.apply({}, bb);
4071 b.xns = b.xns || Roo.bootstrap;
4072 b.xtype = b.xtype || 'Button';
4073 if (typeof(b.listeners) == 'undefined') {
4074 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4077 var btn = Roo.factory(b);
4079 btn.render(this.getButtonContainer());
4083 // render the children.
4086 if(typeof(this.items) != 'undefined'){
4087 var items = this.items;
4090 for(var i =0;i < items.length;i++) {
4091 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4095 this.items = nitems;
4097 // where are these used - they used to be body/close/footer
4101 //this.el.addClass([this.fieldClass, this.cls]);
4105 getAutoCreate : function()
4107 // we will default to modal-body-overflow - might need to remove or make optional later.
4109 cls : 'modal-body enable-modal-body-overflow ',
4110 html : this.html || ''
4115 cls : 'modal-title',
4119 if(this.specificTitle){ // WTF is this?
4124 if (this.allow_close && Roo.bootstrap.version == 3) {
4134 if (this.editableTitle) {
4136 cls: 'form-control roo-editable-title d-none',
4142 if (this.allow_close && Roo.bootstrap.version == 4) {
4152 if(this.size.length){
4153 size = 'modal-' + this.size;
4156 var footer = Roo.bootstrap.version == 3 ?
4158 cls : 'modal-footer',
4162 cls: 'btn-' + this.buttonPosition
4167 { // BS4 uses mr-auto on left buttons....
4168 cls : 'modal-footer'
4179 cls: "modal-dialog " + size,
4182 cls : "modal-content",
4185 cls : 'modal-header',
4200 modal.cls += ' fade';
4206 getChildContainer : function() {
4211 getButtonContainer : function() {
4213 return Roo.bootstrap.version == 4 ?
4214 this.el.select('.modal-footer',true).first()
4215 : this.el.select('.modal-footer div',true).first();
4218 initEvents : function()
4220 if (this.allow_close) {
4221 this.closeEl.on('click', this.hide, this);
4223 Roo.EventManager.onWindowResize(this.resize, this, true);
4224 if (this.editableTitle) {
4225 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4226 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4227 this.headerEditEl.on('keyup', function(e) {
4228 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4229 this.toggleHeaderInput(false)
4232 this.headerEditEl.on('blur', function(e) {
4233 this.toggleHeaderInput(false)
4242 this.maskEl.setSize(
4243 Roo.lib.Dom.getViewWidth(true),
4244 Roo.lib.Dom.getViewHeight(true)
4247 if (this.fitwindow) {
4251 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4252 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4257 if(this.max_width !== 0) {
4259 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4262 this.setSize(w, this.height);
4266 if(this.max_height) {
4267 this.setSize(w,Math.min(
4269 Roo.lib.Dom.getViewportHeight(true) - 60
4275 if(!this.fit_content) {
4276 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4280 this.setSize(w, Math.min(
4282 this.headerEl.getHeight() +
4283 this.footerEl.getHeight() +
4284 this.getChildHeight(this.bodyEl.dom.childNodes),
4285 Roo.lib.Dom.getViewportHeight(true) - 60)
4291 setSize : function(w,h)
4302 if (!this.rendered) {
4305 this.toggleHeaderInput(false);
4306 //this.el.setStyle('display', 'block');
4307 this.el.removeClass('hideing');
4308 this.el.dom.style.display='block';
4310 Roo.get(document.body).addClass('modal-open');
4312 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4315 this.el.addClass('show');
4316 this.el.addClass('in');
4319 this.el.addClass('show');
4320 this.el.addClass('in');
4323 // not sure how we can show data in here..
4325 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4328 Roo.get(document.body).addClass("x-body-masked");
4330 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4331 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4332 this.maskEl.dom.style.display = 'block';
4333 this.maskEl.addClass('show');
4338 this.fireEvent('show', this);
4340 // set zindex here - otherwise it appears to be ignored...
4341 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4344 this.items.forEach( function(e) {
4345 e.layout ? e.layout() : false;
4353 if(this.fireEvent("beforehide", this) !== false){
4355 this.maskEl.removeClass('show');
4357 this.maskEl.dom.style.display = '';
4358 Roo.get(document.body).removeClass("x-body-masked");
4359 this.el.removeClass('in');
4360 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4362 if(this.animate){ // why
4363 this.el.addClass('hideing');
4364 this.el.removeClass('show');
4366 if (!this.el.hasClass('hideing')) {
4367 return; // it's been shown again...
4370 this.el.dom.style.display='';
4372 Roo.get(document.body).removeClass('modal-open');
4373 this.el.removeClass('hideing');
4377 this.el.removeClass('show');
4378 this.el.dom.style.display='';
4379 Roo.get(document.body).removeClass('modal-open');
4382 this.fireEvent('hide', this);
4385 isVisible : function()
4388 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4392 addButton : function(str, cb)
4396 var b = Roo.apply({}, { html : str } );
4397 b.xns = b.xns || Roo.bootstrap;
4398 b.xtype = b.xtype || 'Button';
4399 if (typeof(b.listeners) == 'undefined') {
4400 b.listeners = { click : cb.createDelegate(this) };
4403 var btn = Roo.factory(b);
4405 btn.render(this.getButtonContainer());
4411 setDefaultButton : function(btn)
4413 //this.el.select('.modal-footer').()
4416 resizeTo: function(w,h)
4418 this.dialogEl.setWidth(w);
4420 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4422 this.bodyEl.setHeight(h - diff);
4424 this.fireEvent('resize', this);
4427 setContentSize : function(w, h)
4431 onButtonClick: function(btn,e)
4434 this.fireEvent('btnclick', btn.name, e);
4437 * Set the title of the Dialog
4438 * @param {String} str new Title
4440 setTitle: function(str) {
4441 this.titleEl.dom.innerHTML = str;
4445 * Set the body of the Dialog
4446 * @param {String} str new Title
4448 setBody: function(str) {
4449 this.bodyEl.dom.innerHTML = str;
4452 * Set the body of the Dialog using the template
4453 * @param {Obj} data - apply this data to the template and replace the body contents.
4455 applyBody: function(obj)
4458 Roo.log("Error - using apply Body without a template");
4461 this.tmpl.overwrite(this.bodyEl, obj);
4464 getChildHeight : function(child_nodes)
4468 child_nodes.length == 0
4473 var child_height = 0;
4475 for(var i = 0; i < child_nodes.length; i++) {
4478 * for modal with tabs...
4479 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4481 var layout_childs = child_nodes[i].childNodes;
4483 for(var j = 0; j < layout_childs.length; j++) {
4485 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4487 var layout_body_childs = layout_childs[j].childNodes;
4489 for(var k = 0; k < layout_body_childs.length; k++) {
4491 if(layout_body_childs[k].classList.contains('navbar')) {
4492 child_height += layout_body_childs[k].offsetHeight;
4496 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4498 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4500 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4502 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4503 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4518 child_height += child_nodes[i].offsetHeight;
4519 // Roo.log(child_nodes[i].offsetHeight);
4522 return child_height;
4524 toggleHeaderInput : function(is_edit)
4526 if (!this.editableTitle) {
4527 return; // not editable.
4529 if (is_edit && this.is_header_editing) {
4530 return; // already editing..
4534 this.headerEditEl.dom.value = this.title;
4535 this.headerEditEl.removeClass('d-none');
4536 this.headerEditEl.dom.focus();
4537 this.titleEl.addClass('d-none');
4539 this.is_header_editing = true;
4542 // flip back to not editing.
4543 this.title = this.headerEditEl.dom.value;
4544 this.headerEditEl.addClass('d-none');
4545 this.titleEl.removeClass('d-none');
4546 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4547 this.is_header_editing = false;
4548 this.fireEvent('titlechanged', this, this.title);
4557 Roo.apply(Roo.bootstrap.Modal, {
4559 * Button config that displays a single OK button
4568 * Button config that displays Yes and No buttons
4584 * Button config that displays OK and Cancel buttons
4599 * Button config that displays Yes, No and Cancel buttons
4624 * messagebox - can be used as a replace
4628 * @class Roo.MessageBox
4629 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4633 Roo.Msg.alert('Status', 'Changes saved successfully.');
4635 // Prompt for user data:
4636 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4638 // process text value...
4642 // Show a dialog using config options:
4644 title:'Save Changes?',
4645 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4646 buttons: Roo.Msg.YESNOCANCEL,
4653 Roo.bootstrap.MessageBox = function(){
4654 var dlg, opt, mask, waitTimer;
4655 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4656 var buttons, activeTextEl, bwidth;
4660 var handleButton = function(button){
4662 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4666 var handleHide = function(){
4668 dlg.el.removeClass(opt.cls);
4671 // Roo.TaskMgr.stop(waitTimer);
4672 // waitTimer = null;
4677 var updateButtons = function(b){
4680 buttons["ok"].hide();
4681 buttons["cancel"].hide();
4682 buttons["yes"].hide();
4683 buttons["no"].hide();
4684 dlg.footerEl.hide();
4688 dlg.footerEl.show();
4689 for(var k in buttons){
4690 if(typeof buttons[k] != "function"){
4693 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4694 width += buttons[k].el.getWidth()+15;
4704 var handleEsc = function(d, k, e){
4705 if(opt && opt.closable !== false){
4715 * Returns a reference to the underlying {@link Roo.BasicDialog} element
4716 * @return {Roo.BasicDialog} The BasicDialog element
4718 getDialog : function(){
4720 dlg = new Roo.bootstrap.Modal( {
4723 //constraintoviewport:false,
4725 //collapsible : false,
4730 //buttonAlign:"center",
4731 closeClick : function(){
4732 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4735 handleButton("cancel");
4740 dlg.on("hide", handleHide);
4742 //dlg.addKeyListener(27, handleEsc);
4744 this.buttons = buttons;
4745 var bt = this.buttonText;
4746 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4747 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4748 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4749 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4751 bodyEl = dlg.bodyEl.createChild({
4753 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4754 '<textarea class="roo-mb-textarea"></textarea>' +
4755 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
4757 msgEl = bodyEl.dom.firstChild;
4758 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4759 textboxEl.enableDisplayMode();
4760 textboxEl.addKeyListener([10,13], function(){
4761 if(dlg.isVisible() && opt && opt.buttons){
4764 }else if(opt.buttons.yes){
4765 handleButton("yes");
4769 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4770 textareaEl.enableDisplayMode();
4771 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4772 progressEl.enableDisplayMode();
4774 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4775 var pf = progressEl.dom.firstChild;
4777 pp = Roo.get(pf.firstChild);
4778 pp.setHeight(pf.offsetHeight);
4786 * Updates the message box body text
4787 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4788 * the XHTML-compliant non-breaking space character '&#160;')
4789 * @return {Roo.MessageBox} This message box
4791 updateText : function(text)
4793 if(!dlg.isVisible() && !opt.width){
4794 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4795 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4797 msgEl.innerHTML = text || ' ';
4799 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4800 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4802 Math.min(opt.width || cw , this.maxWidth),
4803 Math.max(opt.minWidth || this.minWidth, bwidth)
4806 activeTextEl.setWidth(w);
4808 if(dlg.isVisible()){
4809 dlg.fixedcenter = false;
4811 // to big, make it scroll. = But as usual stupid IE does not support
4814 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4815 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4816 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4818 bodyEl.dom.style.height = '';
4819 bodyEl.dom.style.overflowY = '';
4822 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4824 bodyEl.dom.style.overflowX = '';
4827 dlg.setContentSize(w, bodyEl.getHeight());
4828 if(dlg.isVisible()){
4829 dlg.fixedcenter = true;
4835 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
4836 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4837 * @param {Number} value Any number between 0 and 1 (e.g., .5)
4838 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4839 * @return {Roo.MessageBox} This message box
4841 updateProgress : function(value, text){
4843 this.updateText(text);
4846 if (pp) { // weird bug on my firefox - for some reason this is not defined
4847 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4848 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4854 * Returns true if the message box is currently displayed
4855 * @return {Boolean} True if the message box is visible, else false
4857 isVisible : function(){
4858 return dlg && dlg.isVisible();
4862 * Hides the message box if it is displayed
4865 if(this.isVisible()){
4871 * Displays a new message box, or reinitializes an existing message box, based on the config options
4872 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4873 * The following config object properties are supported:
4875 Property Type Description
4876 ---------- --------------- ------------------------------------------------------------------------------------
4877 animEl String/Element An id or Element from which the message box should animate as it opens and
4878 closes (defaults to undefined)
4879 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4880 cancel:'Bar'}), or false to not show any buttons (defaults to false)
4881 closable Boolean False to hide the top-right close button (defaults to true). Note that
4882 progress and wait dialogs will ignore this property and always hide the
4883 close button as they can only be closed programmatically.
4884 cls String A custom CSS class to apply to the message box element
4885 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
4886 displayed (defaults to 75)
4887 fn Function A callback function to execute after closing the dialog. The arguments to the
4888 function will be btn (the name of the button that was clicked, if applicable,
4889 e.g. "ok"), and text (the value of the active text field, if applicable).
4890 Progress and wait dialogs will ignore this option since they do not respond to
4891 user actions and can only be closed programmatically, so any required function
4892 should be called by the same code after it closes the dialog.
4893 icon String A CSS class that provides a background image to be used as an icon for
4894 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4895 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
4896 minWidth Number The minimum width in pixels of the message box (defaults to 100)
4897 modal Boolean False to allow user interaction with the page while the message box is
4898 displayed (defaults to true)
4899 msg String A string that will replace the existing message box body text (defaults
4900 to the XHTML-compliant non-breaking space character ' ')
4901 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
4902 progress Boolean True to display a progress bar (defaults to false)
4903 progressText String The text to display inside the progress bar if progress = true (defaults to '')
4904 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
4905 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
4906 title String The title text
4907 value String The string value to set into the active textbox element if displayed
4908 wait Boolean True to display a progress bar (defaults to false)
4909 width Number The width of the dialog in pixels
4916 msg: 'Please enter your address:',
4918 buttons: Roo.MessageBox.OKCANCEL,
4921 animEl: 'addAddressBtn'
4924 * @param {Object} config Configuration options
4925 * @return {Roo.MessageBox} This message box
4927 show : function(options)
4930 // this causes nightmares if you show one dialog after another
4931 // especially on callbacks..
4933 if(this.isVisible()){
4936 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4937 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
4938 Roo.log("New Dialog Message:" + options.msg )
4939 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4940 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4943 var d = this.getDialog();
4945 d.setTitle(opt.title || " ");
4946 d.closeEl.setDisplayed(opt.closable !== false);
4947 activeTextEl = textboxEl;
4948 opt.prompt = opt.prompt || (opt.multiline ? true : false);
4953 textareaEl.setHeight(typeof opt.multiline == "number" ?
4954 opt.multiline : this.defaultTextHeight);
4955 activeTextEl = textareaEl;
4964 progressEl.setDisplayed(opt.progress === true);
4966 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4968 this.updateProgress(0);
4969 activeTextEl.dom.value = opt.value || "";
4971 dlg.setDefaultButton(activeTextEl);
4973 var bs = opt.buttons;
4977 }else if(bs && bs.yes){
4978 db = buttons["yes"];
4980 dlg.setDefaultButton(db);
4982 bwidth = updateButtons(opt.buttons);
4983 this.updateText(opt.msg);
4985 d.el.addClass(opt.cls);
4987 d.proxyDrag = opt.proxyDrag === true;
4988 d.modal = opt.modal !== false;
4989 d.mask = opt.modal !== false ? mask : false;
4991 // force it to the end of the z-index stack so it gets a cursor in FF
4992 document.body.appendChild(dlg.el.dom);
4993 d.animateTarget = null;
4994 d.show(options.animEl);
5000 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5001 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5002 * and closing the message box when the process is complete.
5003 * @param {String} title The title bar text
5004 * @param {String} msg The message box body text
5005 * @return {Roo.MessageBox} This message box
5007 progress : function(title, msg){
5014 minWidth: this.minProgressWidth,
5021 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5022 * If a callback function is passed it will be called after the user clicks the button, and the
5023 * id of the button that was clicked will be passed as the only parameter to the callback
5024 * (could also be the top-right close button).
5025 * @param {String} title The title bar text
5026 * @param {String} msg The message box body text
5027 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5028 * @param {Object} scope (optional) The scope of the callback function
5029 * @return {Roo.MessageBox} This message box
5031 alert : function(title, msg, fn, scope)
5046 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5047 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5048 * You are responsible for closing the message box when the process is complete.
5049 * @param {String} msg The message box body text
5050 * @param {String} title (optional) The title bar text
5051 * @return {Roo.MessageBox} This message box
5053 wait : function(msg, title){
5064 waitTimer = Roo.TaskMgr.start({
5066 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5074 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5075 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5076 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5077 * @param {String} title The title bar text
5078 * @param {String} msg The message box body text
5079 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5080 * @param {Object} scope (optional) The scope of the callback function
5081 * @return {Roo.MessageBox} This message box
5083 confirm : function(title, msg, fn, scope){
5087 buttons: this.YESNO,
5096 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5097 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5098 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5099 * (could also be the top-right close button) and the text that was entered will be passed as the two
5100 * parameters to the callback.
5101 * @param {String} title The title bar text
5102 * @param {String} msg The message box body text
5103 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5104 * @param {Object} scope (optional) The scope of the callback function
5105 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5106 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5107 * @return {Roo.MessageBox} This message box
5109 prompt : function(title, msg, fn, scope, multiline){
5113 buttons: this.OKCANCEL,
5118 multiline: multiline,
5125 * Button config that displays a single OK button
5130 * Button config that displays Yes and No buttons
5133 YESNO : {yes:true, no:true},
5135 * Button config that displays OK and Cancel buttons
5138 OKCANCEL : {ok:true, cancel:true},
5140 * Button config that displays Yes, No and Cancel buttons
5143 YESNOCANCEL : {yes:true, no:true, cancel:true},
5146 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5149 defaultTextHeight : 75,
5151 * The maximum width in pixels of the message box (defaults to 600)
5156 * The minimum width in pixels of the message box (defaults to 100)
5161 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5162 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5165 minProgressWidth : 250,
5167 * An object containing the default button text strings that can be overriden for localized language support.
5168 * Supported properties are: ok, cancel, yes and no.
5169 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5182 * Shorthand for {@link Roo.MessageBox}
5184 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5185 Roo.Msg = Roo.Msg || Roo.MessageBox;
5194 * @class Roo.bootstrap.Navbar
5195 * @extends Roo.bootstrap.Component
5196 * Bootstrap Navbar class
5199 * Create a new Navbar
5200 * @param {Object} config The config object
5204 Roo.bootstrap.Navbar = function(config){
5205 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5209 * @event beforetoggle
5210 * Fire before toggle the menu
5211 * @param {Roo.EventObject} e
5213 "beforetoggle" : true
5217 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
5226 getAutoCreate : function(){
5229 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5233 initEvents :function ()
5235 //Roo.log(this.el.select('.navbar-toggle',true));
5236 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5243 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5245 var size = this.el.getSize();
5246 this.maskEl.setSize(size.width, size.height);
5247 this.maskEl.enableDisplayMode("block");
5256 getChildContainer : function()
5258 if (this.el && this.el.select('.collapse').getCount()) {
5259 return this.el.select('.collapse',true).first();
5274 onToggle : function()
5277 if(this.fireEvent('beforetoggle', this) === false){
5280 var ce = this.el.select('.navbar-collapse',true).first();
5282 if (!ce.hasClass('show')) {
5292 * Expand the navbar pulldown
5294 expand : function ()
5297 var ce = this.el.select('.navbar-collapse',true).first();
5298 if (ce.hasClass('collapsing')) {
5301 ce.dom.style.height = '';
5303 ce.addClass('in'); // old...
5304 ce.removeClass('collapse');
5305 ce.addClass('show');
5306 var h = ce.getHeight();
5308 ce.removeClass('show');
5309 // at this point we should be able to see it..
5310 ce.addClass('collapsing');
5312 ce.setHeight(0); // resize it ...
5313 ce.on('transitionend', function() {
5314 //Roo.log('done transition');
5315 ce.removeClass('collapsing');
5316 ce.addClass('show');
5317 ce.removeClass('collapse');
5319 ce.dom.style.height = '';
5320 }, this, { single: true} );
5322 ce.dom.scrollTop = 0;
5325 * Collapse the navbar pulldown
5327 collapse : function()
5329 var ce = this.el.select('.navbar-collapse',true).first();
5331 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5332 // it's collapsed or collapsing..
5335 ce.removeClass('in'); // old...
5336 ce.setHeight(ce.getHeight());
5337 ce.removeClass('show');
5338 ce.addClass('collapsing');
5340 ce.on('transitionend', function() {
5341 ce.dom.style.height = '';
5342 ce.removeClass('collapsing');
5343 ce.addClass('collapse');
5344 }, this, { single: true} );
5364 * @class Roo.bootstrap.NavSimplebar
5365 * @extends Roo.bootstrap.Navbar
5366 * Bootstrap Sidebar class
5368 * @cfg {Boolean} inverse is inverted color
5370 * @cfg {String} type (nav | pills | tabs)
5371 * @cfg {Boolean} arrangement stacked | justified
5372 * @cfg {String} align (left | right) alignment
5374 * @cfg {Boolean} main (true|false) main nav bar? default false
5375 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5377 * @cfg {String} tag (header|footer|nav|div) default is nav
5379 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5383 * Create a new Sidebar
5384 * @param {Object} config The config object
5388 Roo.bootstrap.NavSimplebar = function(config){
5389 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5392 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5408 getAutoCreate : function(){
5412 tag : this.tag || 'div',
5413 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5415 if (['light','white'].indexOf(this.weight) > -1) {
5416 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5418 cfg.cls += ' bg-' + this.weight;
5421 cfg.cls += ' navbar-inverse';
5425 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5427 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5436 cls: 'nav nav-' + this.xtype,
5442 this.type = this.type || 'nav';
5443 if (['tabs','pills'].indexOf(this.type) != -1) {
5444 cfg.cn[0].cls += ' nav-' + this.type
5448 if (this.type!=='nav') {
5449 Roo.log('nav type must be nav/tabs/pills')
5451 cfg.cn[0].cls += ' navbar-nav'
5457 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5458 cfg.cn[0].cls += ' nav-' + this.arrangement;
5462 if (this.align === 'right') {
5463 cfg.cn[0].cls += ' navbar-right';
5488 * navbar-expand-md fixed-top
5492 * @class Roo.bootstrap.NavHeaderbar
5493 * @extends Roo.bootstrap.NavSimplebar
5494 * Bootstrap Sidebar class
5496 * @cfg {String} brand what is brand
5497 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5498 * @cfg {String} brand_href href of the brand
5499 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5500 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5501 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5502 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5505 * Create a new Sidebar
5506 * @param {Object} config The config object
5510 Roo.bootstrap.NavHeaderbar = function(config){
5511 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5515 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5522 desktopCenter : false,
5525 getAutoCreate : function(){
5528 tag: this.nav || 'nav',
5529 cls: 'navbar navbar-expand-md',
5535 if (this.desktopCenter) {
5536 cn.push({cls : 'container', cn : []});
5544 cls: 'navbar-toggle navbar-toggler',
5545 'data-toggle': 'collapse',
5550 html: 'Toggle navigation'
5554 cls: 'icon-bar navbar-toggler-icon'
5567 cn.push( Roo.bootstrap.version == 4 ? btn : {
5569 cls: 'navbar-header',
5578 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5582 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5584 if (['light','white'].indexOf(this.weight) > -1) {
5585 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5587 cfg.cls += ' bg-' + this.weight;
5590 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5591 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5593 // tag can override this..
5595 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5598 if (this.brand !== '') {
5599 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5600 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5602 href: this.brand_href ? this.brand_href : '#',
5603 cls: 'navbar-brand',
5611 cfg.cls += ' main-nav';
5619 getHeaderChildContainer : function()
5621 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5622 return this.el.select('.navbar-header',true).first();
5625 return this.getChildContainer();
5628 getChildContainer : function()
5631 return this.el.select('.roo-navbar-collapse',true).first();
5636 initEvents : function()
5638 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5640 if (this.autohide) {
5645 Roo.get(document).on('scroll',function(e) {
5646 var ns = Roo.get(document).getScroll().top;
5647 var os = prevScroll;
5651 ft.removeClass('slideDown');
5652 ft.addClass('slideUp');
5655 ft.removeClass('slideUp');
5656 ft.addClass('slideDown');
5677 * @class Roo.bootstrap.NavSidebar
5678 * @extends Roo.bootstrap.Navbar
5679 * Bootstrap Sidebar class
5682 * Create a new Sidebar
5683 * @param {Object} config The config object
5687 Roo.bootstrap.NavSidebar = function(config){
5688 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5691 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
5693 sidebar : true, // used by Navbar Item and NavbarGroup at present...
5695 getAutoCreate : function(){
5700 cls: 'sidebar sidebar-nav'
5722 * @class Roo.bootstrap.NavGroup
5723 * @extends Roo.bootstrap.Component
5724 * Bootstrap NavGroup class
5725 * @cfg {String} align (left|right)
5726 * @cfg {Boolean} inverse
5727 * @cfg {String} type (nav|pills|tab) default nav
5728 * @cfg {String} navId - reference Id for navbar.
5732 * Create a new nav group
5733 * @param {Object} config The config object
5736 Roo.bootstrap.NavGroup = function(config){
5737 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5740 Roo.bootstrap.NavGroup.register(this);
5744 * Fires when the active item changes
5745 * @param {Roo.bootstrap.NavGroup} this
5746 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5747 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
5754 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
5765 getAutoCreate : function()
5767 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5773 if (Roo.bootstrap.version == 4) {
5774 if (['tabs','pills'].indexOf(this.type) != -1) {
5775 cfg.cls += ' nav-' + this.type;
5777 // trying to remove so header bar can right align top?
5778 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5779 // do not use on header bar...
5780 cfg.cls += ' navbar-nav';
5785 if (['tabs','pills'].indexOf(this.type) != -1) {
5786 cfg.cls += ' nav-' + this.type
5788 if (this.type !== 'nav') {
5789 Roo.log('nav type must be nav/tabs/pills')
5791 cfg.cls += ' navbar-nav'
5795 if (this.parent() && this.parent().sidebar) {
5798 cls: 'dashboard-menu sidebar-menu'
5804 if (this.form === true) {
5807 cls: 'navbar-form form-inline'
5809 //nav navbar-right ml-md-auto
5810 if (this.align === 'right') {
5811 cfg.cls += ' navbar-right ml-md-auto';
5813 cfg.cls += ' navbar-left';
5817 if (this.align === 'right') {
5818 cfg.cls += ' navbar-right ml-md-auto';
5820 cfg.cls += ' mr-auto';
5824 cfg.cls += ' navbar-inverse';
5832 * sets the active Navigation item
5833 * @param {Roo.bootstrap.NavItem} the new current navitem
5835 setActiveItem : function(item)
5838 Roo.each(this.navItems, function(v){
5843 v.setActive(false, true);
5850 item.setActive(true, true);
5851 this.fireEvent('changed', this, item, prev);
5856 * gets the active Navigation item
5857 * @return {Roo.bootstrap.NavItem} the current navitem
5859 getActive : function()
5863 Roo.each(this.navItems, function(v){
5874 indexOfNav : function()
5878 Roo.each(this.navItems, function(v,i){
5889 * adds a Navigation item
5890 * @param {Roo.bootstrap.NavItem} the navitem to add
5892 addItem : function(cfg)
5894 if (this.form && Roo.bootstrap.version == 4) {
5897 var cn = new Roo.bootstrap.NavItem(cfg);
5899 cn.parentId = this.id;
5900 cn.onRender(this.el, null);
5904 * register a Navigation item
5905 * @param {Roo.bootstrap.NavItem} the navitem to add
5907 register : function(item)
5909 this.navItems.push( item);
5910 item.navId = this.navId;
5915 * clear all the Navigation item
5918 clearAll : function()
5921 this.el.dom.innerHTML = '';
5924 getNavItem: function(tabId)
5927 Roo.each(this.navItems, function(e) {
5928 if (e.tabId == tabId) {
5938 setActiveNext : function()
5940 var i = this.indexOfNav(this.getActive());
5941 if (i > this.navItems.length) {
5944 this.setActiveItem(this.navItems[i+1]);
5946 setActivePrev : function()
5948 var i = this.indexOfNav(this.getActive());
5952 this.setActiveItem(this.navItems[i-1]);
5954 clearWasActive : function(except) {
5955 Roo.each(this.navItems, function(e) {
5956 if (e.tabId != except.tabId && e.was_active) {
5957 e.was_active = false;
5964 getWasActive : function ()
5967 Roo.each(this.navItems, function(e) {
5982 Roo.apply(Roo.bootstrap.NavGroup, {
5986 * register a Navigation Group
5987 * @param {Roo.bootstrap.NavGroup} the navgroup to add
5989 register : function(navgrp)
5991 this.groups[navgrp.navId] = navgrp;
5995 * fetch a Navigation Group based on the navigation ID
5996 * @param {string} the navgroup to add
5997 * @returns {Roo.bootstrap.NavGroup} the navgroup
5999 get: function(navId) {
6000 if (typeof(this.groups[navId]) == 'undefined') {
6002 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6004 return this.groups[navId] ;
6019 * @class Roo.bootstrap.NavItem
6020 * @extends Roo.bootstrap.Component
6021 * Bootstrap Navbar.NavItem class
6022 * @cfg {String} href link to
6023 * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
6025 * @cfg {String} html content of button
6026 * @cfg {String} badge text inside badge
6027 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6028 * @cfg {String} glyphicon DEPRICATED - use fa
6029 * @cfg {String} icon DEPRICATED - use fa
6030 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6031 * @cfg {Boolean} active Is item active
6032 * @cfg {Boolean} disabled Is item disabled
6034 * @cfg {Boolean} preventDefault (true | false) default false
6035 * @cfg {String} tabId the tab that this item activates.
6036 * @cfg {String} tagtype (a|span) render as a href or span?
6037 * @cfg {Boolean} animateRef (true|false) link to element default false
6040 * Create a new Navbar Item
6041 * @param {Object} config The config object
6043 Roo.bootstrap.NavItem = function(config){
6044 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6049 * The raw click event for the entire grid.
6050 * @param {Roo.EventObject} e
6055 * Fires when the active item active state changes
6056 * @param {Roo.bootstrap.NavItem} this
6057 * @param {boolean} state the new state
6063 * Fires when scroll to element
6064 * @param {Roo.bootstrap.NavItem} this
6065 * @param {Object} options
6066 * @param {Roo.EventObject} e
6074 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
6083 preventDefault : false,
6091 button_outline : false,
6095 getAutoCreate : function(){
6103 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
6105 if (this.disabled) {
6106 cfg.cls += ' disabled';
6110 if (this.button_weight.length) {
6111 cfg.tag = this.href ? 'a' : 'button';
6112 cfg.html = this.html || '';
6113 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6115 cfg.href = this.href;
6118 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6121 // menu .. should add dropdown-menu class - so no need for carat..
6123 if (this.badge !== '') {
6125 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6130 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6134 href : this.href || "#",
6135 html: this.html || ''
6138 if (this.tagtype == 'a') {
6139 cfg.cn[0].cls = 'nav-link';
6142 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6145 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6147 if(this.glyphicon) {
6148 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6153 cfg.cn[0].html += " <span class='caret'></span>";
6157 if (this.badge !== '') {
6159 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6167 onRender : function(ct, position)
6169 // Roo.log("Call onRender: " + this.xtype);
6170 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6174 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6175 this.navLink = this.el.select('.nav-link',true).first();
6180 initEvents: function()
6182 if (typeof (this.menu) != 'undefined') {
6183 this.menu.parentType = this.xtype;
6184 this.menu.triggerEl = this.el;
6185 this.menu = this.addxtype(Roo.apply({}, this.menu));
6188 this.el.select('a',true).on('click', this.onClick, this);
6190 if(this.tagtype == 'span'){
6191 this.el.select('span',true).on('click', this.onClick, this);
6194 // at this point parent should be available..
6195 this.parent().register(this);
6198 onClick : function(e)
6200 if (e.getTarget('.dropdown-menu-item')) {
6201 // did you click on a menu itemm.... - then don't trigger onclick..
6206 this.preventDefault ||
6209 Roo.log("NavItem - prevent Default?");
6213 if (this.disabled) {
6217 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6218 if (tg && tg.transition) {
6219 Roo.log("waiting for the transitionend");
6225 //Roo.log("fire event clicked");
6226 if(this.fireEvent('click', this, e) === false){
6230 if(this.tagtype == 'span'){
6234 //Roo.log(this.href);
6235 var ael = this.el.select('a',true).first();
6238 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6239 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6240 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6241 return; // ignore... - it's a 'hash' to another page.
6243 Roo.log("NavItem - prevent Default?");
6245 this.scrollToElement(e);
6249 var p = this.parent();
6251 if (['tabs','pills'].indexOf(p.type)!==-1) {
6252 if (typeof(p.setActiveItem) !== 'undefined') {
6253 p.setActiveItem(this);
6257 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6258 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6259 // remove the collapsed menu expand...
6260 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6264 isActive: function () {
6267 setActive : function(state, fire, is_was_active)
6269 if (this.active && !state && this.navId) {
6270 this.was_active = true;
6271 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6273 nv.clearWasActive(this);
6277 this.active = state;
6280 this.el.removeClass('active');
6281 this.navLink ? this.navLink.removeClass('active') : false;
6282 } else if (!this.el.hasClass('active')) {
6284 this.el.addClass('active');
6285 if (Roo.bootstrap.version == 4 && this.navLink ) {
6286 this.navLink.addClass('active');
6291 this.fireEvent('changed', this, state);
6294 // show a panel if it's registered and related..
6296 if (!this.navId || !this.tabId || !state || is_was_active) {
6300 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6304 var pan = tg.getPanelByName(this.tabId);
6308 // if we can not flip to new panel - go back to old nav highlight..
6309 if (false == tg.showPanel(pan)) {
6310 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6312 var onav = nv.getWasActive();
6314 onav.setActive(true, false, true);
6323 // this should not be here...
6324 setDisabled : function(state)
6326 this.disabled = state;
6328 this.el.removeClass('disabled');
6329 } else if (!this.el.hasClass('disabled')) {
6330 this.el.addClass('disabled');
6336 * Fetch the element to display the tooltip on.
6337 * @return {Roo.Element} defaults to this.el
6339 tooltipEl : function()
6341 return this.el.select('' + this.tagtype + '', true).first();
6344 scrollToElement : function(e)
6346 var c = document.body;
6349 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6351 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6352 c = document.documentElement;
6355 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6361 var o = target.calcOffsetsTo(c);
6368 this.fireEvent('scrollto', this, options, e);
6370 Roo.get(c).scrollTo('top', options.value, true);
6383 * <span> icon </span>
6384 * <span> text </span>
6385 * <span>badge </span>
6389 * @class Roo.bootstrap.NavSidebarItem
6390 * @extends Roo.bootstrap.NavItem
6391 * Bootstrap Navbar.NavSidebarItem class
6392 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6393 * {Boolean} open is the menu open
6394 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6395 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6396 * {String} buttonSize (sm|md|lg)the extra classes for the button
6397 * {Boolean} showArrow show arrow next to the text (default true)
6399 * Create a new Navbar Button
6400 * @param {Object} config The config object
6402 Roo.bootstrap.NavSidebarItem = function(config){
6403 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6408 * The raw click event for the entire grid.
6409 * @param {Roo.EventObject} e
6414 * Fires when the active item active state changes
6415 * @param {Roo.bootstrap.NavSidebarItem} this
6416 * @param {boolean} state the new state
6424 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6426 badgeWeight : 'default',
6432 buttonWeight : 'default',
6438 getAutoCreate : function(){
6443 href : this.href || '#',
6449 if(this.buttonView){
6452 href : this.href || '#',
6453 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6466 cfg.cls += ' active';
6469 if (this.disabled) {
6470 cfg.cls += ' disabled';
6473 cfg.cls += ' open x-open';
6476 if (this.glyphicon || this.icon) {
6477 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6478 a.cn.push({ tag : 'i', cls : c }) ;
6481 if(!this.buttonView){
6484 html : this.html || ''
6491 if (this.badge !== '') {
6492 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6498 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6501 a.cls += ' dropdown-toggle treeview' ;
6507 initEvents : function()
6509 if (typeof (this.menu) != 'undefined') {
6510 this.menu.parentType = this.xtype;
6511 this.menu.triggerEl = this.el;
6512 this.menu = this.addxtype(Roo.apply({}, this.menu));
6515 this.el.on('click', this.onClick, this);
6517 if(this.badge !== ''){
6518 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6523 onClick : function(e)
6530 if(this.preventDefault){
6534 this.fireEvent('click', this, e);
6537 disable : function()
6539 this.setDisabled(true);
6544 this.setDisabled(false);
6547 setDisabled : function(state)
6549 if(this.disabled == state){
6553 this.disabled = state;
6556 this.el.addClass('disabled');
6560 this.el.removeClass('disabled');
6565 setActive : function(state)
6567 if(this.active == state){
6571 this.active = state;
6574 this.el.addClass('active');
6578 this.el.removeClass('active');
6583 isActive: function ()
6588 setBadge : function(str)
6594 this.badgeEl.dom.innerHTML = str;
6611 * @class Roo.bootstrap.Row
6612 * @extends Roo.bootstrap.Component
6613 * Bootstrap Row class (contains columns...)
6617 * @param {Object} config The config object
6620 Roo.bootstrap.Row = function(config){
6621 Roo.bootstrap.Row.superclass.constructor.call(this, config);
6624 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
6626 getAutoCreate : function(){
6645 * @class Roo.bootstrap.Pagination
6646 * @extends Roo.bootstrap.Component
6647 * Bootstrap Pagination class
6648 * @cfg {String} size xs | sm | md | lg
6649 * @cfg {Boolean} inverse false | true
6652 * Create a new Pagination
6653 * @param {Object} config The config object
6656 Roo.bootstrap.Pagination = function(config){
6657 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6660 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
6666 getAutoCreate : function(){
6672 cfg.cls += ' inverse';
6678 cfg.cls += " " + this.cls;
6696 * @class Roo.bootstrap.PaginationItem
6697 * @extends Roo.bootstrap.Component
6698 * Bootstrap PaginationItem class
6699 * @cfg {String} html text
6700 * @cfg {String} href the link
6701 * @cfg {Boolean} preventDefault (true | false) default true
6702 * @cfg {Boolean} active (true | false) default false
6703 * @cfg {Boolean} disabled default false
6707 * Create a new PaginationItem
6708 * @param {Object} config The config object
6712 Roo.bootstrap.PaginationItem = function(config){
6713 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6718 * The raw click event for the entire grid.
6719 * @param {Roo.EventObject} e
6725 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
6729 preventDefault: true,
6734 getAutoCreate : function(){
6740 href : this.href ? this.href : '#',
6741 html : this.html ? this.html : ''
6751 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6755 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6761 initEvents: function() {
6763 this.el.on('click', this.onClick, this);
6766 onClick : function(e)
6768 Roo.log('PaginationItem on click ');
6769 if(this.preventDefault){
6777 this.fireEvent('click', this, e);
6793 * @class Roo.bootstrap.Slider
6794 * @extends Roo.bootstrap.Component
6795 * Bootstrap Slider class
6798 * Create a new Slider
6799 * @param {Object} config The config object
6802 Roo.bootstrap.Slider = function(config){
6803 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6806 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
6808 getAutoCreate : function(){
6812 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6816 cls: 'ui-slider-handle ui-state-default ui-corner-all'
6828 * Ext JS Library 1.1.1
6829 * Copyright(c) 2006-2007, Ext JS, LLC.
6831 * Originally Released Under LGPL - original licence link has changed is not relivant.
6834 * <script type="text/javascript">
6839 * @class Roo.grid.ColumnModel
6840 * @extends Roo.util.Observable
6841 * This is the default implementation of a ColumnModel used by the Grid. It defines
6842 * the columns in the grid.
6845 var colModel = new Roo.grid.ColumnModel([
6846 {header: "Ticker", width: 60, sortable: true, locked: true},
6847 {header: "Company Name", width: 150, sortable: true},
6848 {header: "Market Cap.", width: 100, sortable: true},
6849 {header: "$ Sales", width: 100, sortable: true, renderer: money},
6850 {header: "Employees", width: 100, sortable: true, resizable: false}
6855 * The config options listed for this class are options which may appear in each
6856 * individual column definition.
6857 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
6859 * @param {Object} config An Array of column config objects. See this class's
6860 * config objects for details.
6862 Roo.grid.ColumnModel = function(config){
6864 * The config passed into the constructor
6866 this.config = config;
6869 // if no id, create one
6870 // if the column does not have a dataIndex mapping,
6871 // map it to the order it is in the config
6872 for(var i = 0, len = config.length; i < len; i++){
6874 if(typeof c.dataIndex == "undefined"){
6877 if(typeof c.renderer == "string"){
6878 c.renderer = Roo.util.Format[c.renderer];
6880 if(typeof c.id == "undefined"){
6883 if(c.editor && c.editor.xtype){
6884 c.editor = Roo.factory(c.editor, Roo.grid);
6886 if(c.editor && c.editor.isFormField){
6887 c.editor = new Roo.grid.GridEditor(c.editor);
6889 this.lookup[c.id] = c;
6893 * The width of columns which have no width specified (defaults to 100)
6896 this.defaultWidth = 100;
6899 * Default sortable of columns which have no sortable specified (defaults to false)
6902 this.defaultSortable = false;
6906 * @event widthchange
6907 * Fires when the width of a column changes.
6908 * @param {ColumnModel} this
6909 * @param {Number} columnIndex The column index
6910 * @param {Number} newWidth The new width
6912 "widthchange": true,
6914 * @event headerchange
6915 * Fires when the text of a header changes.
6916 * @param {ColumnModel} this
6917 * @param {Number} columnIndex The column index
6918 * @param {Number} newText The new header text
6920 "headerchange": true,
6922 * @event hiddenchange
6923 * Fires when a column is hidden or "unhidden".
6924 * @param {ColumnModel} this
6925 * @param {Number} columnIndex The column index
6926 * @param {Boolean} hidden true if hidden, false otherwise
6928 "hiddenchange": true,
6930 * @event columnmoved
6931 * Fires when a column is moved.
6932 * @param {ColumnModel} this
6933 * @param {Number} oldIndex
6934 * @param {Number} newIndex
6936 "columnmoved" : true,
6938 * @event columlockchange
6939 * Fires when a column's locked state is changed
6940 * @param {ColumnModel} this
6941 * @param {Number} colIndex
6942 * @param {Boolean} locked true if locked
6944 "columnlockchange" : true
6946 Roo.grid.ColumnModel.superclass.constructor.call(this);
6948 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
6950 * @cfg {String} header The header text to display in the Grid view.
6953 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
6954 * {@link Roo.data.Record} definition from which to draw the column's value. If not
6955 * specified, the column's index is used as an index into the Record's data Array.
6958 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
6959 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
6962 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
6963 * Defaults to the value of the {@link #defaultSortable} property.
6964 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
6967 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
6970 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
6973 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
6976 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
6979 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
6980 * given the cell's data value. See {@link #setRenderer}. If not specified, the
6981 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
6982 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
6985 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
6988 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
6991 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
6994 * @cfg {String} cursor (Optional)
6997 * @cfg {String} tooltip (Optional)
7000 * @cfg {Number} xs (Optional)
7003 * @cfg {Number} sm (Optional)
7006 * @cfg {Number} md (Optional)
7009 * @cfg {Number} lg (Optional)
7012 * Returns the id of the column at the specified index.
7013 * @param {Number} index The column index
7014 * @return {String} the id
7016 getColumnId : function(index){
7017 return this.config[index].id;
7021 * Returns the column for a specified id.
7022 * @param {String} id The column id
7023 * @return {Object} the column
7025 getColumnById : function(id){
7026 return this.lookup[id];
7031 * Returns the column for a specified dataIndex.
7032 * @param {String} dataIndex The column dataIndex
7033 * @return {Object|Boolean} the column or false if not found
7035 getColumnByDataIndex: function(dataIndex){
7036 var index = this.findColumnIndex(dataIndex);
7037 return index > -1 ? this.config[index] : false;
7041 * Returns the index for a specified column id.
7042 * @param {String} id The column id
7043 * @return {Number} the index, or -1 if not found
7045 getIndexById : function(id){
7046 for(var i = 0, len = this.config.length; i < len; i++){
7047 if(this.config[i].id == id){
7055 * Returns the index for a specified column dataIndex.
7056 * @param {String} dataIndex The column dataIndex
7057 * @return {Number} the index, or -1 if not found
7060 findColumnIndex : function(dataIndex){
7061 for(var i = 0, len = this.config.length; i < len; i++){
7062 if(this.config[i].dataIndex == dataIndex){
7070 moveColumn : function(oldIndex, newIndex){
7071 var c = this.config[oldIndex];
7072 this.config.splice(oldIndex, 1);
7073 this.config.splice(newIndex, 0, c);
7074 this.dataMap = null;
7075 this.fireEvent("columnmoved", this, oldIndex, newIndex);
7078 isLocked : function(colIndex){
7079 return this.config[colIndex].locked === true;
7082 setLocked : function(colIndex, value, suppressEvent){
7083 if(this.isLocked(colIndex) == value){
7086 this.config[colIndex].locked = value;
7088 this.fireEvent("columnlockchange", this, colIndex, value);
7092 getTotalLockedWidth : function(){
7094 for(var i = 0; i < this.config.length; i++){
7095 if(this.isLocked(i) && !this.isHidden(i)){
7096 this.totalWidth += this.getColumnWidth(i);
7102 getLockedCount : function(){
7103 for(var i = 0, len = this.config.length; i < len; i++){
7104 if(!this.isLocked(i)){
7109 return this.config.length;
7113 * Returns the number of columns.
7116 getColumnCount : function(visibleOnly){
7117 if(visibleOnly === true){
7119 for(var i = 0, len = this.config.length; i < len; i++){
7120 if(!this.isHidden(i)){
7126 return this.config.length;
7130 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7131 * @param {Function} fn
7132 * @param {Object} scope (optional)
7133 * @return {Array} result
7135 getColumnsBy : function(fn, scope){
7137 for(var i = 0, len = this.config.length; i < len; i++){
7138 var c = this.config[i];
7139 if(fn.call(scope||this, c, i) === true){
7147 * Returns true if the specified column is sortable.
7148 * @param {Number} col The column index
7151 isSortable : function(col){
7152 if(typeof this.config[col].sortable == "undefined"){
7153 return this.defaultSortable;
7155 return this.config[col].sortable;
7159 * Returns the rendering (formatting) function defined for the column.
7160 * @param {Number} col The column index.
7161 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7163 getRenderer : function(col){
7164 if(!this.config[col].renderer){
7165 return Roo.grid.ColumnModel.defaultRenderer;
7167 return this.config[col].renderer;
7171 * Sets the rendering (formatting) function for a column.
7172 * @param {Number} col The column index
7173 * @param {Function} fn The function to use to process the cell's raw data
7174 * to return HTML markup for the grid view. The render function is called with
7175 * the following parameters:<ul>
7176 * <li>Data value.</li>
7177 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7178 * <li>css A CSS style string to apply to the table cell.</li>
7179 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7180 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7181 * <li>Row index</li>
7182 * <li>Column index</li>
7183 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7185 setRenderer : function(col, fn){
7186 this.config[col].renderer = fn;
7190 * Returns the width for the specified column.
7191 * @param {Number} col The column index
7194 getColumnWidth : function(col){
7195 return this.config[col].width * 1 || this.defaultWidth;
7199 * Sets the width for a column.
7200 * @param {Number} col The column index
7201 * @param {Number} width The new width
7203 setColumnWidth : function(col, width, suppressEvent){
7204 this.config[col].width = width;
7205 this.totalWidth = null;
7207 this.fireEvent("widthchange", this, col, width);
7212 * Returns the total width of all columns.
7213 * @param {Boolean} includeHidden True to include hidden column widths
7216 getTotalWidth : function(includeHidden){
7217 if(!this.totalWidth){
7218 this.totalWidth = 0;
7219 for(var i = 0, len = this.config.length; i < len; i++){
7220 if(includeHidden || !this.isHidden(i)){
7221 this.totalWidth += this.getColumnWidth(i);
7225 return this.totalWidth;
7229 * Returns the header for the specified column.
7230 * @param {Number} col The column index
7233 getColumnHeader : function(col){
7234 return this.config[col].header;
7238 * Sets the header for a column.
7239 * @param {Number} col The column index
7240 * @param {String} header The new header
7242 setColumnHeader : function(col, header){
7243 this.config[col].header = header;
7244 this.fireEvent("headerchange", this, col, header);
7248 * Returns the tooltip for the specified column.
7249 * @param {Number} col The column index
7252 getColumnTooltip : function(col){
7253 return this.config[col].tooltip;
7256 * Sets the tooltip for a column.
7257 * @param {Number} col The column index
7258 * @param {String} tooltip The new tooltip
7260 setColumnTooltip : function(col, tooltip){
7261 this.config[col].tooltip = tooltip;
7265 * Returns the dataIndex for the specified column.
7266 * @param {Number} col The column index
7269 getDataIndex : function(col){
7270 return this.config[col].dataIndex;
7274 * Sets the dataIndex for a column.
7275 * @param {Number} col The column index
7276 * @param {Number} dataIndex The new dataIndex
7278 setDataIndex : function(col, dataIndex){
7279 this.config[col].dataIndex = dataIndex;
7285 * Returns true if the cell is editable.
7286 * @param {Number} colIndex The column index
7287 * @param {Number} rowIndex The row index - this is nto actually used..?
7290 isCellEditable : function(colIndex, rowIndex){
7291 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7295 * Returns the editor defined for the cell/column.
7296 * return false or null to disable editing.
7297 * @param {Number} colIndex The column index
7298 * @param {Number} rowIndex The row index
7301 getCellEditor : function(colIndex, rowIndex){
7302 return this.config[colIndex].editor;
7306 * Sets if a column is editable.
7307 * @param {Number} col The column index
7308 * @param {Boolean} editable True if the column is editable
7310 setEditable : function(col, editable){
7311 this.config[col].editable = editable;
7316 * Returns true if the column is hidden.
7317 * @param {Number} colIndex The column index
7320 isHidden : function(colIndex){
7321 return this.config[colIndex].hidden;
7326 * Returns true if the column width cannot be changed
7328 isFixed : function(colIndex){
7329 return this.config[colIndex].fixed;
7333 * Returns true if the column can be resized
7336 isResizable : function(colIndex){
7337 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7340 * Sets if a column is hidden.
7341 * @param {Number} colIndex The column index
7342 * @param {Boolean} hidden True if the column is hidden
7344 setHidden : function(colIndex, hidden){
7345 this.config[colIndex].hidden = hidden;
7346 this.totalWidth = null;
7347 this.fireEvent("hiddenchange", this, colIndex, hidden);
7351 * Sets the editor for a column.
7352 * @param {Number} col The column index
7353 * @param {Object} editor The editor object
7355 setEditor : function(col, editor){
7356 this.config[col].editor = editor;
7360 Roo.grid.ColumnModel.defaultRenderer = function(value)
7362 if(typeof value == "object") {
7365 if(typeof value == "string" && value.length < 1){
7369 return String.format("{0}", value);
7372 // Alias for backwards compatibility
7373 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7376 * Ext JS Library 1.1.1
7377 * Copyright(c) 2006-2007, Ext JS, LLC.
7379 * Originally Released Under LGPL - original licence link has changed is not relivant.
7382 * <script type="text/javascript">
7386 * @class Roo.LoadMask
7387 * A simple utility class for generically masking elements while loading data. If the element being masked has
7388 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7389 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
7390 * element's UpdateManager load indicator and will be destroyed after the initial load.
7392 * Create a new LoadMask
7393 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7394 * @param {Object} config The config object
7396 Roo.LoadMask = function(el, config){
7397 this.el = Roo.get(el);
7398 Roo.apply(this, config);
7400 this.store.on('beforeload', this.onBeforeLoad, this);
7401 this.store.on('load', this.onLoad, this);
7402 this.store.on('loadexception', this.onLoadException, this);
7403 this.removeMask = false;
7405 var um = this.el.getUpdateManager();
7406 um.showLoadIndicator = false; // disable the default indicator
7407 um.on('beforeupdate', this.onBeforeLoad, this);
7408 um.on('update', this.onLoad, this);
7409 um.on('failure', this.onLoad, this);
7410 this.removeMask = true;
7414 Roo.LoadMask.prototype = {
7416 * @cfg {Boolean} removeMask
7417 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7418 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
7422 * The text to display in a centered loading message box (defaults to 'Loading...')
7426 * @cfg {String} msgCls
7427 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7429 msgCls : 'x-mask-loading',
7432 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7438 * Disables the mask to prevent it from being displayed
7440 disable : function(){
7441 this.disabled = true;
7445 * Enables the mask so that it can be displayed
7447 enable : function(){
7448 this.disabled = false;
7451 onLoadException : function()
7455 if (typeof(arguments[3]) != 'undefined') {
7456 Roo.MessageBox.alert("Error loading",arguments[3]);
7460 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7461 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7468 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7473 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7477 onBeforeLoad : function(){
7479 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7484 destroy : function(){
7486 this.store.un('beforeload', this.onBeforeLoad, this);
7487 this.store.un('load', this.onLoad, this);
7488 this.store.un('loadexception', this.onLoadException, this);
7490 var um = this.el.getUpdateManager();
7491 um.un('beforeupdate', this.onBeforeLoad, this);
7492 um.un('update', this.onLoad, this);
7493 um.un('failure', this.onLoad, this);
7504 * @class Roo.bootstrap.Table
7505 * @extends Roo.bootstrap.Component
7506 * Bootstrap Table class
7507 * @cfg {String} cls table class
7508 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7509 * @cfg {String} bgcolor Specifies the background color for a table
7510 * @cfg {Number} border Specifies whether the table cells should have borders or not
7511 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7512 * @cfg {Number} cellspacing Specifies the space between cells
7513 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7514 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7515 * @cfg {String} sortable Specifies that the table should be sortable
7516 * @cfg {String} summary Specifies a summary of the content of a table
7517 * @cfg {Number} width Specifies the width of a table
7518 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7520 * @cfg {boolean} striped Should the rows be alternative striped
7521 * @cfg {boolean} bordered Add borders to the table
7522 * @cfg {boolean} hover Add hover highlighting
7523 * @cfg {boolean} condensed Format condensed
7524 * @cfg {boolean} responsive Format condensed
7525 * @cfg {Boolean} loadMask (true|false) default false
7526 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7527 * @cfg {Boolean} headerShow (true|false) generate thead, default true
7528 * @cfg {Boolean} rowSelection (true|false) default false
7529 * @cfg {Boolean} cellSelection (true|false) default false
7530 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7531 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
7532 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
7533 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
7537 * Create a new Table
7538 * @param {Object} config The config object
7541 Roo.bootstrap.Table = function(config){
7542 Roo.bootstrap.Table.superclass.constructor.call(this, config);
7547 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7548 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7549 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7550 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7552 this.sm = this.sm || {xtype: 'RowSelectionModel'};
7554 this.sm.grid = this;
7555 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7556 this.sm = this.selModel;
7557 this.sm.xmodule = this.xmodule || false;
7560 if (this.cm && typeof(this.cm.config) == 'undefined') {
7561 this.colModel = new Roo.grid.ColumnModel(this.cm);
7562 this.cm = this.colModel;
7563 this.cm.xmodule = this.xmodule || false;
7566 this.store= Roo.factory(this.store, Roo.data);
7567 this.ds = this.store;
7568 this.ds.xmodule = this.xmodule || false;
7571 if (this.footer && this.store) {
7572 this.footer.dataSource = this.ds;
7573 this.footer = Roo.factory(this.footer);
7580 * Fires when a cell is clicked
7581 * @param {Roo.bootstrap.Table} this
7582 * @param {Roo.Element} el
7583 * @param {Number} rowIndex
7584 * @param {Number} columnIndex
7585 * @param {Roo.EventObject} e
7589 * @event celldblclick
7590 * Fires when a cell is double clicked
7591 * @param {Roo.bootstrap.Table} this
7592 * @param {Roo.Element} el
7593 * @param {Number} rowIndex
7594 * @param {Number} columnIndex
7595 * @param {Roo.EventObject} e
7597 "celldblclick" : true,
7600 * Fires when a row is clicked
7601 * @param {Roo.bootstrap.Table} this
7602 * @param {Roo.Element} el
7603 * @param {Number} rowIndex
7604 * @param {Roo.EventObject} e
7608 * @event rowdblclick
7609 * Fires when a row is double clicked
7610 * @param {Roo.bootstrap.Table} this
7611 * @param {Roo.Element} el
7612 * @param {Number} rowIndex
7613 * @param {Roo.EventObject} e
7615 "rowdblclick" : true,
7618 * Fires when a mouseover occur
7619 * @param {Roo.bootstrap.Table} this
7620 * @param {Roo.Element} el
7621 * @param {Number} rowIndex
7622 * @param {Number} columnIndex
7623 * @param {Roo.EventObject} e
7628 * Fires when a mouseout occur
7629 * @param {Roo.bootstrap.Table} this
7630 * @param {Roo.Element} el
7631 * @param {Number} rowIndex
7632 * @param {Number} columnIndex
7633 * @param {Roo.EventObject} e
7638 * Fires when a row is rendered, so you can change add a style to it.
7639 * @param {Roo.bootstrap.Table} this
7640 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
7644 * @event rowsrendered
7645 * Fires when all the rows have been rendered
7646 * @param {Roo.bootstrap.Table} this
7648 'rowsrendered' : true,
7650 * @event contextmenu
7651 * The raw contextmenu event for the entire grid.
7652 * @param {Roo.EventObject} e
7654 "contextmenu" : true,
7656 * @event rowcontextmenu
7657 * Fires when a row is right clicked
7658 * @param {Roo.bootstrap.Table} this
7659 * @param {Number} rowIndex
7660 * @param {Roo.EventObject} e
7662 "rowcontextmenu" : true,
7664 * @event cellcontextmenu
7665 * Fires when a cell is right clicked
7666 * @param {Roo.bootstrap.Table} this
7667 * @param {Number} rowIndex
7668 * @param {Number} cellIndex
7669 * @param {Roo.EventObject} e
7671 "cellcontextmenu" : true,
7673 * @event headercontextmenu
7674 * Fires when a header is right clicked
7675 * @param {Roo.bootstrap.Table} this
7676 * @param {Number} columnIndex
7677 * @param {Roo.EventObject} e
7679 "headercontextmenu" : true
7683 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
7709 rowSelection : false,
7710 cellSelection : false,
7713 // Roo.Element - the tbody
7715 // Roo.Element - thead element
7718 container: false, // used by gridpanel...
7724 auto_hide_footer : false,
7726 getAutoCreate : function()
7728 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7735 if (this.scrollBody) {
7736 cfg.cls += ' table-body-fixed';
7739 cfg.cls += ' table-striped';
7743 cfg.cls += ' table-hover';
7745 if (this.bordered) {
7746 cfg.cls += ' table-bordered';
7748 if (this.condensed) {
7749 cfg.cls += ' table-condensed';
7751 if (this.responsive) {
7752 cfg.cls += ' table-responsive';
7756 cfg.cls+= ' ' +this.cls;
7759 // this lot should be simplifed...
7772 ].forEach(function(k) {
7780 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7783 if(this.store || this.cm){
7784 if(this.headerShow){
7785 cfg.cn.push(this.renderHeader());
7788 cfg.cn.push(this.renderBody());
7790 if(this.footerShow){
7791 cfg.cn.push(this.renderFooter());
7793 // where does this come from?
7794 //cfg.cls+= ' TableGrid';
7797 return { cn : [ cfg ] };
7800 initEvents : function()
7802 if(!this.store || !this.cm){
7805 if (this.selModel) {
7806 this.selModel.initEvents();
7810 //Roo.log('initEvents with ds!!!!');
7812 this.mainBody = this.el.select('tbody', true).first();
7813 this.mainHead = this.el.select('thead', true).first();
7814 this.mainFoot = this.el.select('tfoot', true).first();
7820 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7821 e.on('click', _this.sort, _this);
7824 this.mainBody.on("click", this.onClick, this);
7825 this.mainBody.on("dblclick", this.onDblClick, this);
7827 // why is this done????? = it breaks dialogs??
7828 //this.parent().el.setStyle('position', 'relative');
7832 this.footer.parentId = this.id;
7833 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7836 this.el.select('tfoot tr td').first().addClass('hide');
7841 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7844 this.store.on('load', this.onLoad, this);
7845 this.store.on('beforeload', this.onBeforeLoad, this);
7846 this.store.on('update', this.onUpdate, this);
7847 this.store.on('add', this.onAdd, this);
7848 this.store.on("clear", this.clear, this);
7850 this.el.on("contextmenu", this.onContextMenu, this);
7852 this.mainBody.on('scroll', this.onBodyScroll, this);
7854 this.cm.on("headerchange", this.onHeaderChange, this);
7856 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
7860 onContextMenu : function(e, t)
7862 this.processEvent("contextmenu", e);
7865 processEvent : function(name, e)
7867 if (name != 'touchstart' ) {
7868 this.fireEvent(name, e);
7871 var t = e.getTarget();
7873 var cell = Roo.get(t);
7879 if(cell.findParent('tfoot', false, true)){
7883 if(cell.findParent('thead', false, true)){
7885 if(e.getTarget().nodeName.toLowerCase() != 'th'){
7886 cell = Roo.get(t).findParent('th', false, true);
7888 Roo.log("failed to find th in thead?");
7889 Roo.log(e.getTarget());
7894 var cellIndex = cell.dom.cellIndex;
7896 var ename = name == 'touchstart' ? 'click' : name;
7897 this.fireEvent("header" + ename, this, cellIndex, e);
7902 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7903 cell = Roo.get(t).findParent('td', false, true);
7905 Roo.log("failed to find th in tbody?");
7906 Roo.log(e.getTarget());
7911 var row = cell.findParent('tr', false, true);
7912 var cellIndex = cell.dom.cellIndex;
7913 var rowIndex = row.dom.rowIndex - 1;
7917 this.fireEvent("row" + name, this, rowIndex, e);
7921 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
7927 onMouseover : function(e, el)
7929 var cell = Roo.get(el);
7935 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7936 cell = cell.findParent('td', false, true);
7939 var row = cell.findParent('tr', false, true);
7940 var cellIndex = cell.dom.cellIndex;
7941 var rowIndex = row.dom.rowIndex - 1; // start from 0
7943 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
7947 onMouseout : function(e, el)
7949 var cell = Roo.get(el);
7955 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7956 cell = cell.findParent('td', false, true);
7959 var row = cell.findParent('tr', false, true);
7960 var cellIndex = cell.dom.cellIndex;
7961 var rowIndex = row.dom.rowIndex - 1; // start from 0
7963 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
7967 onClick : function(e, el)
7969 var cell = Roo.get(el);
7971 if(!cell || (!this.cellSelection && !this.rowSelection)){
7975 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7976 cell = cell.findParent('td', false, true);
7979 if(!cell || typeof(cell) == 'undefined'){
7983 var row = cell.findParent('tr', false, true);
7985 if(!row || typeof(row) == 'undefined'){
7989 var cellIndex = cell.dom.cellIndex;
7990 var rowIndex = this.getRowIndex(row);
7992 // why??? - should these not be based on SelectionModel?
7993 if(this.cellSelection){
7994 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
7997 if(this.rowSelection){
7998 this.fireEvent('rowclick', this, row, rowIndex, e);
8004 onDblClick : function(e,el)
8006 var cell = Roo.get(el);
8008 if(!cell || (!this.cellSelection && !this.rowSelection)){
8012 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8013 cell = cell.findParent('td', false, true);
8016 if(!cell || typeof(cell) == 'undefined'){
8020 var row = cell.findParent('tr', false, true);
8022 if(!row || typeof(row) == 'undefined'){
8026 var cellIndex = cell.dom.cellIndex;
8027 var rowIndex = this.getRowIndex(row);
8029 if(this.cellSelection){
8030 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8033 if(this.rowSelection){
8034 this.fireEvent('rowdblclick', this, row, rowIndex, e);
8038 sort : function(e,el)
8040 var col = Roo.get(el);
8042 if(!col.hasClass('sortable')){
8046 var sort = col.attr('sort');
8049 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8053 this.store.sortInfo = {field : sort, direction : dir};
8056 Roo.log("calling footer first");
8057 this.footer.onClick('first');
8060 this.store.load({ params : { start : 0 } });
8064 renderHeader : function()
8072 this.totalWidth = 0;
8074 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8076 var config = cm.config[i];
8080 cls : 'x-hcol-' + i,
8082 html: cm.getColumnHeader(i)
8087 if(typeof(config.sortable) != 'undefined' && config.sortable){
8089 c.html = '<i class="glyphicon"></i>' + c.html;
8092 // could use BS4 hidden-..-down
8094 if(typeof(config.lgHeader) != 'undefined'){
8095 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8098 if(typeof(config.mdHeader) != 'undefined'){
8099 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8102 if(typeof(config.smHeader) != 'undefined'){
8103 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8106 if(typeof(config.xsHeader) != 'undefined'){
8107 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8114 if(typeof(config.tooltip) != 'undefined'){
8115 c.tooltip = config.tooltip;
8118 if(typeof(config.colspan) != 'undefined'){
8119 c.colspan = config.colspan;
8122 if(typeof(config.hidden) != 'undefined' && config.hidden){
8123 c.style += ' display:none;';
8126 if(typeof(config.dataIndex) != 'undefined'){
8127 c.sort = config.dataIndex;
8132 if(typeof(config.align) != 'undefined' && config.align.length){
8133 c.style += ' text-align:' + config.align + ';';
8136 if(typeof(config.width) != 'undefined'){
8137 c.style += ' width:' + config.width + 'px;';
8138 this.totalWidth += config.width;
8140 this.totalWidth += 100; // assume minimum of 100 per column?
8143 if(typeof(config.cls) != 'undefined'){
8144 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8147 ['xs','sm','md','lg'].map(function(size){
8149 if(typeof(config[size]) == 'undefined'){
8153 if (!config[size]) { // 0 = hidden
8154 // BS 4 '0' is treated as hide that column and below.
8155 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8159 c.cls += ' col-' + size + '-' + config[size] + (
8160 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8172 renderBody : function()
8182 colspan : this.cm.getColumnCount()
8192 renderFooter : function()
8202 colspan : this.cm.getColumnCount()
8216 // Roo.log('ds onload');
8221 var ds = this.store;
8223 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8224 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8225 if (_this.store.sortInfo) {
8227 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8228 e.select('i', true).addClass(['glyphicon-arrow-up']);
8231 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8232 e.select('i', true).addClass(['glyphicon-arrow-down']);
8237 var tbody = this.mainBody;
8239 if(ds.getCount() > 0){
8240 ds.data.each(function(d,rowIndex){
8241 var row = this.renderRow(cm, ds, rowIndex);
8243 tbody.createChild(row);
8247 if(row.cellObjects.length){
8248 Roo.each(row.cellObjects, function(r){
8249 _this.renderCellObject(r);
8256 var tfoot = this.el.select('tfoot', true).first();
8258 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8260 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8262 var total = this.ds.getTotalCount();
8264 if(this.footer.pageSize < total){
8265 this.mainFoot.show();
8269 Roo.each(this.el.select('tbody td', true).elements, function(e){
8270 e.on('mouseover', _this.onMouseover, _this);
8273 Roo.each(this.el.select('tbody td', true).elements, function(e){
8274 e.on('mouseout', _this.onMouseout, _this);
8276 this.fireEvent('rowsrendered', this);
8282 onUpdate : function(ds,record)
8284 this.refreshRow(record);
8288 onRemove : function(ds, record, index, isUpdate){
8289 if(isUpdate !== true){
8290 this.fireEvent("beforerowremoved", this, index, record);
8292 var bt = this.mainBody.dom;
8294 var rows = this.el.select('tbody > tr', true).elements;
8296 if(typeof(rows[index]) != 'undefined'){
8297 bt.removeChild(rows[index].dom);
8300 // if(bt.rows[index]){
8301 // bt.removeChild(bt.rows[index]);
8304 if(isUpdate !== true){
8305 //this.stripeRows(index);
8306 //this.syncRowHeights(index, index);
8308 this.fireEvent("rowremoved", this, index, record);
8312 onAdd : function(ds, records, rowIndex)
8314 //Roo.log('on Add called');
8315 // - note this does not handle multiple adding very well..
8316 var bt = this.mainBody.dom;
8317 for (var i =0 ; i < records.length;i++) {
8318 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8319 //Roo.log(records[i]);
8320 //Roo.log(this.store.getAt(rowIndex+i));
8321 this.insertRow(this.store, rowIndex + i, false);
8328 refreshRow : function(record){
8329 var ds = this.store, index;
8330 if(typeof record == 'number'){
8332 record = ds.getAt(index);
8334 index = ds.indexOf(record);
8336 return; // should not happen - but seems to
8339 this.insertRow(ds, index, true);
8341 this.onRemove(ds, record, index+1, true);
8343 //this.syncRowHeights(index, index);
8345 this.fireEvent("rowupdated", this, index, record);
8348 insertRow : function(dm, rowIndex, isUpdate){
8351 this.fireEvent("beforerowsinserted", this, rowIndex);
8353 //var s = this.getScrollState();
8354 var row = this.renderRow(this.cm, this.store, rowIndex);
8355 // insert before rowIndex..
8356 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8360 if(row.cellObjects.length){
8361 Roo.each(row.cellObjects, function(r){
8362 _this.renderCellObject(r);
8367 this.fireEvent("rowsinserted", this, rowIndex);
8368 //this.syncRowHeights(firstRow, lastRow);
8369 //this.stripeRows(firstRow);
8376 getRowDom : function(rowIndex)
8378 var rows = this.el.select('tbody > tr', true).elements;
8380 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8383 // returns the object tree for a tr..
8386 renderRow : function(cm, ds, rowIndex)
8388 var d = ds.getAt(rowIndex);
8392 cls : 'x-row-' + rowIndex,
8396 var cellObjects = [];
8398 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8399 var config = cm.config[i];
8401 var renderer = cm.getRenderer(i);
8405 if(typeof(renderer) !== 'undefined'){
8406 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8408 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8409 // and are rendered into the cells after the row is rendered - using the id for the element.
8411 if(typeof(value) === 'object'){
8421 rowIndex : rowIndex,
8426 this.fireEvent('rowclass', this, rowcfg);
8430 cls : rowcfg.rowClass + ' x-col-' + i,
8432 html: (typeof(value) === 'object') ? '' : value
8439 if(typeof(config.colspan) != 'undefined'){
8440 td.colspan = config.colspan;
8443 if(typeof(config.hidden) != 'undefined' && config.hidden){
8444 td.style += ' display:none;';
8447 if(typeof(config.align) != 'undefined' && config.align.length){
8448 td.style += ' text-align:' + config.align + ';';
8450 if(typeof(config.valign) != 'undefined' && config.valign.length){
8451 td.style += ' vertical-align:' + config.valign + ';';
8454 if(typeof(config.width) != 'undefined'){
8455 td.style += ' width:' + config.width + 'px;';
8458 if(typeof(config.cursor) != 'undefined'){
8459 td.style += ' cursor:' + config.cursor + ';';
8462 if(typeof(config.cls) != 'undefined'){
8463 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8466 ['xs','sm','md','lg'].map(function(size){
8468 if(typeof(config[size]) == 'undefined'){
8474 if (!config[size]) { // 0 = hidden
8475 // BS 4 '0' is treated as hide that column and below.
8476 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8480 td.cls += ' col-' + size + '-' + config[size] + (
8481 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8491 row.cellObjects = cellObjects;
8499 onBeforeLoad : function()
8508 this.el.select('tbody', true).first().dom.innerHTML = '';
8511 * Show or hide a row.
8512 * @param {Number} rowIndex to show or hide
8513 * @param {Boolean} state hide
8515 setRowVisibility : function(rowIndex, state)
8517 var bt = this.mainBody.dom;
8519 var rows = this.el.select('tbody > tr', true).elements;
8521 if(typeof(rows[rowIndex]) == 'undefined'){
8524 rows[rowIndex].dom.style.display = state ? '' : 'none';
8528 getSelectionModel : function(){
8530 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8532 return this.selModel;
8535 * Render the Roo.bootstrap object from renderder
8537 renderCellObject : function(r)
8541 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8543 var t = r.cfg.render(r.container);
8546 Roo.each(r.cfg.cn, function(c){
8548 container: t.getChildContainer(),
8551 _this.renderCellObject(child);
8556 getRowIndex : function(row)
8560 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8571 * Returns the grid's underlying element = used by panel.Grid
8572 * @return {Element} The element
8574 getGridEl : function(){
8578 * Forces a resize - used by panel.Grid
8579 * @return {Element} The element
8581 autoSize : function()
8583 //var ctr = Roo.get(this.container.dom.parentElement);
8584 var ctr = Roo.get(this.el.dom);
8586 var thd = this.getGridEl().select('thead',true).first();
8587 var tbd = this.getGridEl().select('tbody', true).first();
8588 var tfd = this.getGridEl().select('tfoot', true).first();
8590 var cw = ctr.getWidth();
8594 tbd.setWidth(ctr.getWidth());
8595 // if the body has a max height - and then scrolls - we should perhaps set up the height here
8596 // this needs fixing for various usage - currently only hydra job advers I think..
8598 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8600 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8603 cw = Math.max(cw, this.totalWidth);
8604 this.getGridEl().select('tr',true).setWidth(cw);
8605 // resize 'expandable coloumn?
8607 return; // we doe not have a view in this design..
8610 onBodyScroll: function()
8612 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8614 this.mainHead.setStyle({
8615 'position' : 'relative',
8616 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8622 var scrollHeight = this.mainBody.dom.scrollHeight;
8624 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8626 var height = this.mainBody.getHeight();
8628 if(scrollHeight - height == scrollTop) {
8630 var total = this.ds.getTotalCount();
8632 if(this.footer.cursor + this.footer.pageSize < total){
8634 this.footer.ds.load({
8636 start : this.footer.cursor + this.footer.pageSize,
8637 limit : this.footer.pageSize
8647 onHeaderChange : function()
8649 var header = this.renderHeader();
8650 var table = this.el.select('table', true).first();
8652 this.mainHead.remove();
8653 this.mainHead = table.createChild(header, this.mainBody, false);
8656 onHiddenChange : function(colModel, colIndex, hidden)
8658 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8659 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8661 this.CSS.updateRule(thSelector, "display", "");
8662 this.CSS.updateRule(tdSelector, "display", "");
8665 this.CSS.updateRule(thSelector, "display", "none");
8666 this.CSS.updateRule(tdSelector, "display", "none");
8669 this.onHeaderChange();
8673 setColumnWidth: function(col_index, width)
8675 // width = "md-2 xs-2..."
8676 if(!this.colModel.config[col_index]) {
8680 var w = width.split(" ");
8682 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8684 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8687 for(var j = 0; j < w.length; j++) {
8693 var size_cls = w[j].split("-");
8695 if(!Number.isInteger(size_cls[1] * 1)) {
8699 if(!this.colModel.config[col_index][size_cls[0]]) {
8703 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8707 h_row[0].classList.replace(
8708 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8709 "col-"+size_cls[0]+"-"+size_cls[1]
8712 for(var i = 0; i < rows.length; i++) {
8714 var size_cls = w[j].split("-");
8716 if(!Number.isInteger(size_cls[1] * 1)) {
8720 if(!this.colModel.config[col_index][size_cls[0]]) {
8724 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8728 rows[i].classList.replace(
8729 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8730 "col-"+size_cls[0]+"-"+size_cls[1]
8734 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8749 * @class Roo.bootstrap.TableCell
8750 * @extends Roo.bootstrap.Component
8751 * Bootstrap TableCell class
8752 * @cfg {String} html cell contain text
8753 * @cfg {String} cls cell class
8754 * @cfg {String} tag cell tag (td|th) default td
8755 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8756 * @cfg {String} align Aligns the content in a cell
8757 * @cfg {String} axis Categorizes cells
8758 * @cfg {String} bgcolor Specifies the background color of a cell
8759 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8760 * @cfg {Number} colspan Specifies the number of columns a cell should span
8761 * @cfg {String} headers Specifies one or more header cells a cell is related to
8762 * @cfg {Number} height Sets the height of a cell
8763 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8764 * @cfg {Number} rowspan Sets the number of rows a cell should span
8765 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8766 * @cfg {String} valign Vertical aligns the content in a cell
8767 * @cfg {Number} width Specifies the width of a cell
8770 * Create a new TableCell
8771 * @param {Object} config The config object
8774 Roo.bootstrap.TableCell = function(config){
8775 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8778 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
8798 getAutoCreate : function(){
8799 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8819 cfg.align=this.align
8825 cfg.bgcolor=this.bgcolor
8828 cfg.charoff=this.charoff
8831 cfg.colspan=this.colspan
8834 cfg.headers=this.headers
8837 cfg.height=this.height
8840 cfg.nowrap=this.nowrap
8843 cfg.rowspan=this.rowspan
8846 cfg.scope=this.scope
8849 cfg.valign=this.valign
8852 cfg.width=this.width
8871 * @class Roo.bootstrap.TableRow
8872 * @extends Roo.bootstrap.Component
8873 * Bootstrap TableRow class
8874 * @cfg {String} cls row class
8875 * @cfg {String} align Aligns the content in a table row
8876 * @cfg {String} bgcolor Specifies a background color for a table row
8877 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8878 * @cfg {String} valign Vertical aligns the content in a table row
8881 * Create a new TableRow
8882 * @param {Object} config The config object
8885 Roo.bootstrap.TableRow = function(config){
8886 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
8889 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
8897 getAutoCreate : function(){
8898 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
8908 cfg.align = this.align;
8911 cfg.bgcolor = this.bgcolor;
8914 cfg.charoff = this.charoff;
8917 cfg.valign = this.valign;
8935 * @class Roo.bootstrap.TableBody
8936 * @extends Roo.bootstrap.Component
8937 * Bootstrap TableBody class
8938 * @cfg {String} cls element class
8939 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
8940 * @cfg {String} align Aligns the content inside the element
8941 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
8942 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
8945 * Create a new TableBody
8946 * @param {Object} config The config object
8949 Roo.bootstrap.TableBody = function(config){
8950 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
8953 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
8961 getAutoCreate : function(){
8962 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
8976 cfg.align = this.align;
8979 cfg.charoff = this.charoff;
8982 cfg.valign = this.valign;
8989 // initEvents : function()
8996 // this.store = Roo.factory(this.store, Roo.data);
8997 // this.store.on('load', this.onLoad, this);
8999 // this.store.load();
9003 // onLoad: function ()
9005 // this.fireEvent('load', this);
9015 * Ext JS Library 1.1.1
9016 * Copyright(c) 2006-2007, Ext JS, LLC.
9018 * Originally Released Under LGPL - original licence link has changed is not relivant.
9021 * <script type="text/javascript">
9024 // as we use this in bootstrap.
9025 Roo.namespace('Roo.form');
9027 * @class Roo.form.Action
9028 * Internal Class used to handle form actions
9030 * @param {Roo.form.BasicForm} el The form element or its id
9031 * @param {Object} config Configuration options
9036 // define the action interface
9037 Roo.form.Action = function(form, options){
9039 this.options = options || {};
9042 * Client Validation Failed
9045 Roo.form.Action.CLIENT_INVALID = 'client';
9047 * Server Validation Failed
9050 Roo.form.Action.SERVER_INVALID = 'server';
9052 * Connect to Server Failed
9055 Roo.form.Action.CONNECT_FAILURE = 'connect';
9057 * Reading Data from Server Failed
9060 Roo.form.Action.LOAD_FAILURE = 'load';
9062 Roo.form.Action.prototype = {
9064 failureType : undefined,
9065 response : undefined,
9069 run : function(options){
9074 success : function(response){
9079 handleResponse : function(response){
9083 // default connection failure
9084 failure : function(response){
9086 this.response = response;
9087 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9088 this.form.afterAction(this, false);
9091 processResponse : function(response){
9092 this.response = response;
9093 if(!response.responseText){
9096 this.result = this.handleResponse(response);
9100 // utility functions used internally
9101 getUrl : function(appendParams){
9102 var url = this.options.url || this.form.url || this.form.el.dom.action;
9104 var p = this.getParams();
9106 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9112 getMethod : function(){
9113 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9116 getParams : function(){
9117 var bp = this.form.baseParams;
9118 var p = this.options.params;
9120 if(typeof p == "object"){
9121 p = Roo.urlEncode(Roo.applyIf(p, bp));
9122 }else if(typeof p == 'string' && bp){
9123 p += '&' + Roo.urlEncode(bp);
9126 p = Roo.urlEncode(bp);
9131 createCallback : function(){
9133 success: this.success,
9134 failure: this.failure,
9136 timeout: (this.form.timeout*1000),
9137 upload: this.form.fileUpload ? this.success : undefined
9142 Roo.form.Action.Submit = function(form, options){
9143 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9146 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9149 haveProgress : false,
9150 uploadComplete : false,
9152 // uploadProgress indicator.
9153 uploadProgress : function()
9155 if (!this.form.progressUrl) {
9159 if (!this.haveProgress) {
9160 Roo.MessageBox.progress("Uploading", "Uploading");
9162 if (this.uploadComplete) {
9163 Roo.MessageBox.hide();
9167 this.haveProgress = true;
9169 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9171 var c = new Roo.data.Connection();
9173 url : this.form.progressUrl,
9178 success : function(req){
9179 //console.log(data);
9183 rdata = Roo.decode(req.responseText)
9185 Roo.log("Invalid data from server..");
9189 if (!rdata || !rdata.success) {
9191 Roo.MessageBox.alert(Roo.encode(rdata));
9194 var data = rdata.data;
9196 if (this.uploadComplete) {
9197 Roo.MessageBox.hide();
9202 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9203 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9206 this.uploadProgress.defer(2000,this);
9209 failure: function(data) {
9210 Roo.log('progress url failed ');
9221 // run get Values on the form, so it syncs any secondary forms.
9222 this.form.getValues();
9224 var o = this.options;
9225 var method = this.getMethod();
9226 var isPost = method == 'POST';
9227 if(o.clientValidation === false || this.form.isValid()){
9229 if (this.form.progressUrl) {
9230 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9231 (new Date() * 1) + '' + Math.random());
9236 Roo.Ajax.request(Roo.apply(this.createCallback(), {
9237 form:this.form.el.dom,
9238 url:this.getUrl(!isPost),
9240 params:isPost ? this.getParams() : null,
9241 isUpload: this.form.fileUpload,
9242 formData : this.form.formData
9245 this.uploadProgress();
9247 }else if (o.clientValidation !== false){ // client validation failed
9248 this.failureType = Roo.form.Action.CLIENT_INVALID;
9249 this.form.afterAction(this, false);
9253 success : function(response)
9255 this.uploadComplete= true;
9256 if (this.haveProgress) {
9257 Roo.MessageBox.hide();
9261 var result = this.processResponse(response);
9262 if(result === true || result.success){
9263 this.form.afterAction(this, true);
9267 this.form.markInvalid(result.errors);
9268 this.failureType = Roo.form.Action.SERVER_INVALID;
9270 this.form.afterAction(this, false);
9272 failure : function(response)
9274 this.uploadComplete= true;
9275 if (this.haveProgress) {
9276 Roo.MessageBox.hide();
9279 this.response = response;
9280 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9281 this.form.afterAction(this, false);
9284 handleResponse : function(response){
9285 if(this.form.errorReader){
9286 var rs = this.form.errorReader.read(response);
9289 for(var i = 0, len = rs.records.length; i < len; i++) {
9290 var r = rs.records[i];
9294 if(errors.length < 1){
9298 success : rs.success,
9304 ret = Roo.decode(response.responseText);
9308 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9318 Roo.form.Action.Load = function(form, options){
9319 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9320 this.reader = this.form.reader;
9323 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9328 Roo.Ajax.request(Roo.apply(
9329 this.createCallback(), {
9330 method:this.getMethod(),
9331 url:this.getUrl(false),
9332 params:this.getParams()
9336 success : function(response){
9338 var result = this.processResponse(response);
9339 if(result === true || !result.success || !result.data){
9340 this.failureType = Roo.form.Action.LOAD_FAILURE;
9341 this.form.afterAction(this, false);
9344 this.form.clearInvalid();
9345 this.form.setValues(result.data);
9346 this.form.afterAction(this, true);
9349 handleResponse : function(response){
9350 if(this.form.reader){
9351 var rs = this.form.reader.read(response);
9352 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9354 success : rs.success,
9358 return Roo.decode(response.responseText);
9362 Roo.form.Action.ACTION_TYPES = {
9363 'load' : Roo.form.Action.Load,
9364 'submit' : Roo.form.Action.Submit
9373 * @class Roo.bootstrap.Form
9374 * @extends Roo.bootstrap.Component
9375 * Bootstrap Form class
9376 * @cfg {String} method GET | POST (default POST)
9377 * @cfg {String} labelAlign top | left (default top)
9378 * @cfg {String} align left | right - for navbars
9379 * @cfg {Boolean} loadMask load mask when submit (default true)
9384 * @param {Object} config The config object
9388 Roo.bootstrap.Form = function(config){
9390 Roo.bootstrap.Form.superclass.constructor.call(this, config);
9392 Roo.bootstrap.Form.popover.apply();
9396 * @event clientvalidation
9397 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9398 * @param {Form} this
9399 * @param {Boolean} valid true if the form has passed client-side validation
9401 clientvalidation: true,
9403 * @event beforeaction
9404 * Fires before any action is performed. Return false to cancel the action.
9405 * @param {Form} this
9406 * @param {Action} action The action to be performed
9410 * @event actionfailed
9411 * Fires when an action fails.
9412 * @param {Form} this
9413 * @param {Action} action The action that failed
9415 actionfailed : true,
9417 * @event actioncomplete
9418 * Fires when an action is completed.
9419 * @param {Form} this
9420 * @param {Action} action The action that completed
9422 actioncomplete : true
9426 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
9429 * @cfg {String} method
9430 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9435 * The URL to use for form actions if one isn't supplied in the action options.
9438 * @cfg {Boolean} fileUpload
9439 * Set to true if this form is a file upload.
9443 * @cfg {Object} baseParams
9444 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9448 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9452 * @cfg {Sting} align (left|right) for navbar forms
9457 activeAction : null,
9460 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9461 * element by passing it or its id or mask the form itself by passing in true.
9464 waitMsgTarget : false,
9469 * @cfg {Boolean} errorMask (true|false) default false
9474 * @cfg {Number} maskOffset Default 100
9479 * @cfg {Boolean} maskBody
9483 getAutoCreate : function(){
9487 method : this.method || 'POST',
9488 id : this.id || Roo.id(),
9491 if (this.parent().xtype.match(/^Nav/)) {
9492 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9496 if (this.labelAlign == 'left' ) {
9497 cfg.cls += ' form-horizontal';
9503 initEvents : function()
9505 this.el.on('submit', this.onSubmit, this);
9506 // this was added as random key presses on the form where triggering form submit.
9507 this.el.on('keypress', function(e) {
9508 if (e.getCharCode() != 13) {
9511 // we might need to allow it for textareas.. and some other items.
9512 // check e.getTarget().
9514 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9518 Roo.log("keypress blocked");
9526 onSubmit : function(e){
9531 * Returns true if client-side validation on the form is successful.
9534 isValid : function(){
9535 var items = this.getItems();
9539 items.each(function(f){
9545 Roo.log('invalid field: ' + f.name);
9549 if(!target && f.el.isVisible(true)){
9555 if(this.errorMask && !valid){
9556 Roo.bootstrap.Form.popover.mask(this, target);
9563 * Returns true if any fields in this form have changed since their original load.
9566 isDirty : function(){
9568 var items = this.getItems();
9569 items.each(function(f){
9579 * Performs a predefined action (submit or load) or custom actions you define on this form.
9580 * @param {String} actionName The name of the action type
9581 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
9582 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9583 * accept other config options):
9585 Property Type Description
9586 ---------------- --------------- ----------------------------------------------------------------------------------
9587 url String The url for the action (defaults to the form's url)
9588 method String The form method to use (defaults to the form's method, or POST if not defined)
9589 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
9590 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
9591 validate the form on the client (defaults to false)
9593 * @return {BasicForm} this
9595 doAction : function(action, options){
9596 if(typeof action == 'string'){
9597 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9599 if(this.fireEvent('beforeaction', this, action) !== false){
9600 this.beforeAction(action);
9601 action.run.defer(100, action);
9607 beforeAction : function(action){
9608 var o = action.options;
9613 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9615 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9618 // not really supported yet.. ??
9620 //if(this.waitMsgTarget === true){
9621 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9622 //}else if(this.waitMsgTarget){
9623 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9624 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9626 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9632 afterAction : function(action, success){
9633 this.activeAction = null;
9634 var o = action.options;
9639 Roo.get(document.body).unmask();
9645 //if(this.waitMsgTarget === true){
9646 // this.el.unmask();
9647 //}else if(this.waitMsgTarget){
9648 // this.waitMsgTarget.unmask();
9650 // Roo.MessageBox.updateProgress(1);
9651 // Roo.MessageBox.hide();
9658 Roo.callback(o.success, o.scope, [this, action]);
9659 this.fireEvent('actioncomplete', this, action);
9663 // failure condition..
9664 // we have a scenario where updates need confirming.
9665 // eg. if a locking scenario exists..
9666 // we look for { errors : { needs_confirm : true }} in the response.
9668 (typeof(action.result) != 'undefined') &&
9669 (typeof(action.result.errors) != 'undefined') &&
9670 (typeof(action.result.errors.needs_confirm) != 'undefined')
9673 Roo.log("not supported yet");
9676 Roo.MessageBox.confirm(
9677 "Change requires confirmation",
9678 action.result.errorMsg,
9683 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
9693 Roo.callback(o.failure, o.scope, [this, action]);
9694 // show an error message if no failed handler is set..
9695 if (!this.hasListener('actionfailed')) {
9696 Roo.log("need to add dialog support");
9698 Roo.MessageBox.alert("Error",
9699 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9700 action.result.errorMsg :
9701 "Saving Failed, please check your entries or try again"
9706 this.fireEvent('actionfailed', this, action);
9711 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9712 * @param {String} id The value to search for
9715 findField : function(id){
9716 var items = this.getItems();
9717 var field = items.get(id);
9719 items.each(function(f){
9720 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9727 return field || null;
9730 * Mark fields in this form invalid in bulk.
9731 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9732 * @return {BasicForm} this
9734 markInvalid : function(errors){
9735 if(errors instanceof Array){
9736 for(var i = 0, len = errors.length; i < len; i++){
9737 var fieldError = errors[i];
9738 var f = this.findField(fieldError.id);
9740 f.markInvalid(fieldError.msg);
9746 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9747 field.markInvalid(errors[id]);
9751 //Roo.each(this.childForms || [], function (f) {
9752 // f.markInvalid(errors);
9759 * Set values for fields in this form in bulk.
9760 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9761 * @return {BasicForm} this
9763 setValues : function(values){
9764 if(values instanceof Array){ // array of objects
9765 for(var i = 0, len = values.length; i < len; i++){
9767 var f = this.findField(v.id);
9769 f.setValue(v.value);
9770 if(this.trackResetOnLoad){
9771 f.originalValue = f.getValue();
9775 }else{ // object hash
9778 if(typeof values[id] != 'function' && (field = this.findField(id))){
9780 if (field.setFromData &&
9782 field.displayField &&
9783 // combos' with local stores can
9784 // be queried via setValue()
9785 // to set their value..
9786 (field.store && !field.store.isLocal)
9790 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9791 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9792 field.setFromData(sd);
9794 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9796 field.setFromData(values);
9799 field.setValue(values[id]);
9803 if(this.trackResetOnLoad){
9804 field.originalValue = field.getValue();
9810 //Roo.each(this.childForms || [], function (f) {
9811 // f.setValues(values);
9818 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9819 * they are returned as an array.
9820 * @param {Boolean} asString
9823 getValues : function(asString){
9824 //if (this.childForms) {
9825 // copy values from the child forms
9826 // Roo.each(this.childForms, function (f) {
9827 // this.setValues(f.getValues());
9833 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9834 if(asString === true){
9837 return Roo.urlDecode(fs);
9841 * Returns the fields in this form as an object with key/value pairs.
9842 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9845 getFieldValues : function(with_hidden)
9847 var items = this.getItems();
9849 items.each(function(f){
9855 var v = f.getValue();
9857 if (f.inputType =='radio') {
9858 if (typeof(ret[f.getName()]) == 'undefined') {
9859 ret[f.getName()] = ''; // empty..
9862 if (!f.el.dom.checked) {
9870 if(f.xtype == 'MoneyField'){
9871 ret[f.currencyName] = f.getCurrency();
9874 // not sure if this supported any more..
9875 if ((typeof(v) == 'object') && f.getRawValue) {
9876 v = f.getRawValue() ; // dates..
9878 // combo boxes where name != hiddenName...
9879 if (f.name !== false && f.name != '' && f.name != f.getName()) {
9880 ret[f.name] = f.getRawValue();
9882 ret[f.getName()] = v;
9889 * Clears all invalid messages in this form.
9890 * @return {BasicForm} this
9892 clearInvalid : function(){
9893 var items = this.getItems();
9895 items.each(function(f){
9904 * @return {BasicForm} this
9907 var items = this.getItems();
9908 items.each(function(f){
9912 Roo.each(this.childForms || [], function (f) {
9920 getItems : function()
9922 var r=new Roo.util.MixedCollection(false, function(o){
9923 return o.id || (o.id = Roo.id());
9925 var iter = function(el) {
9932 Roo.each(el.items,function(e) {
9941 hideFields : function(items)
9943 Roo.each(items, function(i){
9945 var f = this.findField(i);
9956 showFields : function(items)
9958 Roo.each(items, function(i){
9960 var f = this.findField(i);
9973 Roo.apply(Roo.bootstrap.Form, {
10000 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10001 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10002 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10003 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10006 this.maskEl.top.enableDisplayMode("block");
10007 this.maskEl.left.enableDisplayMode("block");
10008 this.maskEl.bottom.enableDisplayMode("block");
10009 this.maskEl.right.enableDisplayMode("block");
10011 this.toolTip = new Roo.bootstrap.Tooltip({
10012 cls : 'roo-form-error-popover',
10014 'left' : ['r-l', [-2,0], 'right'],
10015 'right' : ['l-r', [2,0], 'left'],
10016 'bottom' : ['tl-bl', [0,2], 'top'],
10017 'top' : [ 'bl-tl', [0,-2], 'bottom']
10021 this.toolTip.render(Roo.get(document.body));
10023 this.toolTip.el.enableDisplayMode("block");
10025 Roo.get(document.body).on('click', function(){
10029 Roo.get(document.body).on('touchstart', function(){
10033 this.isApplied = true
10036 mask : function(form, target)
10040 this.target = target;
10042 if(!this.form.errorMask || !target.el){
10046 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10048 Roo.log(scrollable);
10050 var ot = this.target.el.calcOffsetsTo(scrollable);
10052 var scrollTo = ot[1] - this.form.maskOffset;
10054 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10056 scrollable.scrollTo('top', scrollTo);
10058 var box = this.target.el.getBox();
10060 var zIndex = Roo.bootstrap.Modal.zIndex++;
10063 this.maskEl.top.setStyle('position', 'absolute');
10064 this.maskEl.top.setStyle('z-index', zIndex);
10065 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10066 this.maskEl.top.setLeft(0);
10067 this.maskEl.top.setTop(0);
10068 this.maskEl.top.show();
10070 this.maskEl.left.setStyle('position', 'absolute');
10071 this.maskEl.left.setStyle('z-index', zIndex);
10072 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10073 this.maskEl.left.setLeft(0);
10074 this.maskEl.left.setTop(box.y - this.padding);
10075 this.maskEl.left.show();
10077 this.maskEl.bottom.setStyle('position', 'absolute');
10078 this.maskEl.bottom.setStyle('z-index', zIndex);
10079 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10080 this.maskEl.bottom.setLeft(0);
10081 this.maskEl.bottom.setTop(box.bottom + this.padding);
10082 this.maskEl.bottom.show();
10084 this.maskEl.right.setStyle('position', 'absolute');
10085 this.maskEl.right.setStyle('z-index', zIndex);
10086 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10087 this.maskEl.right.setLeft(box.right + this.padding);
10088 this.maskEl.right.setTop(box.y - this.padding);
10089 this.maskEl.right.show();
10091 this.toolTip.bindEl = this.target.el;
10093 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10095 var tip = this.target.blankText;
10097 if(this.target.getValue() !== '' ) {
10099 if (this.target.invalidText.length) {
10100 tip = this.target.invalidText;
10101 } else if (this.target.regexText.length){
10102 tip = this.target.regexText;
10106 this.toolTip.show(tip);
10108 this.intervalID = window.setInterval(function() {
10109 Roo.bootstrap.Form.popover.unmask();
10112 window.onwheel = function(){ return false;};
10114 (function(){ this.isMasked = true; }).defer(500, this);
10118 unmask : function()
10120 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10124 this.maskEl.top.setStyle('position', 'absolute');
10125 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10126 this.maskEl.top.hide();
10128 this.maskEl.left.setStyle('position', 'absolute');
10129 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10130 this.maskEl.left.hide();
10132 this.maskEl.bottom.setStyle('position', 'absolute');
10133 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10134 this.maskEl.bottom.hide();
10136 this.maskEl.right.setStyle('position', 'absolute');
10137 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10138 this.maskEl.right.hide();
10140 this.toolTip.hide();
10142 this.toolTip.el.hide();
10144 window.onwheel = function(){ return true;};
10146 if(this.intervalID){
10147 window.clearInterval(this.intervalID);
10148 this.intervalID = false;
10151 this.isMasked = false;
10161 * Ext JS Library 1.1.1
10162 * Copyright(c) 2006-2007, Ext JS, LLC.
10164 * Originally Released Under LGPL - original licence link has changed is not relivant.
10167 * <script type="text/javascript">
10170 * @class Roo.form.VTypes
10171 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10174 Roo.form.VTypes = function(){
10175 // closure these in so they are only created once.
10176 var alpha = /^[a-zA-Z_]+$/;
10177 var alphanum = /^[a-zA-Z0-9_]+$/;
10178 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10179 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10181 // All these messages and functions are configurable
10184 * The function used to validate email addresses
10185 * @param {String} value The email address
10187 'email' : function(v){
10188 return email.test(v);
10191 * The error text to display when the email validation function returns false
10194 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10196 * The keystroke filter mask to be applied on email input
10199 'emailMask' : /[a-z0-9_\.\-@]/i,
10202 * The function used to validate URLs
10203 * @param {String} value The URL
10205 'url' : function(v){
10206 return url.test(v);
10209 * The error text to display when the url validation function returns false
10212 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10215 * The function used to validate alpha values
10216 * @param {String} value The value
10218 'alpha' : function(v){
10219 return alpha.test(v);
10222 * The error text to display when the alpha validation function returns false
10225 'alphaText' : 'This field should only contain letters and _',
10227 * The keystroke filter mask to be applied on alpha input
10230 'alphaMask' : /[a-z_]/i,
10233 * The function used to validate alphanumeric values
10234 * @param {String} value The value
10236 'alphanum' : function(v){
10237 return alphanum.test(v);
10240 * The error text to display when the alphanumeric validation function returns false
10243 'alphanumText' : 'This field should only contain letters, numbers and _',
10245 * The keystroke filter mask to be applied on alphanumeric input
10248 'alphanumMask' : /[a-z0-9_]/i
10258 * @class Roo.bootstrap.Input
10259 * @extends Roo.bootstrap.Component
10260 * Bootstrap Input class
10261 * @cfg {Boolean} disabled is it disabled
10262 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
10263 * @cfg {String} name name of the input
10264 * @cfg {string} fieldLabel - the label associated
10265 * @cfg {string} placeholder - placeholder to put in text.
10266 * @cfg {string} before - input group add on before
10267 * @cfg {string} after - input group add on after
10268 * @cfg {string} size - (lg|sm) or leave empty..
10269 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10270 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10271 * @cfg {Number} md colspan out of 12 for computer-sized screens
10272 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10273 * @cfg {string} value default value of the input
10274 * @cfg {Number} labelWidth set the width of label
10275 * @cfg {Number} labellg set the width of label (1-12)
10276 * @cfg {Number} labelmd set the width of label (1-12)
10277 * @cfg {Number} labelsm set the width of label (1-12)
10278 * @cfg {Number} labelxs set the width of label (1-12)
10279 * @cfg {String} labelAlign (top|left)
10280 * @cfg {Boolean} readOnly Specifies that the field should be read-only
10281 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10282 * @cfg {String} indicatorpos (left|right) default left
10283 * @cfg {String} capture (user|camera) use for file input only. (default empty)
10284 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10285 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10287 * @cfg {String} align (left|center|right) Default left
10288 * @cfg {Boolean} forceFeedback (true|false) Default false
10291 * Create a new Input
10292 * @param {Object} config The config object
10295 Roo.bootstrap.Input = function(config){
10297 Roo.bootstrap.Input.superclass.constructor.call(this, config);
10302 * Fires when this field receives input focus.
10303 * @param {Roo.form.Field} this
10308 * Fires when this field loses input focus.
10309 * @param {Roo.form.Field} this
10313 * @event specialkey
10314 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
10315 * {@link Roo.EventObject#getKey} to determine which key was pressed.
10316 * @param {Roo.form.Field} this
10317 * @param {Roo.EventObject} e The event object
10322 * Fires just before the field blurs if the field value has changed.
10323 * @param {Roo.form.Field} this
10324 * @param {Mixed} newValue The new value
10325 * @param {Mixed} oldValue The original value
10330 * Fires after the field has been marked as invalid.
10331 * @param {Roo.form.Field} this
10332 * @param {String} msg The validation message
10337 * Fires after the field has been validated with no errors.
10338 * @param {Roo.form.Field} this
10343 * Fires after the key up
10344 * @param {Roo.form.Field} this
10345 * @param {Roo.EventObject} e The event Object
10351 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
10353 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10354 automatic validation (defaults to "keyup").
10356 validationEvent : "keyup",
10358 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10360 validateOnBlur : true,
10362 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10364 validationDelay : 250,
10366 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10368 focusClass : "x-form-focus", // not needed???
10372 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10374 invalidClass : "has-warning",
10377 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10379 validClass : "has-success",
10382 * @cfg {Boolean} hasFeedback (true|false) default true
10384 hasFeedback : true,
10387 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10389 invalidFeedbackClass : "glyphicon-warning-sign",
10392 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10394 validFeedbackClass : "glyphicon-ok",
10397 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10399 selectOnFocus : false,
10402 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10406 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10411 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10413 disableKeyFilter : false,
10416 * @cfg {Boolean} disabled True to disable the field (defaults to false).
10420 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10424 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10426 blankText : "Please complete this mandatory field",
10429 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10433 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10435 maxLength : Number.MAX_VALUE,
10437 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10439 minLengthText : "The minimum length for this field is {0}",
10441 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10443 maxLengthText : "The maximum length for this field is {0}",
10447 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10448 * If available, this function will be called only after the basic validators all return true, and will be passed the
10449 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10453 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10454 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10455 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
10459 * @cfg {String} regexText -- Depricated - use Invalid Text
10464 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10470 autocomplete: false,
10474 inputType : 'text',
10477 placeholder: false,
10482 preventMark: false,
10483 isFormField : true,
10486 labelAlign : false,
10489 formatedValue : false,
10490 forceFeedback : false,
10492 indicatorpos : 'left',
10502 parentLabelAlign : function()
10505 while (parent.parent()) {
10506 parent = parent.parent();
10507 if (typeof(parent.labelAlign) !='undefined') {
10508 return parent.labelAlign;
10515 getAutoCreate : function()
10517 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10523 if(this.inputType != 'hidden'){
10524 cfg.cls = 'form-group' //input-group
10530 type : this.inputType,
10531 value : this.value,
10532 cls : 'form-control',
10533 placeholder : this.placeholder || '',
10534 autocomplete : this.autocomplete || 'new-password'
10536 if (this.inputType == 'file') {
10537 input.style = 'overflow:hidden'; // why not in CSS?
10540 if(this.capture.length){
10541 input.capture = this.capture;
10544 if(this.accept.length){
10545 input.accept = this.accept + "/*";
10549 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10552 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10553 input.maxLength = this.maxLength;
10556 if (this.disabled) {
10557 input.disabled=true;
10560 if (this.readOnly) {
10561 input.readonly=true;
10565 input.name = this.name;
10569 input.cls += ' input-' + this.size;
10573 ['xs','sm','md','lg'].map(function(size){
10574 if (settings[size]) {
10575 cfg.cls += ' col-' + size + '-' + settings[size];
10579 var inputblock = input;
10583 cls: 'glyphicon form-control-feedback'
10586 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10589 cls : 'has-feedback',
10597 if (this.before || this.after) {
10600 cls : 'input-group',
10604 if (this.before && typeof(this.before) == 'string') {
10606 inputblock.cn.push({
10608 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10612 if (this.before && typeof(this.before) == 'object') {
10613 this.before = Roo.factory(this.before);
10615 inputblock.cn.push({
10617 cls : 'roo-input-before input-group-prepend input-group-' +
10618 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10622 inputblock.cn.push(input);
10624 if (this.after && typeof(this.after) == 'string') {
10625 inputblock.cn.push({
10627 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10631 if (this.after && typeof(this.after) == 'object') {
10632 this.after = Roo.factory(this.after);
10634 inputblock.cn.push({
10636 cls : 'roo-input-after input-group-append input-group-' +
10637 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10641 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10642 inputblock.cls += ' has-feedback';
10643 inputblock.cn.push(feedback);
10648 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10649 tooltip : 'This field is required'
10651 if (this.allowBlank ) {
10652 indicator.style = this.allowBlank ? ' display:none' : '';
10654 if (align ==='left' && this.fieldLabel.length) {
10656 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
10663 cls : 'control-label col-form-label',
10664 html : this.fieldLabel
10675 var labelCfg = cfg.cn[1];
10676 var contentCfg = cfg.cn[2];
10678 if(this.indicatorpos == 'right'){
10683 cls : 'control-label col-form-label',
10687 html : this.fieldLabel
10701 labelCfg = cfg.cn[0];
10702 contentCfg = cfg.cn[1];
10706 if(this.labelWidth > 12){
10707 labelCfg.style = "width: " + this.labelWidth + 'px';
10710 if(this.labelWidth < 13 && this.labelmd == 0){
10711 this.labelmd = this.labelWidth;
10714 if(this.labellg > 0){
10715 labelCfg.cls += ' col-lg-' + this.labellg;
10716 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10719 if(this.labelmd > 0){
10720 labelCfg.cls += ' col-md-' + this.labelmd;
10721 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10724 if(this.labelsm > 0){
10725 labelCfg.cls += ' col-sm-' + this.labelsm;
10726 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10729 if(this.labelxs > 0){
10730 labelCfg.cls += ' col-xs-' + this.labelxs;
10731 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10735 } else if ( this.fieldLabel.length) {
10742 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10743 tooltip : 'This field is required',
10744 style : this.allowBlank ? ' display:none' : ''
10748 //cls : 'input-group-addon',
10749 html : this.fieldLabel
10757 if(this.indicatorpos == 'right'){
10762 //cls : 'input-group-addon',
10763 html : this.fieldLabel
10768 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10769 tooltip : 'This field is required',
10770 style : this.allowBlank ? ' display:none' : ''
10790 if (this.parentType === 'Navbar' && this.parent().bar) {
10791 cfg.cls += ' navbar-form';
10794 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10795 // on BS4 we do this only if not form
10796 cfg.cls += ' navbar-form';
10804 * return the real input element.
10806 inputEl: function ()
10808 return this.el.select('input.form-control',true).first();
10811 tooltipEl : function()
10813 return this.inputEl();
10816 indicatorEl : function()
10818 if (Roo.bootstrap.version == 4) {
10819 return false; // not enabled in v4 yet.
10822 var indicator = this.el.select('i.roo-required-indicator',true).first();
10832 setDisabled : function(v)
10834 var i = this.inputEl().dom;
10836 i.removeAttribute('disabled');
10840 i.setAttribute('disabled','true');
10842 initEvents : function()
10845 this.inputEl().on("keydown" , this.fireKey, this);
10846 this.inputEl().on("focus", this.onFocus, this);
10847 this.inputEl().on("blur", this.onBlur, this);
10849 this.inputEl().relayEvent('keyup', this);
10851 this.indicator = this.indicatorEl();
10853 if(this.indicator){
10854 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
10857 // reference to original value for reset
10858 this.originalValue = this.getValue();
10859 //Roo.form.TextField.superclass.initEvents.call(this);
10860 if(this.validationEvent == 'keyup'){
10861 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
10862 this.inputEl().on('keyup', this.filterValidation, this);
10864 else if(this.validationEvent !== false){
10865 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
10868 if(this.selectOnFocus){
10869 this.on("focus", this.preFocus, this);
10872 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
10873 this.inputEl().on("keypress", this.filterKeys, this);
10875 this.inputEl().relayEvent('keypress', this);
10878 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
10879 this.el.on("click", this.autoSize, this);
10882 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
10883 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
10886 if (typeof(this.before) == 'object') {
10887 this.before.render(this.el.select('.roo-input-before',true).first());
10889 if (typeof(this.after) == 'object') {
10890 this.after.render(this.el.select('.roo-input-after',true).first());
10893 this.inputEl().on('change', this.onChange, this);
10896 filterValidation : function(e){
10897 if(!e.isNavKeyPress()){
10898 this.validationTask.delay(this.validationDelay);
10902 * Validates the field value
10903 * @return {Boolean} True if the value is valid, else false
10905 validate : function(){
10906 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
10907 if(this.disabled || this.validateValue(this.getRawValue())){
10912 this.markInvalid();
10918 * Validates a value according to the field's validation rules and marks the field as invalid
10919 * if the validation fails
10920 * @param {Mixed} value The value to validate
10921 * @return {Boolean} True if the value is valid, else false
10923 validateValue : function(value)
10925 if(this.getVisibilityEl().hasClass('hidden')){
10929 if(value.length < 1) { // if it's blank
10930 if(this.allowBlank){
10936 if(value.length < this.minLength){
10939 if(value.length > this.maxLength){
10943 var vt = Roo.form.VTypes;
10944 if(!vt[this.vtype](value, this)){
10948 if(typeof this.validator == "function"){
10949 var msg = this.validator(value);
10953 if (typeof(msg) == 'string') {
10954 this.invalidText = msg;
10958 if(this.regex && !this.regex.test(value)){
10966 fireKey : function(e){
10967 //Roo.log('field ' + e.getKey());
10968 if(e.isNavKeyPress()){
10969 this.fireEvent("specialkey", this, e);
10972 focus : function (selectText){
10974 this.inputEl().focus();
10975 if(selectText === true){
10976 this.inputEl().dom.select();
10982 onFocus : function(){
10983 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10984 // this.el.addClass(this.focusClass);
10986 if(!this.hasFocus){
10987 this.hasFocus = true;
10988 this.startValue = this.getValue();
10989 this.fireEvent("focus", this);
10993 beforeBlur : Roo.emptyFn,
10997 onBlur : function(){
10999 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11000 //this.el.removeClass(this.focusClass);
11002 this.hasFocus = false;
11003 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11006 var v = this.getValue();
11007 if(String(v) !== String(this.startValue)){
11008 this.fireEvent('change', this, v, this.startValue);
11010 this.fireEvent("blur", this);
11013 onChange : function(e)
11015 var v = this.getValue();
11016 if(String(v) !== String(this.startValue)){
11017 this.fireEvent('change', this, v, this.startValue);
11023 * Resets the current field value to the originally loaded value and clears any validation messages
11025 reset : function(){
11026 this.setValue(this.originalValue);
11030 * Returns the name of the field
11031 * @return {Mixed} name The name field
11033 getName: function(){
11037 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
11038 * @return {Mixed} value The field value
11040 getValue : function(){
11042 var v = this.inputEl().getValue();
11047 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
11048 * @return {Mixed} value The field value
11050 getRawValue : function(){
11051 var v = this.inputEl().getValue();
11057 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
11058 * @param {Mixed} value The value to set
11060 setRawValue : function(v){
11061 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11064 selectText : function(start, end){
11065 var v = this.getRawValue();
11067 start = start === undefined ? 0 : start;
11068 end = end === undefined ? v.length : end;
11069 var d = this.inputEl().dom;
11070 if(d.setSelectionRange){
11071 d.setSelectionRange(start, end);
11072 }else if(d.createTextRange){
11073 var range = d.createTextRange();
11074 range.moveStart("character", start);
11075 range.moveEnd("character", v.length-end);
11082 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
11083 * @param {Mixed} value The value to set
11085 setValue : function(v){
11088 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11094 processValue : function(value){
11095 if(this.stripCharsRe){
11096 var newValue = value.replace(this.stripCharsRe, '');
11097 if(newValue !== value){
11098 this.setRawValue(newValue);
11105 preFocus : function(){
11107 if(this.selectOnFocus){
11108 this.inputEl().dom.select();
11111 filterKeys : function(e){
11112 var k = e.getKey();
11113 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11116 var c = e.getCharCode(), cc = String.fromCharCode(c);
11117 if(Roo.isIE && (e.isSpecialKey() || !cc)){
11120 if(!this.maskRe.test(cc)){
11125 * Clear any invalid styles/messages for this field
11127 clearInvalid : function(){
11129 if(!this.el || this.preventMark){ // not rendered
11134 this.el.removeClass([this.invalidClass, 'is-invalid']);
11136 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11138 var feedback = this.el.select('.form-control-feedback', true).first();
11141 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11146 if(this.indicator){
11147 this.indicator.removeClass('visible');
11148 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11151 this.fireEvent('valid', this);
11155 * Mark this field as valid
11157 markValid : function()
11159 if(!this.el || this.preventMark){ // not rendered...
11163 this.el.removeClass([this.invalidClass, this.validClass]);
11164 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11166 var feedback = this.el.select('.form-control-feedback', true).first();
11169 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11172 if(this.indicator){
11173 this.indicator.removeClass('visible');
11174 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11182 if(this.allowBlank && !this.getRawValue().length){
11185 if (Roo.bootstrap.version == 3) {
11186 this.el.addClass(this.validClass);
11188 this.inputEl().addClass('is-valid');
11191 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11193 var feedback = this.el.select('.form-control-feedback', true).first();
11196 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11197 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11202 this.fireEvent('valid', this);
11206 * Mark this field as invalid
11207 * @param {String} msg The validation message
11209 markInvalid : function(msg)
11211 if(!this.el || this.preventMark){ // not rendered
11215 this.el.removeClass([this.invalidClass, this.validClass]);
11216 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11218 var feedback = this.el.select('.form-control-feedback', true).first();
11221 this.el.select('.form-control-feedback', true).first().removeClass(
11222 [this.invalidFeedbackClass, this.validFeedbackClass]);
11229 if(this.allowBlank && !this.getRawValue().length){
11233 if(this.indicator){
11234 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11235 this.indicator.addClass('visible');
11237 if (Roo.bootstrap.version == 3) {
11238 this.el.addClass(this.invalidClass);
11240 this.inputEl().addClass('is-invalid');
11245 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11247 var feedback = this.el.select('.form-control-feedback', true).first();
11250 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11252 if(this.getValue().length || this.forceFeedback){
11253 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11260 this.fireEvent('invalid', this, msg);
11263 SafariOnKeyDown : function(event)
11265 // this is a workaround for a password hang bug on chrome/ webkit.
11266 if (this.inputEl().dom.type != 'password') {
11270 var isSelectAll = false;
11272 if(this.inputEl().dom.selectionEnd > 0){
11273 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11275 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11276 event.preventDefault();
11281 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11283 event.preventDefault();
11284 // this is very hacky as keydown always get's upper case.
11286 var cc = String.fromCharCode(event.getCharCode());
11287 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
11291 adjustWidth : function(tag, w){
11292 tag = tag.toLowerCase();
11293 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11294 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11295 if(tag == 'input'){
11298 if(tag == 'textarea'){
11301 }else if(Roo.isOpera){
11302 if(tag == 'input'){
11305 if(tag == 'textarea'){
11313 setFieldLabel : function(v)
11315 if(!this.rendered){
11319 if(this.indicatorEl()){
11320 var ar = this.el.select('label > span',true);
11322 if (ar.elements.length) {
11323 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11324 this.fieldLabel = v;
11328 var br = this.el.select('label',true);
11330 if(br.elements.length) {
11331 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11332 this.fieldLabel = v;
11336 Roo.log('Cannot Found any of label > span || label in input');
11340 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11341 this.fieldLabel = v;
11356 * @class Roo.bootstrap.TextArea
11357 * @extends Roo.bootstrap.Input
11358 * Bootstrap TextArea class
11359 * @cfg {Number} cols Specifies the visible width of a text area
11360 * @cfg {Number} rows Specifies the visible number of lines in a text area
11361 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11362 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11363 * @cfg {string} html text
11366 * Create a new TextArea
11367 * @param {Object} config The config object
11370 Roo.bootstrap.TextArea = function(config){
11371 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11375 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
11385 getAutoCreate : function(){
11387 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11393 if(this.inputType != 'hidden'){
11394 cfg.cls = 'form-group' //input-group
11402 value : this.value || '',
11403 html: this.html || '',
11404 cls : 'form-control',
11405 placeholder : this.placeholder || ''
11409 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11410 input.maxLength = this.maxLength;
11414 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11418 input.cols = this.cols;
11421 if (this.readOnly) {
11422 input.readonly = true;
11426 input.name = this.name;
11430 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11434 ['xs','sm','md','lg'].map(function(size){
11435 if (settings[size]) {
11436 cfg.cls += ' col-' + size + '-' + settings[size];
11440 var inputblock = input;
11442 if(this.hasFeedback && !this.allowBlank){
11446 cls: 'glyphicon form-control-feedback'
11450 cls : 'has-feedback',
11459 if (this.before || this.after) {
11462 cls : 'input-group',
11466 inputblock.cn.push({
11468 cls : 'input-group-addon',
11473 inputblock.cn.push(input);
11475 if(this.hasFeedback && !this.allowBlank){
11476 inputblock.cls += ' has-feedback';
11477 inputblock.cn.push(feedback);
11481 inputblock.cn.push({
11483 cls : 'input-group-addon',
11490 if (align ==='left' && this.fieldLabel.length) {
11495 cls : 'control-label',
11496 html : this.fieldLabel
11507 if(this.labelWidth > 12){
11508 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11511 if(this.labelWidth < 13 && this.labelmd == 0){
11512 this.labelmd = this.labelWidth;
11515 if(this.labellg > 0){
11516 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11517 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11520 if(this.labelmd > 0){
11521 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11522 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11525 if(this.labelsm > 0){
11526 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11527 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11530 if(this.labelxs > 0){
11531 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11532 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11535 } else if ( this.fieldLabel.length) {
11540 //cls : 'input-group-addon',
11541 html : this.fieldLabel
11559 if (this.disabled) {
11560 input.disabled=true;
11567 * return the real textarea element.
11569 inputEl: function ()
11571 return this.el.select('textarea.form-control',true).first();
11575 * Clear any invalid styles/messages for this field
11577 clearInvalid : function()
11580 if(!this.el || this.preventMark){ // not rendered
11584 var label = this.el.select('label', true).first();
11585 var icon = this.el.select('i.fa-star', true).first();
11590 this.el.removeClass( this.validClass);
11591 this.inputEl().removeClass('is-invalid');
11593 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11595 var feedback = this.el.select('.form-control-feedback', true).first();
11598 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11603 this.fireEvent('valid', this);
11607 * Mark this field as valid
11609 markValid : function()
11611 if(!this.el || this.preventMark){ // not rendered
11615 this.el.removeClass([this.invalidClass, this.validClass]);
11616 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11618 var feedback = this.el.select('.form-control-feedback', true).first();
11621 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11624 if(this.disabled || this.allowBlank){
11628 var label = this.el.select('label', true).first();
11629 var icon = this.el.select('i.fa-star', true).first();
11634 if (Roo.bootstrap.version == 3) {
11635 this.el.addClass(this.validClass);
11637 this.inputEl().addClass('is-valid');
11641 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11643 var feedback = this.el.select('.form-control-feedback', true).first();
11646 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11647 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11652 this.fireEvent('valid', this);
11656 * Mark this field as invalid
11657 * @param {String} msg The validation message
11659 markInvalid : function(msg)
11661 if(!this.el || this.preventMark){ // not rendered
11665 this.el.removeClass([this.invalidClass, this.validClass]);
11666 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11668 var feedback = this.el.select('.form-control-feedback', true).first();
11671 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11674 if(this.disabled || this.allowBlank){
11678 var label = this.el.select('label', true).first();
11679 var icon = this.el.select('i.fa-star', true).first();
11681 if(!this.getValue().length && label && !icon){
11682 this.el.createChild({
11684 cls : 'text-danger fa fa-lg fa-star',
11685 tooltip : 'This field is required',
11686 style : 'margin-right:5px;'
11690 if (Roo.bootstrap.version == 3) {
11691 this.el.addClass(this.invalidClass);
11693 this.inputEl().addClass('is-invalid');
11696 // fixme ... this may be depricated need to test..
11697 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11699 var feedback = this.el.select('.form-control-feedback', true).first();
11702 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11704 if(this.getValue().length || this.forceFeedback){
11705 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11712 this.fireEvent('invalid', this, msg);
11720 * trigger field - base class for combo..
11725 * @class Roo.bootstrap.TriggerField
11726 * @extends Roo.bootstrap.Input
11727 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11728 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11729 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11730 * for which you can provide a custom implementation. For example:
11732 var trigger = new Roo.bootstrap.TriggerField();
11733 trigger.onTriggerClick = myTriggerFn;
11734 trigger.applyTo('my-field');
11737 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11738 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11739 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
11740 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11741 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11744 * Create a new TriggerField.
11745 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11746 * to the base TextField)
11748 Roo.bootstrap.TriggerField = function(config){
11749 this.mimicing = false;
11750 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11753 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
11755 * @cfg {String} triggerClass A CSS class to apply to the trigger
11758 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11763 * @cfg {Boolean} removable (true|false) special filter default false
11767 /** @cfg {Boolean} grow @hide */
11768 /** @cfg {Number} growMin @hide */
11769 /** @cfg {Number} growMax @hide */
11775 autoSize: Roo.emptyFn,
11779 deferHeight : true,
11782 actionMode : 'wrap',
11787 getAutoCreate : function(){
11789 var align = this.labelAlign || this.parentLabelAlign();
11794 cls: 'form-group' //input-group
11801 type : this.inputType,
11802 cls : 'form-control',
11803 autocomplete: 'new-password',
11804 placeholder : this.placeholder || ''
11808 input.name = this.name;
11811 input.cls += ' input-' + this.size;
11814 if (this.disabled) {
11815 input.disabled=true;
11818 var inputblock = input;
11820 if(this.hasFeedback && !this.allowBlank){
11824 cls: 'glyphicon form-control-feedback'
11827 if(this.removable && !this.editable ){
11829 cls : 'has-feedback',
11835 cls : 'roo-combo-removable-btn close'
11842 cls : 'has-feedback',
11851 if(this.removable && !this.editable ){
11853 cls : 'roo-removable',
11859 cls : 'roo-combo-removable-btn close'
11866 if (this.before || this.after) {
11869 cls : 'input-group',
11873 inputblock.cn.push({
11875 cls : 'input-group-addon input-group-prepend input-group-text',
11880 inputblock.cn.push(input);
11882 if(this.hasFeedback && !this.allowBlank){
11883 inputblock.cls += ' has-feedback';
11884 inputblock.cn.push(feedback);
11888 inputblock.cn.push({
11890 cls : 'input-group-addon input-group-append input-group-text',
11899 var ibwrap = inputblock;
11904 cls: 'roo-select2-choices',
11908 cls: 'roo-select2-search-field',
11920 cls: 'roo-select2-container input-group',
11925 cls: 'form-hidden-field'
11931 if(!this.multiple && this.showToggleBtn){
11937 if (this.caret != false) {
11940 cls: 'fa fa-' + this.caret
11947 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
11949 Roo.bootstrap.version == 3 ? caret : '',
11952 cls: 'combobox-clear',
11966 combobox.cls += ' roo-select2-container-multi';
11970 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11971 tooltip : 'This field is required'
11973 if (Roo.bootstrap.version == 4) {
11976 style : 'display:none'
11981 if (align ==='left' && this.fieldLabel.length) {
11983 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
11990 cls : 'control-label',
11991 html : this.fieldLabel
12003 var labelCfg = cfg.cn[1];
12004 var contentCfg = cfg.cn[2];
12006 if(this.indicatorpos == 'right'){
12011 cls : 'control-label',
12015 html : this.fieldLabel
12029 labelCfg = cfg.cn[0];
12030 contentCfg = cfg.cn[1];
12033 if(this.labelWidth > 12){
12034 labelCfg.style = "width: " + this.labelWidth + 'px';
12037 if(this.labelWidth < 13 && this.labelmd == 0){
12038 this.labelmd = this.labelWidth;
12041 if(this.labellg > 0){
12042 labelCfg.cls += ' col-lg-' + this.labellg;
12043 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12046 if(this.labelmd > 0){
12047 labelCfg.cls += ' col-md-' + this.labelmd;
12048 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12051 if(this.labelsm > 0){
12052 labelCfg.cls += ' col-sm-' + this.labelsm;
12053 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12056 if(this.labelxs > 0){
12057 labelCfg.cls += ' col-xs-' + this.labelxs;
12058 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12061 } else if ( this.fieldLabel.length) {
12062 // Roo.log(" label");
12067 //cls : 'input-group-addon',
12068 html : this.fieldLabel
12076 if(this.indicatorpos == 'right'){
12084 html : this.fieldLabel
12098 // Roo.log(" no label && no align");
12105 ['xs','sm','md','lg'].map(function(size){
12106 if (settings[size]) {
12107 cfg.cls += ' col-' + size + '-' + settings[size];
12118 onResize : function(w, h){
12119 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12120 // if(typeof w == 'number'){
12121 // var x = w - this.trigger.getWidth();
12122 // this.inputEl().setWidth(this.adjustWidth('input', x));
12123 // this.trigger.setStyle('left', x+'px');
12128 adjustSize : Roo.BoxComponent.prototype.adjustSize,
12131 getResizeEl : function(){
12132 return this.inputEl();
12136 getPositionEl : function(){
12137 return this.inputEl();
12141 alignErrorIcon : function(){
12142 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12146 initEvents : function(){
12150 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12151 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12152 if(!this.multiple && this.showToggleBtn){
12153 this.trigger = this.el.select('span.dropdown-toggle',true).first();
12154 if(this.hideTrigger){
12155 this.trigger.setDisplayed(false);
12157 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12161 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12164 if(this.removable && !this.editable && !this.tickable){
12165 var close = this.closeTriggerEl();
12168 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12169 close.on('click', this.removeBtnClick, this, close);
12173 //this.trigger.addClassOnOver('x-form-trigger-over');
12174 //this.trigger.addClassOnClick('x-form-trigger-click');
12177 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12181 closeTriggerEl : function()
12183 var close = this.el.select('.roo-combo-removable-btn', true).first();
12184 return close ? close : false;
12187 removeBtnClick : function(e, h, el)
12189 e.preventDefault();
12191 if(this.fireEvent("remove", this) !== false){
12193 this.fireEvent("afterremove", this)
12197 createList : function()
12199 this.list = Roo.get(document.body).createChild({
12200 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12201 cls: 'typeahead typeahead-long dropdown-menu',
12202 style: 'display:none'
12205 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12210 initTrigger : function(){
12215 onDestroy : function(){
12217 this.trigger.removeAllListeners();
12218 // this.trigger.remove();
12221 // this.wrap.remove();
12223 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12227 onFocus : function(){
12228 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12230 if(!this.mimicing){
12231 this.wrap.addClass('x-trigger-wrap-focus');
12232 this.mimicing = true;
12233 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12234 if(this.monitorTab){
12235 this.el.on("keydown", this.checkTab, this);
12242 checkTab : function(e){
12243 if(e.getKey() == e.TAB){
12244 this.triggerBlur();
12249 onBlur : function(){
12254 mimicBlur : function(e, t){
12256 if(!this.wrap.contains(t) && this.validateBlur()){
12257 this.triggerBlur();
12263 triggerBlur : function(){
12264 this.mimicing = false;
12265 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12266 if(this.monitorTab){
12267 this.el.un("keydown", this.checkTab, this);
12269 //this.wrap.removeClass('x-trigger-wrap-focus');
12270 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12274 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12275 validateBlur : function(e, t){
12280 onDisable : function(){
12281 this.inputEl().dom.disabled = true;
12282 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12284 // this.wrap.addClass('x-item-disabled');
12289 onEnable : function(){
12290 this.inputEl().dom.disabled = false;
12291 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12293 // this.el.removeClass('x-item-disabled');
12298 onShow : function(){
12299 var ae = this.getActionEl();
12302 ae.dom.style.display = '';
12303 ae.dom.style.visibility = 'visible';
12309 onHide : function(){
12310 var ae = this.getActionEl();
12311 ae.dom.style.display = 'none';
12315 * The function that should handle the trigger's click event. This method does nothing by default until overridden
12316 * by an implementing function.
12318 * @param {EventObject} e
12320 onTriggerClick : Roo.emptyFn
12328 * @class Roo.bootstrap.CardUploader
12329 * @extends Roo.bootstrap.Button
12330 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12331 * @cfg {Number} errorTimeout default 3000
12332 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
12333 * @cfg {Array} html The button text.
12337 * Create a new CardUploader
12338 * @param {Object} config The config object
12341 Roo.bootstrap.CardUploader = function(config){
12345 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12348 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
12355 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
12358 errorTimeout : 3000,
12362 fileCollection : false,
12365 getAutoCreate : function()
12369 cls :'form-group' ,
12374 //cls : 'input-group-addon',
12375 html : this.fieldLabel
12382 value : this.value,
12383 cls : 'd-none form-control'
12388 multiple : 'multiple',
12390 cls : 'd-none roo-card-upload-selector'
12394 cls : 'roo-card-uploader-button-container w-100 mb-2'
12397 cls : 'card-columns roo-card-uploader-container'
12407 getChildContainer : function() /// what children are added to.
12409 return this.containerEl;
12412 getButtonContainer : function() /// what children are added to.
12414 return this.el.select(".roo-card-uploader-button-container").first();
12417 initEvents : function()
12420 Roo.bootstrap.Input.prototype.initEvents.call(this);
12424 xns: Roo.bootstrap,
12427 container_method : 'getButtonContainer' ,
12428 html : this.html, // fix changable?
12431 'click' : function(btn, e) {
12440 this.urlAPI = (window.createObjectURL && window) ||
12441 (window.URL && URL.revokeObjectURL && URL) ||
12442 (window.webkitURL && webkitURL);
12447 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12449 this.selectorEl.on('change', this.onFileSelected, this);
12452 this.images.forEach(function(img) {
12455 this.images = false;
12457 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12463 onClick : function(e)
12465 e.preventDefault();
12467 this.selectorEl.dom.click();
12471 onFileSelected : function(e)
12473 e.preventDefault();
12475 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12479 Roo.each(this.selectorEl.dom.files, function(file){
12480 this.addFile(file);
12489 addFile : function(file)
12492 if(typeof(file) === 'string'){
12493 throw "Add file by name?"; // should not happen
12497 if(!file || !this.urlAPI){
12507 var url = _this.urlAPI.createObjectURL( file);
12510 id : Roo.bootstrap.CardUploader.ID--,
12511 is_uploaded : false,
12514 mimetype : file.type,
12521 addCard : function (data)
12523 // hidden input element?
12524 // if the file is not an image...
12525 //then we need to use something other that and header_image
12530 xns : Roo.bootstrap,
12531 xtype : 'CardFooter',
12534 xns : Roo.bootstrap,
12540 xns : Roo.bootstrap,
12542 html : String.format("<small>{0}</small>", data.title),
12543 cls : 'col-11 text-left',
12548 click : function() {
12549 this.downloadCard(data.id)
12555 xns : Roo.bootstrap,
12563 click : function() {
12564 t.removeCard(data.id)
12576 var cn = this.addxtype(
12579 xns : Roo.bootstrap,
12582 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12583 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
12584 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
12589 initEvents : function() {
12590 Roo.bootstrap.Card.prototype.initEvents.call(this);
12591 this.imgEl = this.el.select('.card-img-top').first();
12593 this.imgEl.on('click', function() { t.previewCard( data.id); }, this);
12594 this.imgEl.set({ 'pointer' : 'cursor' });
12603 // dont' really need ot update items.
12604 // this.items.push(cn);
12605 this.fileCollection.add(cn);
12606 this.updateInput();
12609 removeCard : function(id)
12612 var card = this.fileCollection.get(id);
12613 card.data.is_deleted = 1;
12614 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12615 this.fileCollection.remove(card);
12616 //this.items = this.items.filter(function(e) { return e != card });
12617 // dont' really need ot update items.
12618 card.el.dom.parentNode.removeChild(card.el.dom);
12623 this.fileCollection.each(function(card) {
12624 card.el.dom.parentNode.removeChild(card.el.dom);
12626 this.fileCollection.clear();
12627 this.updateInput();
12630 updateInput : function()
12633 this.fileCollection.each(function(e) {
12637 this.inputEl().dom.value = JSON.stringify(data);
12644 Roo.bootstrap.CardUploader.ID = -1;/*
12646 * Ext JS Library 1.1.1
12647 * Copyright(c) 2006-2007, Ext JS, LLC.
12649 * Originally Released Under LGPL - original licence link has changed is not relivant.
12652 * <script type="text/javascript">
12657 * @class Roo.data.SortTypes
12659 * Defines the default sorting (casting?) comparison functions used when sorting data.
12661 Roo.data.SortTypes = {
12663 * Default sort that does nothing
12664 * @param {Mixed} s The value being converted
12665 * @return {Mixed} The comparison value
12667 none : function(s){
12672 * The regular expression used to strip tags
12676 stripTagsRE : /<\/?[^>]+>/gi,
12679 * Strips all HTML tags to sort on text only
12680 * @param {Mixed} s The value being converted
12681 * @return {String} The comparison value
12683 asText : function(s){
12684 return String(s).replace(this.stripTagsRE, "");
12688 * Strips all HTML tags to sort on text only - Case insensitive
12689 * @param {Mixed} s The value being converted
12690 * @return {String} The comparison value
12692 asUCText : function(s){
12693 return String(s).toUpperCase().replace(this.stripTagsRE, "");
12697 * Case insensitive string
12698 * @param {Mixed} s The value being converted
12699 * @return {String} The comparison value
12701 asUCString : function(s) {
12702 return String(s).toUpperCase();
12707 * @param {Mixed} s The value being converted
12708 * @return {Number} The comparison value
12710 asDate : function(s) {
12714 if(s instanceof Date){
12715 return s.getTime();
12717 return Date.parse(String(s));
12722 * @param {Mixed} s The value being converted
12723 * @return {Float} The comparison value
12725 asFloat : function(s) {
12726 var val = parseFloat(String(s).replace(/,/g, ""));
12735 * @param {Mixed} s The value being converted
12736 * @return {Number} The comparison value
12738 asInt : function(s) {
12739 var val = parseInt(String(s).replace(/,/g, ""));
12747 * Ext JS Library 1.1.1
12748 * Copyright(c) 2006-2007, Ext JS, LLC.
12750 * Originally Released Under LGPL - original licence link has changed is not relivant.
12753 * <script type="text/javascript">
12757 * @class Roo.data.Record
12758 * Instances of this class encapsulate both record <em>definition</em> information, and record
12759 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12760 * to access Records cached in an {@link Roo.data.Store} object.<br>
12762 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12763 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12766 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12768 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12769 * {@link #create}. The parameters are the same.
12770 * @param {Array} data An associative Array of data values keyed by the field name.
12771 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12772 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12773 * not specified an integer id is generated.
12775 Roo.data.Record = function(data, id){
12776 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12781 * Generate a constructor for a specific record layout.
12782 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12783 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12784 * Each field definition object may contain the following properties: <ul>
12785 * <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,
12786 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12787 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12788 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12789 * is being used, then this is a string containing the javascript expression to reference the data relative to
12790 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12791 * to the data item relative to the record element. If the mapping expression is the same as the field name,
12792 * this may be omitted.</p></li>
12793 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12794 * <ul><li>auto (Default, implies no conversion)</li>
12799 * <li>date</li></ul></p></li>
12800 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12801 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12802 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12803 * by the Reader into an object that will be stored in the Record. It is passed the
12804 * following parameters:<ul>
12805 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12807 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12809 * <br>usage:<br><pre><code>
12810 var TopicRecord = Roo.data.Record.create(
12811 {name: 'title', mapping: 'topic_title'},
12812 {name: 'author', mapping: 'username'},
12813 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12814 {name: 'lastPost', mapping: 'post_time', type: 'date'},
12815 {name: 'lastPoster', mapping: 'user2'},
12816 {name: 'excerpt', mapping: 'post_text'}
12819 var myNewRecord = new TopicRecord({
12820 title: 'Do my job please',
12823 lastPost: new Date(),
12824 lastPoster: 'Animal',
12825 excerpt: 'No way dude!'
12827 myStore.add(myNewRecord);
12832 Roo.data.Record.create = function(o){
12833 var f = function(){
12834 f.superclass.constructor.apply(this, arguments);
12836 Roo.extend(f, Roo.data.Record);
12837 var p = f.prototype;
12838 p.fields = new Roo.util.MixedCollection(false, function(field){
12841 for(var i = 0, len = o.length; i < len; i++){
12842 p.fields.add(new Roo.data.Field(o[i]));
12844 f.getField = function(name){
12845 return p.fields.get(name);
12850 Roo.data.Record.AUTO_ID = 1000;
12851 Roo.data.Record.EDIT = 'edit';
12852 Roo.data.Record.REJECT = 'reject';
12853 Roo.data.Record.COMMIT = 'commit';
12855 Roo.data.Record.prototype = {
12857 * Readonly flag - true if this record has been modified.
12866 join : function(store){
12867 this.store = store;
12871 * Set the named field to the specified value.
12872 * @param {String} name The name of the field to set.
12873 * @param {Object} value The value to set the field to.
12875 set : function(name, value){
12876 if(this.data[name] == value){
12880 if(!this.modified){
12881 this.modified = {};
12883 if(typeof this.modified[name] == 'undefined'){
12884 this.modified[name] = this.data[name];
12886 this.data[name] = value;
12887 if(!this.editing && this.store){
12888 this.store.afterEdit(this);
12893 * Get the value of the named field.
12894 * @param {String} name The name of the field to get the value of.
12895 * @return {Object} The value of the field.
12897 get : function(name){
12898 return this.data[name];
12902 beginEdit : function(){
12903 this.editing = true;
12904 this.modified = {};
12908 cancelEdit : function(){
12909 this.editing = false;
12910 delete this.modified;
12914 endEdit : function(){
12915 this.editing = false;
12916 if(this.dirty && this.store){
12917 this.store.afterEdit(this);
12922 * Usually called by the {@link Roo.data.Store} which owns the Record.
12923 * Rejects all changes made to the Record since either creation, or the last commit operation.
12924 * Modified fields are reverted to their original values.
12926 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
12927 * of reject operations.
12929 reject : function(){
12930 var m = this.modified;
12932 if(typeof m[n] != "function"){
12933 this.data[n] = m[n];
12936 this.dirty = false;
12937 delete this.modified;
12938 this.editing = false;
12940 this.store.afterReject(this);
12945 * Usually called by the {@link Roo.data.Store} which owns the Record.
12946 * Commits all changes made to the Record since either creation, or the last commit operation.
12948 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
12949 * of commit operations.
12951 commit : function(){
12952 this.dirty = false;
12953 delete this.modified;
12954 this.editing = false;
12956 this.store.afterCommit(this);
12961 hasError : function(){
12962 return this.error != null;
12966 clearError : function(){
12971 * Creates a copy of this record.
12972 * @param {String} id (optional) A new record id if you don't want to use this record's id
12975 copy : function(newId) {
12976 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
12980 * Ext JS Library 1.1.1
12981 * Copyright(c) 2006-2007, Ext JS, LLC.
12983 * Originally Released Under LGPL - original licence link has changed is not relivant.
12986 * <script type="text/javascript">
12992 * @class Roo.data.Store
12993 * @extends Roo.util.Observable
12994 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
12995 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
12997 * 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
12998 * has no knowledge of the format of the data returned by the Proxy.<br>
13000 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13001 * instances from the data object. These records are cached and made available through accessor functions.
13003 * Creates a new Store.
13004 * @param {Object} config A config object containing the objects needed for the Store to access data,
13005 * and read the data into Records.
13007 Roo.data.Store = function(config){
13008 this.data = new Roo.util.MixedCollection(false);
13009 this.data.getKey = function(o){
13012 this.baseParams = {};
13014 this.paramNames = {
13019 "multisort" : "_multisort"
13022 if(config && config.data){
13023 this.inlineData = config.data;
13024 delete config.data;
13027 Roo.apply(this, config);
13029 if(this.reader){ // reader passed
13030 this.reader = Roo.factory(this.reader, Roo.data);
13031 this.reader.xmodule = this.xmodule || false;
13032 if(!this.recordType){
13033 this.recordType = this.reader.recordType;
13035 if(this.reader.onMetaChange){
13036 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13040 if(this.recordType){
13041 this.fields = this.recordType.prototype.fields;
13043 this.modified = [];
13047 * @event datachanged
13048 * Fires when the data cache has changed, and a widget which is using this Store
13049 * as a Record cache should refresh its view.
13050 * @param {Store} this
13052 datachanged : true,
13054 * @event metachange
13055 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13056 * @param {Store} this
13057 * @param {Object} meta The JSON metadata
13062 * Fires when Records have been added to the Store
13063 * @param {Store} this
13064 * @param {Roo.data.Record[]} records The array of Records added
13065 * @param {Number} index The index at which the record(s) were added
13070 * Fires when a Record has been removed from the Store
13071 * @param {Store} this
13072 * @param {Roo.data.Record} record The Record that was removed
13073 * @param {Number} index The index at which the record was removed
13078 * Fires when a Record has been updated
13079 * @param {Store} this
13080 * @param {Roo.data.Record} record The Record that was updated
13081 * @param {String} operation The update operation being performed. Value may be one of:
13083 Roo.data.Record.EDIT
13084 Roo.data.Record.REJECT
13085 Roo.data.Record.COMMIT
13091 * Fires when the data cache has been cleared.
13092 * @param {Store} this
13096 * @event beforeload
13097 * Fires before a request is made for a new data object. If the beforeload handler returns false
13098 * the load action will be canceled.
13099 * @param {Store} this
13100 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13104 * @event beforeloadadd
13105 * Fires after a new set of Records has been loaded.
13106 * @param {Store} this
13107 * @param {Roo.data.Record[]} records The Records that were loaded
13108 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13110 beforeloadadd : true,
13113 * Fires after a new set of Records has been loaded, before they are added to the store.
13114 * @param {Store} this
13115 * @param {Roo.data.Record[]} records The Records that were loaded
13116 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13117 * @params {Object} return from reader
13121 * @event loadexception
13122 * Fires if an exception occurs in the Proxy during loading.
13123 * Called with the signature of the Proxy's "loadexception" event.
13124 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13127 * @param {Object} return from JsonData.reader() - success, totalRecords, records
13128 * @param {Object} load options
13129 * @param {Object} jsonData from your request (normally this contains the Exception)
13131 loadexception : true
13135 this.proxy = Roo.factory(this.proxy, Roo.data);
13136 this.proxy.xmodule = this.xmodule || false;
13137 this.relayEvents(this.proxy, ["loadexception"]);
13139 this.sortToggle = {};
13140 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13142 Roo.data.Store.superclass.constructor.call(this);
13144 if(this.inlineData){
13145 this.loadData(this.inlineData);
13146 delete this.inlineData;
13150 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13152 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
13153 * without a remote query - used by combo/forms at present.
13157 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13160 * @cfg {Array} data Inline data to be loaded when the store is initialized.
13163 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13164 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13167 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13168 * on any HTTP request
13171 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13174 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13178 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13179 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13181 remoteSort : false,
13184 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13185 * loaded or when a record is removed. (defaults to false).
13187 pruneModifiedRecords : false,
13190 lastOptions : null,
13193 * Add Records to the Store and fires the add event.
13194 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13196 add : function(records){
13197 records = [].concat(records);
13198 for(var i = 0, len = records.length; i < len; i++){
13199 records[i].join(this);
13201 var index = this.data.length;
13202 this.data.addAll(records);
13203 this.fireEvent("add", this, records, index);
13207 * Remove a Record from the Store and fires the remove event.
13208 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13210 remove : function(record){
13211 var index = this.data.indexOf(record);
13212 this.data.removeAt(index);
13214 if(this.pruneModifiedRecords){
13215 this.modified.remove(record);
13217 this.fireEvent("remove", this, record, index);
13221 * Remove all Records from the Store and fires the clear event.
13223 removeAll : function(){
13225 if(this.pruneModifiedRecords){
13226 this.modified = [];
13228 this.fireEvent("clear", this);
13232 * Inserts Records to the Store at the given index and fires the add event.
13233 * @param {Number} index The start index at which to insert the passed Records.
13234 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13236 insert : function(index, records){
13237 records = [].concat(records);
13238 for(var i = 0, len = records.length; i < len; i++){
13239 this.data.insert(index, records[i]);
13240 records[i].join(this);
13242 this.fireEvent("add", this, records, index);
13246 * Get the index within the cache of the passed Record.
13247 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13248 * @return {Number} The index of the passed Record. Returns -1 if not found.
13250 indexOf : function(record){
13251 return this.data.indexOf(record);
13255 * Get the index within the cache of the Record with the passed id.
13256 * @param {String} id The id of the Record to find.
13257 * @return {Number} The index of the Record. Returns -1 if not found.
13259 indexOfId : function(id){
13260 return this.data.indexOfKey(id);
13264 * Get the Record with the specified id.
13265 * @param {String} id The id of the Record to find.
13266 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13268 getById : function(id){
13269 return this.data.key(id);
13273 * Get the Record at the specified index.
13274 * @param {Number} index The index of the Record to find.
13275 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13277 getAt : function(index){
13278 return this.data.itemAt(index);
13282 * Returns a range of Records between specified indices.
13283 * @param {Number} startIndex (optional) The starting index (defaults to 0)
13284 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13285 * @return {Roo.data.Record[]} An array of Records
13287 getRange : function(start, end){
13288 return this.data.getRange(start, end);
13292 storeOptions : function(o){
13293 o = Roo.apply({}, o);
13296 this.lastOptions = o;
13300 * Loads the Record cache from the configured Proxy using the configured Reader.
13302 * If using remote paging, then the first load call must specify the <em>start</em>
13303 * and <em>limit</em> properties in the options.params property to establish the initial
13304 * position within the dataset, and the number of Records to cache on each read from the Proxy.
13306 * <strong>It is important to note that for remote data sources, loading is asynchronous,
13307 * and this call will return before the new data has been loaded. Perform any post-processing
13308 * in a callback function, or in a "load" event handler.</strong>
13310 * @param {Object} options An object containing properties which control loading options:<ul>
13311 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13312 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13313 * passed the following arguments:<ul>
13314 * <li>r : Roo.data.Record[]</li>
13315 * <li>options: Options object from the load call</li>
13316 * <li>success: Boolean success indicator</li></ul></li>
13317 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13318 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13321 load : function(options){
13322 options = options || {};
13323 if(this.fireEvent("beforeload", this, options) !== false){
13324 this.storeOptions(options);
13325 var p = Roo.apply(options.params || {}, this.baseParams);
13326 // if meta was not loaded from remote source.. try requesting it.
13327 if (!this.reader.metaFromRemote) {
13328 p._requestMeta = 1;
13330 if(this.sortInfo && this.remoteSort){
13331 var pn = this.paramNames;
13332 p[pn["sort"]] = this.sortInfo.field;
13333 p[pn["dir"]] = this.sortInfo.direction;
13335 if (this.multiSort) {
13336 var pn = this.paramNames;
13337 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13340 this.proxy.load(p, this.reader, this.loadRecords, this, options);
13345 * Reloads the Record cache from the configured Proxy using the configured Reader and
13346 * the options from the last load operation performed.
13347 * @param {Object} options (optional) An object containing properties which may override the options
13348 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13349 * the most recently used options are reused).
13351 reload : function(options){
13352 this.load(Roo.applyIf(options||{}, this.lastOptions));
13356 // Called as a callback by the Reader during a load operation.
13357 loadRecords : function(o, options, success){
13358 if(!o || success === false){
13359 if(success !== false){
13360 this.fireEvent("load", this, [], options, o);
13362 if(options.callback){
13363 options.callback.call(options.scope || this, [], options, false);
13367 // if data returned failure - throw an exception.
13368 if (o.success === false) {
13369 // show a message if no listener is registered.
13370 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13371 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13373 // loadmask wil be hooked into this..
13374 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13377 var r = o.records, t = o.totalRecords || r.length;
13379 this.fireEvent("beforeloadadd", this, r, options, o);
13381 if(!options || options.add !== true){
13382 if(this.pruneModifiedRecords){
13383 this.modified = [];
13385 for(var i = 0, len = r.length; i < len; i++){
13389 this.data = this.snapshot;
13390 delete this.snapshot;
13393 this.data.addAll(r);
13394 this.totalLength = t;
13396 this.fireEvent("datachanged", this);
13398 this.totalLength = Math.max(t, this.data.length+r.length);
13402 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13404 var e = new Roo.data.Record({});
13406 e.set(this.parent.displayField, this.parent.emptyTitle);
13407 e.set(this.parent.valueField, '');
13412 this.fireEvent("load", this, r, options, o);
13413 if(options.callback){
13414 options.callback.call(options.scope || this, r, options, true);
13420 * Loads data from a passed data block. A Reader which understands the format of the data
13421 * must have been configured in the constructor.
13422 * @param {Object} data The data block from which to read the Records. The format of the data expected
13423 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13424 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13426 loadData : function(o, append){
13427 var r = this.reader.readRecords(o);
13428 this.loadRecords(r, {add: append}, true);
13432 * using 'cn' the nested child reader read the child array into it's child stores.
13433 * @param {Object} rec The record with a 'children array
13435 loadDataFromChildren : function(rec)
13437 this.loadData(this.reader.toLoadData(rec));
13442 * Gets the number of cached records.
13444 * <em>If using paging, this may not be the total size of the dataset. If the data object
13445 * used by the Reader contains the dataset size, then the getTotalCount() function returns
13446 * the data set size</em>
13448 getCount : function(){
13449 return this.data.length || 0;
13453 * Gets the total number of records in the dataset as returned by the server.
13455 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13456 * the dataset size</em>
13458 getTotalCount : function(){
13459 return this.totalLength || 0;
13463 * Returns the sort state of the Store as an object with two properties:
13465 field {String} The name of the field by which the Records are sorted
13466 direction {String} The sort order, "ASC" or "DESC"
13469 getSortState : function(){
13470 return this.sortInfo;
13474 applySort : function(){
13475 if(this.sortInfo && !this.remoteSort){
13476 var s = this.sortInfo, f = s.field;
13477 var st = this.fields.get(f).sortType;
13478 var fn = function(r1, r2){
13479 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13480 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13482 this.data.sort(s.direction, fn);
13483 if(this.snapshot && this.snapshot != this.data){
13484 this.snapshot.sort(s.direction, fn);
13490 * Sets the default sort column and order to be used by the next load operation.
13491 * @param {String} fieldName The name of the field to sort by.
13492 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13494 setDefaultSort : function(field, dir){
13495 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13499 * Sort the Records.
13500 * If remote sorting is used, the sort is performed on the server, and the cache is
13501 * reloaded. If local sorting is used, the cache is sorted internally.
13502 * @param {String} fieldName The name of the field to sort by.
13503 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13505 sort : function(fieldName, dir){
13506 var f = this.fields.get(fieldName);
13508 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13510 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13511 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13516 this.sortToggle[f.name] = dir;
13517 this.sortInfo = {field: f.name, direction: dir};
13518 if(!this.remoteSort){
13520 this.fireEvent("datachanged", this);
13522 this.load(this.lastOptions);
13527 * Calls the specified function for each of the Records in the cache.
13528 * @param {Function} fn The function to call. The Record is passed as the first parameter.
13529 * Returning <em>false</em> aborts and exits the iteration.
13530 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13532 each : function(fn, scope){
13533 this.data.each(fn, scope);
13537 * Gets all records modified since the last commit. Modified records are persisted across load operations
13538 * (e.g., during paging).
13539 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13541 getModifiedRecords : function(){
13542 return this.modified;
13546 createFilterFn : function(property, value, anyMatch){
13547 if(!value.exec){ // not a regex
13548 value = String(value);
13549 if(value.length == 0){
13552 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13554 return function(r){
13555 return value.test(r.data[property]);
13560 * Sums the value of <i>property</i> for each record between start and end and returns the result.
13561 * @param {String} property A field on your records
13562 * @param {Number} start The record index to start at (defaults to 0)
13563 * @param {Number} end The last record index to include (defaults to length - 1)
13564 * @return {Number} The sum
13566 sum : function(property, start, end){
13567 var rs = this.data.items, v = 0;
13568 start = start || 0;
13569 end = (end || end === 0) ? end : rs.length-1;
13571 for(var i = start; i <= end; i++){
13572 v += (rs[i].data[property] || 0);
13578 * Filter the records by a specified property.
13579 * @param {String} field A field on your records
13580 * @param {String/RegExp} value Either a string that the field
13581 * should start with or a RegExp to test against the field
13582 * @param {Boolean} anyMatch True to match any part not just the beginning
13584 filter : function(property, value, anyMatch){
13585 var fn = this.createFilterFn(property, value, anyMatch);
13586 return fn ? this.filterBy(fn) : this.clearFilter();
13590 * Filter by a function. The specified function will be called with each
13591 * record in this data source. If the function returns true the record is included,
13592 * otherwise it is filtered.
13593 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13594 * @param {Object} scope (optional) The scope of the function (defaults to this)
13596 filterBy : function(fn, scope){
13597 this.snapshot = this.snapshot || this.data;
13598 this.data = this.queryBy(fn, scope||this);
13599 this.fireEvent("datachanged", this);
13603 * Query the records by a specified property.
13604 * @param {String} field A field on your records
13605 * @param {String/RegExp} value Either a string that the field
13606 * should start with or a RegExp to test against the field
13607 * @param {Boolean} anyMatch True to match any part not just the beginning
13608 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13610 query : function(property, value, anyMatch){
13611 var fn = this.createFilterFn(property, value, anyMatch);
13612 return fn ? this.queryBy(fn) : this.data.clone();
13616 * Query by a function. The specified function will be called with each
13617 * record in this data source. If the function returns true the record is included
13619 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13620 * @param {Object} scope (optional) The scope of the function (defaults to this)
13621 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13623 queryBy : function(fn, scope){
13624 var data = this.snapshot || this.data;
13625 return data.filterBy(fn, scope||this);
13629 * Collects unique values for a particular dataIndex from this store.
13630 * @param {String} dataIndex The property to collect
13631 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13632 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13633 * @return {Array} An array of the unique values
13635 collect : function(dataIndex, allowNull, bypassFilter){
13636 var d = (bypassFilter === true && this.snapshot) ?
13637 this.snapshot.items : this.data.items;
13638 var v, sv, r = [], l = {};
13639 for(var i = 0, len = d.length; i < len; i++){
13640 v = d[i].data[dataIndex];
13642 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13651 * Revert to a view of the Record cache with no filtering applied.
13652 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13654 clearFilter : function(suppressEvent){
13655 if(this.snapshot && this.snapshot != this.data){
13656 this.data = this.snapshot;
13657 delete this.snapshot;
13658 if(suppressEvent !== true){
13659 this.fireEvent("datachanged", this);
13665 afterEdit : function(record){
13666 if(this.modified.indexOf(record) == -1){
13667 this.modified.push(record);
13669 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13673 afterReject : function(record){
13674 this.modified.remove(record);
13675 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13679 afterCommit : function(record){
13680 this.modified.remove(record);
13681 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13685 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13686 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13688 commitChanges : function(){
13689 var m = this.modified.slice(0);
13690 this.modified = [];
13691 for(var i = 0, len = m.length; i < len; i++){
13697 * Cancel outstanding changes on all changed records.
13699 rejectChanges : function(){
13700 var m = this.modified.slice(0);
13701 this.modified = [];
13702 for(var i = 0, len = m.length; i < len; i++){
13707 onMetaChange : function(meta, rtype, o){
13708 this.recordType = rtype;
13709 this.fields = rtype.prototype.fields;
13710 delete this.snapshot;
13711 this.sortInfo = meta.sortInfo || this.sortInfo;
13712 this.modified = [];
13713 this.fireEvent('metachange', this, this.reader.meta);
13716 moveIndex : function(data, type)
13718 var index = this.indexOf(data);
13720 var newIndex = index + type;
13724 this.insert(newIndex, data);
13729 * Ext JS Library 1.1.1
13730 * Copyright(c) 2006-2007, Ext JS, LLC.
13732 * Originally Released Under LGPL - original licence link has changed is not relivant.
13735 * <script type="text/javascript">
13739 * @class Roo.data.SimpleStore
13740 * @extends Roo.data.Store
13741 * Small helper class to make creating Stores from Array data easier.
13742 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13743 * @cfg {Array} fields An array of field definition objects, or field name strings.
13744 * @cfg {Object} an existing reader (eg. copied from another store)
13745 * @cfg {Array} data The multi-dimensional array of data
13747 * @param {Object} config
13749 Roo.data.SimpleStore = function(config)
13751 Roo.data.SimpleStore.superclass.constructor.call(this, {
13753 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13756 Roo.data.Record.create(config.fields)
13758 proxy : new Roo.data.MemoryProxy(config.data)
13762 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13764 * Ext JS Library 1.1.1
13765 * Copyright(c) 2006-2007, Ext JS, LLC.
13767 * Originally Released Under LGPL - original licence link has changed is not relivant.
13770 * <script type="text/javascript">
13775 * @extends Roo.data.Store
13776 * @class Roo.data.JsonStore
13777 * Small helper class to make creating Stores for JSON data easier. <br/>
13779 var store = new Roo.data.JsonStore({
13780 url: 'get-images.php',
13782 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13785 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13786 * JsonReader and HttpProxy (unless inline data is provided).</b>
13787 * @cfg {Array} fields An array of field definition objects, or field name strings.
13789 * @param {Object} config
13791 Roo.data.JsonStore = function(c){
13792 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13793 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13794 reader: new Roo.data.JsonReader(c, c.fields)
13797 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13799 * Ext JS Library 1.1.1
13800 * Copyright(c) 2006-2007, Ext JS, LLC.
13802 * Originally Released Under LGPL - original licence link has changed is not relivant.
13805 * <script type="text/javascript">
13809 Roo.data.Field = function(config){
13810 if(typeof config == "string"){
13811 config = {name: config};
13813 Roo.apply(this, config);
13816 this.type = "auto";
13819 var st = Roo.data.SortTypes;
13820 // named sortTypes are supported, here we look them up
13821 if(typeof this.sortType == "string"){
13822 this.sortType = st[this.sortType];
13825 // set default sortType for strings and dates
13826 if(!this.sortType){
13829 this.sortType = st.asUCString;
13832 this.sortType = st.asDate;
13835 this.sortType = st.none;
13840 var stripRe = /[\$,%]/g;
13842 // prebuilt conversion function for this field, instead of
13843 // switching every time we're reading a value
13845 var cv, dateFormat = this.dateFormat;
13850 cv = function(v){ return v; };
13853 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
13857 return v !== undefined && v !== null && v !== '' ?
13858 parseInt(String(v).replace(stripRe, ""), 10) : '';
13863 return v !== undefined && v !== null && v !== '' ?
13864 parseFloat(String(v).replace(stripRe, ""), 10) : '';
13869 cv = function(v){ return v === true || v === "true" || v == 1; };
13876 if(v instanceof Date){
13880 if(dateFormat == "timestamp"){
13881 return new Date(v*1000);
13883 return Date.parseDate(v, dateFormat);
13885 var parsed = Date.parse(v);
13886 return parsed ? new Date(parsed) : null;
13895 Roo.data.Field.prototype = {
13903 * Ext JS Library 1.1.1
13904 * Copyright(c) 2006-2007, Ext JS, LLC.
13906 * Originally Released Under LGPL - original licence link has changed is not relivant.
13909 * <script type="text/javascript">
13912 // Base class for reading structured data from a data source. This class is intended to be
13913 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
13916 * @class Roo.data.DataReader
13917 * Base class for reading structured data from a data source. This class is intended to be
13918 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
13921 Roo.data.DataReader = function(meta, recordType){
13925 this.recordType = recordType instanceof Array ?
13926 Roo.data.Record.create(recordType) : recordType;
13929 Roo.data.DataReader.prototype = {
13932 readerType : 'Data',
13934 * Create an empty record
13935 * @param {Object} data (optional) - overlay some values
13936 * @return {Roo.data.Record} record created.
13938 newRow : function(d) {
13940 this.recordType.prototype.fields.each(function(c) {
13942 case 'int' : da[c.name] = 0; break;
13943 case 'date' : da[c.name] = new Date(); break;
13944 case 'float' : da[c.name] = 0.0; break;
13945 case 'boolean' : da[c.name] = false; break;
13946 default : da[c.name] = ""; break;
13950 return new this.recordType(Roo.apply(da, d));
13956 * Ext JS Library 1.1.1
13957 * Copyright(c) 2006-2007, Ext JS, LLC.
13959 * Originally Released Under LGPL - original licence link has changed is not relivant.
13962 * <script type="text/javascript">
13966 * @class Roo.data.DataProxy
13967 * @extends Roo.data.Observable
13968 * This class is an abstract base class for implementations which provide retrieval of
13969 * unformatted data objects.<br>
13971 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
13972 * (of the appropriate type which knows how to parse the data object) to provide a block of
13973 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
13975 * Custom implementations must implement the load method as described in
13976 * {@link Roo.data.HttpProxy#load}.
13978 Roo.data.DataProxy = function(){
13981 * @event beforeload
13982 * Fires before a network request is made to retrieve a data object.
13983 * @param {Object} This DataProxy object.
13984 * @param {Object} params The params parameter to the load function.
13989 * Fires before the load method's callback is called.
13990 * @param {Object} This DataProxy object.
13991 * @param {Object} o The data object.
13992 * @param {Object} arg The callback argument object passed to the load function.
13996 * @event loadexception
13997 * Fires if an Exception occurs during data retrieval.
13998 * @param {Object} This DataProxy object.
13999 * @param {Object} o The data object.
14000 * @param {Object} arg The callback argument object passed to the load function.
14001 * @param {Object} e The Exception.
14003 loadexception : true
14005 Roo.data.DataProxy.superclass.constructor.call(this);
14008 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14011 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14015 * Ext JS Library 1.1.1
14016 * Copyright(c) 2006-2007, Ext JS, LLC.
14018 * Originally Released Under LGPL - original licence link has changed is not relivant.
14021 * <script type="text/javascript">
14024 * @class Roo.data.MemoryProxy
14025 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14026 * to the Reader when its load method is called.
14028 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14030 Roo.data.MemoryProxy = function(data){
14034 Roo.data.MemoryProxy.superclass.constructor.call(this);
14038 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14041 * Load data from the requested source (in this case an in-memory
14042 * data object passed to the constructor), read the data object into
14043 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14044 * process that block using the passed callback.
14045 * @param {Object} params This parameter is not used by the MemoryProxy class.
14046 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14047 * object into a block of Roo.data.Records.
14048 * @param {Function} callback The function into which to pass the block of Roo.data.records.
14049 * The function must be passed <ul>
14050 * <li>The Record block object</li>
14051 * <li>The "arg" argument from the load function</li>
14052 * <li>A boolean success indicator</li>
14054 * @param {Object} scope The scope in which to call the callback
14055 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14057 load : function(params, reader, callback, scope, arg){
14058 params = params || {};
14061 result = reader.readRecords(params.data ? params.data :this.data);
14063 this.fireEvent("loadexception", this, arg, null, e);
14064 callback.call(scope, null, arg, false);
14067 callback.call(scope, result, arg, true);
14071 update : function(params, records){
14076 * Ext JS Library 1.1.1
14077 * Copyright(c) 2006-2007, Ext JS, LLC.
14079 * Originally Released Under LGPL - original licence link has changed is not relivant.
14082 * <script type="text/javascript">
14085 * @class Roo.data.HttpProxy
14086 * @extends Roo.data.DataProxy
14087 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14088 * configured to reference a certain URL.<br><br>
14090 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14091 * from which the running page was served.<br><br>
14093 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14095 * Be aware that to enable the browser to parse an XML document, the server must set
14096 * the Content-Type header in the HTTP response to "text/xml".
14098 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14099 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
14100 * will be used to make the request.
14102 Roo.data.HttpProxy = function(conn){
14103 Roo.data.HttpProxy.superclass.constructor.call(this);
14104 // is conn a conn config or a real conn?
14106 this.useAjax = !conn || !conn.events;
14110 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14111 // thse are take from connection...
14114 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14117 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14118 * extra parameters to each request made by this object. (defaults to undefined)
14121 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14122 * to each request made by this object. (defaults to undefined)
14125 * @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)
14128 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14131 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14137 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14141 * Return the {@link Roo.data.Connection} object being used by this Proxy.
14142 * @return {Connection} The Connection object. This object may be used to subscribe to events on
14143 * a finer-grained basis than the DataProxy events.
14145 getConnection : function(){
14146 return this.useAjax ? Roo.Ajax : this.conn;
14150 * Load data from the configured {@link Roo.data.Connection}, read the data object into
14151 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14152 * process that block using the passed callback.
14153 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14154 * for the request to the remote server.
14155 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14156 * object into a block of Roo.data.Records.
14157 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14158 * The function must be passed <ul>
14159 * <li>The Record block object</li>
14160 * <li>The "arg" argument from the load function</li>
14161 * <li>A boolean success indicator</li>
14163 * @param {Object} scope The scope in which to call the callback
14164 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14166 load : function(params, reader, callback, scope, arg){
14167 if(this.fireEvent("beforeload", this, params) !== false){
14169 params : params || {},
14171 callback : callback,
14176 callback : this.loadResponse,
14180 Roo.applyIf(o, this.conn);
14181 if(this.activeRequest){
14182 Roo.Ajax.abort(this.activeRequest);
14184 this.activeRequest = Roo.Ajax.request(o);
14186 this.conn.request(o);
14189 callback.call(scope||this, null, arg, false);
14194 loadResponse : function(o, success, response){
14195 delete this.activeRequest;
14197 this.fireEvent("loadexception", this, o, response);
14198 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14203 result = o.reader.read(response);
14205 this.fireEvent("loadexception", this, o, response, e);
14206 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14210 this.fireEvent("load", this, o, o.request.arg);
14211 o.request.callback.call(o.request.scope, result, o.request.arg, true);
14215 update : function(dataSet){
14220 updateResponse : function(dataSet){
14225 * Ext JS Library 1.1.1
14226 * Copyright(c) 2006-2007, Ext JS, LLC.
14228 * Originally Released Under LGPL - original licence link has changed is not relivant.
14231 * <script type="text/javascript">
14235 * @class Roo.data.ScriptTagProxy
14236 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14237 * other than the originating domain of the running page.<br><br>
14239 * <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
14240 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14242 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14243 * source code that is used as the source inside a <script> tag.<br><br>
14245 * In order for the browser to process the returned data, the server must wrap the data object
14246 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14247 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14248 * depending on whether the callback name was passed:
14251 boolean scriptTag = false;
14252 String cb = request.getParameter("callback");
14255 response.setContentType("text/javascript");
14257 response.setContentType("application/x-json");
14259 Writer out = response.getWriter();
14261 out.write(cb + "(");
14263 out.print(dataBlock.toJsonString());
14270 * @param {Object} config A configuration object.
14272 Roo.data.ScriptTagProxy = function(config){
14273 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14274 Roo.apply(this, config);
14275 this.head = document.getElementsByTagName("head")[0];
14278 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14280 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14282 * @cfg {String} url The URL from which to request the data object.
14285 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14289 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14290 * the server the name of the callback function set up by the load call to process the returned data object.
14291 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14292 * javascript output which calls this named function passing the data object as its only parameter.
14294 callbackParam : "callback",
14296 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14297 * name to the request.
14302 * Load data from the configured URL, read the data object into
14303 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14304 * process that block using the passed callback.
14305 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14306 * for the request to the remote server.
14307 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14308 * object into a block of Roo.data.Records.
14309 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14310 * The function must be passed <ul>
14311 * <li>The Record block object</li>
14312 * <li>The "arg" argument from the load function</li>
14313 * <li>A boolean success indicator</li>
14315 * @param {Object} scope The scope in which to call the callback
14316 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14318 load : function(params, reader, callback, scope, arg){
14319 if(this.fireEvent("beforeload", this, params) !== false){
14321 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14323 var url = this.url;
14324 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14326 url += "&_dc=" + (new Date().getTime());
14328 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14331 cb : "stcCallback"+transId,
14332 scriptId : "stcScript"+transId,
14336 callback : callback,
14342 window[trans.cb] = function(o){
14343 conn.handleResponse(o, trans);
14346 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14348 if(this.autoAbort !== false){
14352 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14354 var script = document.createElement("script");
14355 script.setAttribute("src", url);
14356 script.setAttribute("type", "text/javascript");
14357 script.setAttribute("id", trans.scriptId);
14358 this.head.appendChild(script);
14360 this.trans = trans;
14362 callback.call(scope||this, null, arg, false);
14367 isLoading : function(){
14368 return this.trans ? true : false;
14372 * Abort the current server request.
14374 abort : function(){
14375 if(this.isLoading()){
14376 this.destroyTrans(this.trans);
14381 destroyTrans : function(trans, isLoaded){
14382 this.head.removeChild(document.getElementById(trans.scriptId));
14383 clearTimeout(trans.timeoutId);
14385 window[trans.cb] = undefined;
14387 delete window[trans.cb];
14390 // if hasn't been loaded, wait for load to remove it to prevent script error
14391 window[trans.cb] = function(){
14392 window[trans.cb] = undefined;
14394 delete window[trans.cb];
14401 handleResponse : function(o, trans){
14402 this.trans = false;
14403 this.destroyTrans(trans, true);
14406 result = trans.reader.readRecords(o);
14408 this.fireEvent("loadexception", this, o, trans.arg, e);
14409 trans.callback.call(trans.scope||window, null, trans.arg, false);
14412 this.fireEvent("load", this, o, trans.arg);
14413 trans.callback.call(trans.scope||window, result, trans.arg, true);
14417 handleFailure : function(trans){
14418 this.trans = false;
14419 this.destroyTrans(trans, false);
14420 this.fireEvent("loadexception", this, null, trans.arg);
14421 trans.callback.call(trans.scope||window, null, trans.arg, false);
14425 * Ext JS Library 1.1.1
14426 * Copyright(c) 2006-2007, Ext JS, LLC.
14428 * Originally Released Under LGPL - original licence link has changed is not relivant.
14431 * <script type="text/javascript">
14435 * @class Roo.data.JsonReader
14436 * @extends Roo.data.DataReader
14437 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14438 * based on mappings in a provided Roo.data.Record constructor.
14440 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14441 * in the reply previously.
14446 var RecordDef = Roo.data.Record.create([
14447 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
14448 {name: 'occupation'} // This field will use "occupation" as the mapping.
14450 var myReader = new Roo.data.JsonReader({
14451 totalProperty: "results", // The property which contains the total dataset size (optional)
14452 root: "rows", // The property which contains an Array of row objects
14453 id: "id" // The property within each row object that provides an ID for the record (optional)
14457 * This would consume a JSON file like this:
14459 { 'results': 2, 'rows': [
14460 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14461 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14464 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14465 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14466 * paged from the remote server.
14467 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14468 * @cfg {String} root name of the property which contains the Array of row objects.
14469 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14470 * @cfg {Array} fields Array of field definition objects
14472 * Create a new JsonReader
14473 * @param {Object} meta Metadata configuration options
14474 * @param {Object} recordType Either an Array of field definition objects,
14475 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14477 Roo.data.JsonReader = function(meta, recordType){
14480 // set some defaults:
14481 Roo.applyIf(meta, {
14482 totalProperty: 'total',
14483 successProperty : 'success',
14488 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14490 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14492 readerType : 'Json',
14495 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
14496 * Used by Store query builder to append _requestMeta to params.
14499 metaFromRemote : false,
14501 * This method is only used by a DataProxy which has retrieved data from a remote server.
14502 * @param {Object} response The XHR object which contains the JSON data in its responseText.
14503 * @return {Object} data A data block which is used by an Roo.data.Store object as
14504 * a cache of Roo.data.Records.
14506 read : function(response){
14507 var json = response.responseText;
14509 var o = /* eval:var:o */ eval("("+json+")");
14511 throw {message: "JsonReader.read: Json object not found"};
14517 this.metaFromRemote = true;
14518 this.meta = o.metaData;
14519 this.recordType = Roo.data.Record.create(o.metaData.fields);
14520 this.onMetaChange(this.meta, this.recordType, o);
14522 return this.readRecords(o);
14525 // private function a store will implement
14526 onMetaChange : function(meta, recordType, o){
14533 simpleAccess: function(obj, subsc) {
14540 getJsonAccessor: function(){
14542 return function(expr) {
14544 return(re.test(expr))
14545 ? new Function("obj", "return obj." + expr)
14550 return Roo.emptyFn;
14555 * Create a data block containing Roo.data.Records from an XML document.
14556 * @param {Object} o An object which contains an Array of row objects in the property specified
14557 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14558 * which contains the total size of the dataset.
14559 * @return {Object} data A data block which is used by an Roo.data.Store object as
14560 * a cache of Roo.data.Records.
14562 readRecords : function(o){
14564 * After any data loads, the raw JSON data is available for further custom processing.
14568 var s = this.meta, Record = this.recordType,
14569 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14571 // Generate extraction functions for the totalProperty, the root, the id, and for each field
14573 if(s.totalProperty) {
14574 this.getTotal = this.getJsonAccessor(s.totalProperty);
14576 if(s.successProperty) {
14577 this.getSuccess = this.getJsonAccessor(s.successProperty);
14579 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14581 var g = this.getJsonAccessor(s.id);
14582 this.getId = function(rec) {
14584 return (r === undefined || r === "") ? null : r;
14587 this.getId = function(){return null;};
14590 for(var jj = 0; jj < fl; jj++){
14592 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14593 this.ef[jj] = this.getJsonAccessor(map);
14597 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14598 if(s.totalProperty){
14599 var vt = parseInt(this.getTotal(o), 10);
14604 if(s.successProperty){
14605 var vs = this.getSuccess(o);
14606 if(vs === false || vs === 'false'){
14611 for(var i = 0; i < c; i++){
14614 var id = this.getId(n);
14615 for(var j = 0; j < fl; j++){
14617 var v = this.ef[j](n);
14619 Roo.log('missing convert for ' + f.name);
14623 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14625 var record = new Record(values, id);
14627 records[i] = record;
14633 totalRecords : totalRecords
14636 // used when loading children.. @see loadDataFromChildren
14637 toLoadData: function(rec)
14639 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14640 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14641 return { data : data, total : data.length };
14646 * Ext JS Library 1.1.1
14647 * Copyright(c) 2006-2007, Ext JS, LLC.
14649 * Originally Released Under LGPL - original licence link has changed is not relivant.
14652 * <script type="text/javascript">
14656 * @class Roo.data.ArrayReader
14657 * @extends Roo.data.DataReader
14658 * Data reader class to create an Array of Roo.data.Record objects from an Array.
14659 * Each element of that Array represents a row of data fields. The
14660 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14661 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14665 var RecordDef = Roo.data.Record.create([
14666 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
14667 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
14669 var myReader = new Roo.data.ArrayReader({
14670 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
14674 * This would consume an Array like this:
14676 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14680 * Create a new JsonReader
14681 * @param {Object} meta Metadata configuration options.
14682 * @param {Object|Array} recordType Either an Array of field definition objects
14684 * @cfg {Array} fields Array of field definition objects
14685 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14686 * as specified to {@link Roo.data.Record#create},
14687 * or an {@link Roo.data.Record} object
14690 * created using {@link Roo.data.Record#create}.
14692 Roo.data.ArrayReader = function(meta, recordType)
14694 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14697 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14700 * Create a data block containing Roo.data.Records from an XML document.
14701 * @param {Object} o An Array of row objects which represents the dataset.
14702 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14703 * a cache of Roo.data.Records.
14705 readRecords : function(o)
14707 var sid = this.meta ? this.meta.id : null;
14708 var recordType = this.recordType, fields = recordType.prototype.fields;
14711 for(var i = 0; i < root.length; i++){
14714 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14715 for(var j = 0, jlen = fields.length; j < jlen; j++){
14716 var f = fields.items[j];
14717 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14718 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14720 values[f.name] = v;
14722 var record = new recordType(values, id);
14724 records[records.length] = record;
14728 totalRecords : records.length
14731 // used when loading children.. @see loadDataFromChildren
14732 toLoadData: function(rec)
14734 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14735 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14746 * @class Roo.bootstrap.ComboBox
14747 * @extends Roo.bootstrap.TriggerField
14748 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14749 * @cfg {Boolean} append (true|false) default false
14750 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14751 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14752 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14753 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14754 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14755 * @cfg {Boolean} animate default true
14756 * @cfg {Boolean} emptyResultText only for touch device
14757 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14758 * @cfg {String} emptyTitle default ''
14759 * @cfg {Number} width fixed with? experimental
14761 * Create a new ComboBox.
14762 * @param {Object} config Configuration options
14764 Roo.bootstrap.ComboBox = function(config){
14765 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14769 * Fires when the dropdown list is expanded
14770 * @param {Roo.bootstrap.ComboBox} combo This combo box
14775 * Fires when the dropdown list is collapsed
14776 * @param {Roo.bootstrap.ComboBox} combo This combo box
14780 * @event beforeselect
14781 * Fires before a list item is selected. Return false to cancel the selection.
14782 * @param {Roo.bootstrap.ComboBox} combo This combo box
14783 * @param {Roo.data.Record} record The data record returned from the underlying store
14784 * @param {Number} index The index of the selected item in the dropdown list
14786 'beforeselect' : true,
14789 * Fires when a list item is selected
14790 * @param {Roo.bootstrap.ComboBox} combo This combo box
14791 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14792 * @param {Number} index The index of the selected item in the dropdown list
14796 * @event beforequery
14797 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14798 * The event object passed has these properties:
14799 * @param {Roo.bootstrap.ComboBox} combo This combo box
14800 * @param {String} query The query
14801 * @param {Boolean} forceAll true to force "all" query
14802 * @param {Boolean} cancel true to cancel the query
14803 * @param {Object} e The query event object
14805 'beforequery': true,
14808 * Fires when the 'add' icon is pressed (add a listener to enable add button)
14809 * @param {Roo.bootstrap.ComboBox} combo This combo box
14814 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14815 * @param {Roo.bootstrap.ComboBox} combo This combo box
14816 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14821 * Fires when the remove value from the combobox array
14822 * @param {Roo.bootstrap.ComboBox} combo This combo box
14826 * @event afterremove
14827 * Fires when the remove value from the combobox array
14828 * @param {Roo.bootstrap.ComboBox} combo This combo box
14830 'afterremove' : true,
14832 * @event specialfilter
14833 * Fires when specialfilter
14834 * @param {Roo.bootstrap.ComboBox} combo This combo box
14836 'specialfilter' : true,
14839 * Fires when tick the element
14840 * @param {Roo.bootstrap.ComboBox} combo This combo box
14844 * @event touchviewdisplay
14845 * Fires when touch view require special display (default is using displayField)
14846 * @param {Roo.bootstrap.ComboBox} combo This combo box
14847 * @param {Object} cfg set html .
14849 'touchviewdisplay' : true
14854 this.tickItems = [];
14856 this.selectedIndex = -1;
14857 if(this.mode == 'local'){
14858 if(config.queryDelay === undefined){
14859 this.queryDelay = 10;
14861 if(config.minChars === undefined){
14867 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
14870 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
14871 * rendering into an Roo.Editor, defaults to false)
14874 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
14875 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
14878 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
14881 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
14882 * the dropdown list (defaults to undefined, with no header element)
14886 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
14890 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
14892 listWidth: undefined,
14894 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
14895 * mode = 'remote' or 'text' if mode = 'local')
14897 displayField: undefined,
14900 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
14901 * mode = 'remote' or 'value' if mode = 'local').
14902 * Note: use of a valueField requires the user make a selection
14903 * in order for a value to be mapped.
14905 valueField: undefined,
14907 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
14912 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
14913 * field's data value (defaults to the underlying DOM element's name)
14915 hiddenName: undefined,
14917 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
14921 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
14923 selectedClass: 'active',
14926 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14930 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
14931 * anchor positions (defaults to 'tl-bl')
14933 listAlign: 'tl-bl?',
14935 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
14939 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
14940 * query specified by the allQuery config option (defaults to 'query')
14942 triggerAction: 'query',
14944 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
14945 * (defaults to 4, does not apply if editable = false)
14949 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
14950 * delay (typeAheadDelay) if it matches a known value (defaults to false)
14954 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
14955 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
14959 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
14960 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
14964 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
14965 * when editable = true (defaults to false)
14967 selectOnFocus:false,
14969 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
14971 queryParam: 'query',
14973 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
14974 * when mode = 'remote' (defaults to 'Loading...')
14976 loadingText: 'Loading...',
14978 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
14982 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
14986 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
14987 * traditional select (defaults to true)
14991 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
14995 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
14999 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15000 * listWidth has a higher value)
15004 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15005 * allow the user to set arbitrary text into the field (defaults to false)
15007 forceSelection:false,
15009 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15010 * if typeAhead = true (defaults to 250)
15012 typeAheadDelay : 250,
15014 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15015 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15017 valueNotFoundText : undefined,
15019 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15021 blockFocus : false,
15024 * @cfg {Boolean} disableClear Disable showing of clear button.
15026 disableClear : false,
15028 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
15030 alwaysQuery : false,
15033 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
15038 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15040 invalidClass : "has-warning",
15043 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15045 validClass : "has-success",
15048 * @cfg {Boolean} specialFilter (true|false) special filter default false
15050 specialFilter : false,
15053 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15055 mobileTouchView : true,
15058 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15060 useNativeIOS : false,
15063 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15065 mobile_restrict_height : false,
15067 ios_options : false,
15079 btnPosition : 'right',
15080 triggerList : true,
15081 showToggleBtn : true,
15083 emptyResultText: 'Empty',
15084 triggerText : 'Select',
15088 // element that contains real text value.. (when hidden is used..)
15090 getAutoCreate : function()
15095 * Render classic select for iso
15098 if(Roo.isIOS && this.useNativeIOS){
15099 cfg = this.getAutoCreateNativeIOS();
15107 if(Roo.isTouch && this.mobileTouchView){
15108 cfg = this.getAutoCreateTouchView();
15115 if(!this.tickable){
15116 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15121 * ComboBox with tickable selections
15124 var align = this.labelAlign || this.parentLabelAlign();
15127 cls : 'form-group roo-combobox-tickable' //input-group
15130 var btn_text_select = '';
15131 var btn_text_done = '';
15132 var btn_text_cancel = '';
15134 if (this.btn_text_show) {
15135 btn_text_select = 'Select';
15136 btn_text_done = 'Done';
15137 btn_text_cancel = 'Cancel';
15142 cls : 'tickable-buttons',
15147 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15148 //html : this.triggerText
15149 html: btn_text_select
15155 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15157 html: btn_text_done
15163 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15165 html: btn_text_cancel
15171 buttons.cn.unshift({
15173 cls: 'roo-select2-search-field-input'
15179 Roo.each(buttons.cn, function(c){
15181 c.cls += ' btn-' + _this.size;
15184 if (_this.disabled) {
15191 style : 'display: contents',
15196 cls: 'form-hidden-field'
15200 cls: 'roo-select2-choices',
15204 cls: 'roo-select2-search-field',
15215 cls: 'roo-select2-container input-group roo-select2-container-multi',
15221 // cls: 'typeahead typeahead-long dropdown-menu',
15222 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
15227 if(this.hasFeedback && !this.allowBlank){
15231 cls: 'glyphicon form-control-feedback'
15234 combobox.cn.push(feedback);
15241 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15242 tooltip : 'This field is required'
15244 if (Roo.bootstrap.version == 4) {
15247 style : 'display:none'
15250 if (align ==='left' && this.fieldLabel.length) {
15252 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
15259 cls : 'control-label col-form-label',
15260 html : this.fieldLabel
15272 var labelCfg = cfg.cn[1];
15273 var contentCfg = cfg.cn[2];
15276 if(this.indicatorpos == 'right'){
15282 cls : 'control-label col-form-label',
15286 html : this.fieldLabel
15302 labelCfg = cfg.cn[0];
15303 contentCfg = cfg.cn[1];
15307 if(this.labelWidth > 12){
15308 labelCfg.style = "width: " + this.labelWidth + 'px';
15310 if(this.width * 1 > 0){
15311 contentCfg.style = "width: " + this.width + 'px';
15313 if(this.labelWidth < 13 && this.labelmd == 0){
15314 this.labelmd = this.labelWidth;
15317 if(this.labellg > 0){
15318 labelCfg.cls += ' col-lg-' + this.labellg;
15319 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15322 if(this.labelmd > 0){
15323 labelCfg.cls += ' col-md-' + this.labelmd;
15324 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15327 if(this.labelsm > 0){
15328 labelCfg.cls += ' col-sm-' + this.labelsm;
15329 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15332 if(this.labelxs > 0){
15333 labelCfg.cls += ' col-xs-' + this.labelxs;
15334 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15338 } else if ( this.fieldLabel.length) {
15339 // Roo.log(" label");
15344 //cls : 'input-group-addon',
15345 html : this.fieldLabel
15350 if(this.indicatorpos == 'right'){
15354 //cls : 'input-group-addon',
15355 html : this.fieldLabel
15365 // Roo.log(" no label && no align");
15372 ['xs','sm','md','lg'].map(function(size){
15373 if (settings[size]) {
15374 cfg.cls += ' col-' + size + '-' + settings[size];
15382 _initEventsCalled : false,
15385 initEvents: function()
15387 if (this._initEventsCalled) { // as we call render... prevent looping...
15390 this._initEventsCalled = true;
15393 throw "can not find store for combo";
15396 this.indicator = this.indicatorEl();
15398 this.store = Roo.factory(this.store, Roo.data);
15399 this.store.parent = this;
15401 // if we are building from html. then this element is so complex, that we can not really
15402 // use the rendered HTML.
15403 // so we have to trash and replace the previous code.
15404 if (Roo.XComponent.build_from_html) {
15405 // remove this element....
15406 var e = this.el.dom, k=0;
15407 while (e ) { e = e.previousSibling; ++k;}
15412 this.rendered = false;
15414 this.render(this.parent().getChildContainer(true), k);
15417 if(Roo.isIOS && this.useNativeIOS){
15418 this.initIOSView();
15426 if(Roo.isTouch && this.mobileTouchView){
15427 this.initTouchView();
15432 this.initTickableEvents();
15436 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15438 if(this.hiddenName){
15440 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15442 this.hiddenField.dom.value =
15443 this.hiddenValue !== undefined ? this.hiddenValue :
15444 this.value !== undefined ? this.value : '';
15446 // prevent input submission
15447 this.el.dom.removeAttribute('name');
15448 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15453 // this.el.dom.setAttribute('autocomplete', 'off');
15456 var cls = 'x-combo-list';
15458 //this.list = new Roo.Layer({
15459 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15465 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15466 _this.list.setWidth(lw);
15469 this.list.on('mouseover', this.onViewOver, this);
15470 this.list.on('mousemove', this.onViewMove, this);
15471 this.list.on('scroll', this.onViewScroll, this);
15474 this.list.swallowEvent('mousewheel');
15475 this.assetHeight = 0;
15478 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15479 this.assetHeight += this.header.getHeight();
15482 this.innerList = this.list.createChild({cls:cls+'-inner'});
15483 this.innerList.on('mouseover', this.onViewOver, this);
15484 this.innerList.on('mousemove', this.onViewMove, this);
15485 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15487 if(this.allowBlank && !this.pageSize && !this.disableClear){
15488 this.footer = this.list.createChild({cls:cls+'-ft'});
15489 this.pageTb = new Roo.Toolbar(this.footer);
15493 this.footer = this.list.createChild({cls:cls+'-ft'});
15494 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15495 {pageSize: this.pageSize});
15499 if (this.pageTb && this.allowBlank && !this.disableClear) {
15501 this.pageTb.add(new Roo.Toolbar.Fill(), {
15502 cls: 'x-btn-icon x-btn-clear',
15504 handler: function()
15507 _this.clearValue();
15508 _this.onSelect(false, -1);
15513 this.assetHeight += this.footer.getHeight();
15518 this.tpl = Roo.bootstrap.version == 4 ?
15519 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
15520 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15523 this.view = new Roo.View(this.list, this.tpl, {
15524 singleSelect:true, store: this.store, selectedClass: this.selectedClass
15526 //this.view.wrapEl.setDisplayed(false);
15527 this.view.on('click', this.onViewClick, this);
15530 this.store.on('beforeload', this.onBeforeLoad, this);
15531 this.store.on('load', this.onLoad, this);
15532 this.store.on('loadexception', this.onLoadException, this);
15534 if(this.resizable){
15535 this.resizer = new Roo.Resizable(this.list, {
15536 pinned:true, handles:'se'
15538 this.resizer.on('resize', function(r, w, h){
15539 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15540 this.listWidth = w;
15541 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15542 this.restrictHeight();
15544 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15547 if(!this.editable){
15548 this.editable = true;
15549 this.setEditable(false);
15554 if (typeof(this.events.add.listeners) != 'undefined') {
15556 this.addicon = this.wrap.createChild(
15557 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
15559 this.addicon.on('click', function(e) {
15560 this.fireEvent('add', this);
15563 if (typeof(this.events.edit.listeners) != 'undefined') {
15565 this.editicon = this.wrap.createChild(
15566 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
15567 if (this.addicon) {
15568 this.editicon.setStyle('margin-left', '40px');
15570 this.editicon.on('click', function(e) {
15572 // we fire even if inothing is selected..
15573 this.fireEvent('edit', this, this.lastData );
15579 this.keyNav = new Roo.KeyNav(this.inputEl(), {
15580 "up" : function(e){
15581 this.inKeyMode = true;
15585 "down" : function(e){
15586 if(!this.isExpanded()){
15587 this.onTriggerClick();
15589 this.inKeyMode = true;
15594 "enter" : function(e){
15595 // this.onViewClick();
15599 if(this.fireEvent("specialkey", this, e)){
15600 this.onViewClick(false);
15606 "esc" : function(e){
15610 "tab" : function(e){
15613 if(this.fireEvent("specialkey", this, e)){
15614 this.onViewClick(false);
15622 doRelay : function(foo, bar, hname){
15623 if(hname == 'down' || this.scope.isExpanded()){
15624 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15633 this.queryDelay = Math.max(this.queryDelay || 10,
15634 this.mode == 'local' ? 10 : 250);
15637 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15639 if(this.typeAhead){
15640 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15642 if(this.editable !== false){
15643 this.inputEl().on("keyup", this.onKeyUp, this);
15645 if(this.forceSelection){
15646 this.inputEl().on('blur', this.doForce, this);
15650 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15651 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15655 initTickableEvents: function()
15659 if(this.hiddenName){
15661 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15663 this.hiddenField.dom.value =
15664 this.hiddenValue !== undefined ? this.hiddenValue :
15665 this.value !== undefined ? this.value : '';
15667 // prevent input submission
15668 this.el.dom.removeAttribute('name');
15669 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15674 // this.list = this.el.select('ul.dropdown-menu',true).first();
15676 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15677 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15678 if(this.triggerList){
15679 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15682 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15683 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15685 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15686 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15688 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15689 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15691 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15692 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15693 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15696 this.cancelBtn.hide();
15701 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15702 _this.list.setWidth(lw);
15705 this.list.on('mouseover', this.onViewOver, this);
15706 this.list.on('mousemove', this.onViewMove, this);
15708 this.list.on('scroll', this.onViewScroll, this);
15711 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
15712 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15715 this.view = new Roo.View(this.list, this.tpl, {
15720 selectedClass: this.selectedClass
15723 //this.view.wrapEl.setDisplayed(false);
15724 this.view.on('click', this.onViewClick, this);
15728 this.store.on('beforeload', this.onBeforeLoad, this);
15729 this.store.on('load', this.onLoad, this);
15730 this.store.on('loadexception', this.onLoadException, this);
15733 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15734 "up" : function(e){
15735 this.inKeyMode = true;
15739 "down" : function(e){
15740 this.inKeyMode = true;
15744 "enter" : function(e){
15745 if(this.fireEvent("specialkey", this, e)){
15746 this.onViewClick(false);
15752 "esc" : function(e){
15753 this.onTickableFooterButtonClick(e, false, false);
15756 "tab" : function(e){
15757 this.fireEvent("specialkey", this, e);
15759 this.onTickableFooterButtonClick(e, false, false);
15766 doRelay : function(e, fn, key){
15767 if(this.scope.isExpanded()){
15768 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15777 this.queryDelay = Math.max(this.queryDelay || 10,
15778 this.mode == 'local' ? 10 : 250);
15781 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15783 if(this.typeAhead){
15784 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15787 if(this.editable !== false){
15788 this.tickableInputEl().on("keyup", this.onKeyUp, this);
15791 this.indicator = this.indicatorEl();
15793 if(this.indicator){
15794 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15795 this.indicator.hide();
15800 onDestroy : function(){
15802 this.view.setStore(null);
15803 this.view.el.removeAllListeners();
15804 this.view.el.remove();
15805 this.view.purgeListeners();
15808 this.list.dom.innerHTML = '';
15812 this.store.un('beforeload', this.onBeforeLoad, this);
15813 this.store.un('load', this.onLoad, this);
15814 this.store.un('loadexception', this.onLoadException, this);
15816 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15820 fireKey : function(e){
15821 if(e.isNavKeyPress() && !this.list.isVisible()){
15822 this.fireEvent("specialkey", this, e);
15827 onResize: function(w, h)
15831 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
15833 // if(typeof w != 'number'){
15834 // // we do not handle it!?!?
15837 // var tw = this.trigger.getWidth();
15838 // // tw += this.addicon ? this.addicon.getWidth() : 0;
15839 // // tw += this.editicon ? this.editicon.getWidth() : 0;
15841 // this.inputEl().setWidth( this.adjustWidth('input', x));
15843 // //this.trigger.setStyle('left', x+'px');
15845 // if(this.list && this.listWidth === undefined){
15846 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
15847 // this.list.setWidth(lw);
15848 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15856 * Allow or prevent the user from directly editing the field text. If false is passed,
15857 * the user will only be able to select from the items defined in the dropdown list. This method
15858 * is the runtime equivalent of setting the 'editable' config option at config time.
15859 * @param {Boolean} value True to allow the user to directly edit the field text
15861 setEditable : function(value){
15862 if(value == this.editable){
15865 this.editable = value;
15867 this.inputEl().dom.setAttribute('readOnly', true);
15868 this.inputEl().on('mousedown', this.onTriggerClick, this);
15869 this.inputEl().addClass('x-combo-noedit');
15871 this.inputEl().dom.setAttribute('readOnly', false);
15872 this.inputEl().un('mousedown', this.onTriggerClick, this);
15873 this.inputEl().removeClass('x-combo-noedit');
15879 onBeforeLoad : function(combo,opts){
15880 if(!this.hasFocus){
15884 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
15886 this.restrictHeight();
15887 this.selectedIndex = -1;
15891 onLoad : function(){
15893 this.hasQuery = false;
15895 if(!this.hasFocus){
15899 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
15900 this.loading.hide();
15903 if(this.store.getCount() > 0){
15906 this.restrictHeight();
15907 if(this.lastQuery == this.allQuery){
15908 if(this.editable && !this.tickable){
15909 this.inputEl().dom.select();
15913 !this.selectByValue(this.value, true) &&
15916 !this.store.lastOptions ||
15917 typeof(this.store.lastOptions.add) == 'undefined' ||
15918 this.store.lastOptions.add != true
15921 this.select(0, true);
15924 if(this.autoFocus){
15927 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
15928 this.taTask.delay(this.typeAheadDelay);
15932 this.onEmptyResults();
15938 onLoadException : function()
15940 this.hasQuery = false;
15942 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
15943 this.loading.hide();
15946 if(this.tickable && this.editable){
15951 // only causes errors at present
15952 //Roo.log(this.store.reader.jsonData);
15953 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
15955 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
15961 onTypeAhead : function(){
15962 if(this.store.getCount() > 0){
15963 var r = this.store.getAt(0);
15964 var newValue = r.data[this.displayField];
15965 var len = newValue.length;
15966 var selStart = this.getRawValue().length;
15968 if(selStart != len){
15969 this.setRawValue(newValue);
15970 this.selectText(selStart, newValue.length);
15976 onSelect : function(record, index){
15978 if(this.fireEvent('beforeselect', this, record, index) !== false){
15980 this.setFromData(index > -1 ? record.data : false);
15983 this.fireEvent('select', this, record, index);
15988 * Returns the currently selected field value or empty string if no value is set.
15989 * @return {String} value The selected value
15991 getValue : function()
15993 if(Roo.isIOS && this.useNativeIOS){
15994 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
15998 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16001 if(this.valueField){
16002 return typeof this.value != 'undefined' ? this.value : '';
16004 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16008 getRawValue : function()
16010 if(Roo.isIOS && this.useNativeIOS){
16011 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16014 var v = this.inputEl().getValue();
16020 * Clears any text/value currently set in the field
16022 clearValue : function(){
16024 if(this.hiddenField){
16025 this.hiddenField.dom.value = '';
16028 this.setRawValue('');
16029 this.lastSelectionText = '';
16030 this.lastData = false;
16032 var close = this.closeTriggerEl();
16043 * Sets the specified value into the field. If the value finds a match, the corresponding record text
16044 * will be displayed in the field. If the value does not match the data value of an existing item,
16045 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16046 * Otherwise the field will be blank (although the value will still be set).
16047 * @param {String} value The value to match
16049 setValue : function(v)
16051 if(Roo.isIOS && this.useNativeIOS){
16052 this.setIOSValue(v);
16062 if(this.valueField){
16063 var r = this.findRecord(this.valueField, v);
16065 text = r.data[this.displayField];
16066 }else if(this.valueNotFoundText !== undefined){
16067 text = this.valueNotFoundText;
16070 this.lastSelectionText = text;
16071 if(this.hiddenField){
16072 this.hiddenField.dom.value = v;
16074 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16077 var close = this.closeTriggerEl();
16080 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16086 * @property {Object} the last set data for the element
16091 * Sets the value of the field based on a object which is related to the record format for the store.
16092 * @param {Object} value the value to set as. or false on reset?
16094 setFromData : function(o){
16101 var dv = ''; // display value
16102 var vv = ''; // value value..
16104 if (this.displayField) {
16105 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16107 // this is an error condition!!!
16108 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16111 if(this.valueField){
16112 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16115 var close = this.closeTriggerEl();
16118 if(dv.length || vv * 1 > 0){
16120 this.blockFocus=true;
16126 if(this.hiddenField){
16127 this.hiddenField.dom.value = vv;
16129 this.lastSelectionText = dv;
16130 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16134 // no hidden field.. - we store the value in 'value', but still display
16135 // display field!!!!
16136 this.lastSelectionText = dv;
16137 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16144 reset : function(){
16145 // overridden so that last data is reset..
16152 this.setValue(this.originalValue);
16153 //this.clearInvalid();
16154 this.lastData = false;
16156 this.view.clearSelections();
16162 findRecord : function(prop, value){
16164 if(this.store.getCount() > 0){
16165 this.store.each(function(r){
16166 if(r.data[prop] == value){
16176 getName: function()
16178 // returns hidden if it's set..
16179 if (!this.rendered) {return ''};
16180 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
16184 onViewMove : function(e, t){
16185 this.inKeyMode = false;
16189 onViewOver : function(e, t){
16190 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16193 var item = this.view.findItemFromChild(t);
16196 var index = this.view.indexOf(item);
16197 this.select(index, false);
16202 onViewClick : function(view, doFocus, el, e)
16204 var index = this.view.getSelectedIndexes()[0];
16206 var r = this.store.getAt(index);
16210 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16217 Roo.each(this.tickItems, function(v,k){
16219 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16221 _this.tickItems.splice(k, 1);
16223 if(typeof(e) == 'undefined' && view == false){
16224 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16236 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16237 this.tickItems.push(r.data);
16240 if(typeof(e) == 'undefined' && view == false){
16241 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16248 this.onSelect(r, index);
16250 if(doFocus !== false && !this.blockFocus){
16251 this.inputEl().focus();
16256 restrictHeight : function(){
16257 //this.innerList.dom.style.height = '';
16258 //var inner = this.innerList.dom;
16259 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16260 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16261 //this.list.beginUpdate();
16262 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16263 this.list.alignTo(this.inputEl(), this.listAlign);
16264 this.list.alignTo(this.inputEl(), this.listAlign);
16265 //this.list.endUpdate();
16269 onEmptyResults : function(){
16271 if(this.tickable && this.editable){
16272 this.hasFocus = false;
16273 this.restrictHeight();
16281 * Returns true if the dropdown list is expanded, else false.
16283 isExpanded : function(){
16284 return this.list.isVisible();
16288 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16289 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16290 * @param {String} value The data value of the item to select
16291 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16292 * selected item if it is not currently in view (defaults to true)
16293 * @return {Boolean} True if the value matched an item in the list, else false
16295 selectByValue : function(v, scrollIntoView){
16296 if(v !== undefined && v !== null){
16297 var r = this.findRecord(this.valueField || this.displayField, v);
16299 this.select(this.store.indexOf(r), scrollIntoView);
16307 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16308 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16309 * @param {Number} index The zero-based index of the list item to select
16310 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16311 * selected item if it is not currently in view (defaults to true)
16313 select : function(index, scrollIntoView){
16314 this.selectedIndex = index;
16315 this.view.select(index);
16316 if(scrollIntoView !== false){
16317 var el = this.view.getNode(index);
16319 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16322 this.list.scrollChildIntoView(el, false);
16328 selectNext : function(){
16329 var ct = this.store.getCount();
16331 if(this.selectedIndex == -1){
16333 }else if(this.selectedIndex < ct-1){
16334 this.select(this.selectedIndex+1);
16340 selectPrev : function(){
16341 var ct = this.store.getCount();
16343 if(this.selectedIndex == -1){
16345 }else if(this.selectedIndex != 0){
16346 this.select(this.selectedIndex-1);
16352 onKeyUp : function(e){
16353 if(this.editable !== false && !e.isSpecialKey()){
16354 this.lastKey = e.getKey();
16355 this.dqTask.delay(this.queryDelay);
16360 validateBlur : function(){
16361 return !this.list || !this.list.isVisible();
16365 initQuery : function(){
16367 var v = this.getRawValue();
16369 if(this.tickable && this.editable){
16370 v = this.tickableInputEl().getValue();
16377 doForce : function(){
16378 if(this.inputEl().dom.value.length > 0){
16379 this.inputEl().dom.value =
16380 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16386 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
16387 * query allowing the query action to be canceled if needed.
16388 * @param {String} query The SQL query to execute
16389 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16390 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
16391 * saved in the current store (defaults to false)
16393 doQuery : function(q, forceAll){
16395 if(q === undefined || q === null){
16400 forceAll: forceAll,
16404 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16409 forceAll = qe.forceAll;
16410 if(forceAll === true || (q.length >= this.minChars)){
16412 this.hasQuery = true;
16414 if(this.lastQuery != q || this.alwaysQuery){
16415 this.lastQuery = q;
16416 if(this.mode == 'local'){
16417 this.selectedIndex = -1;
16419 this.store.clearFilter();
16422 if(this.specialFilter){
16423 this.fireEvent('specialfilter', this);
16428 this.store.filter(this.displayField, q);
16431 this.store.fireEvent("datachanged", this.store);
16438 this.store.baseParams[this.queryParam] = q;
16440 var options = {params : this.getParams(q)};
16443 options.add = true;
16444 options.params.start = this.page * this.pageSize;
16447 this.store.load(options);
16450 * this code will make the page width larger, at the beginning, the list not align correctly,
16451 * we should expand the list on onLoad
16452 * so command out it
16457 this.selectedIndex = -1;
16462 this.loadNext = false;
16466 getParams : function(q){
16468 //p[this.queryParam] = q;
16472 p.limit = this.pageSize;
16478 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16480 collapse : function(){
16481 if(!this.isExpanded()){
16487 this.hasFocus = false;
16491 this.cancelBtn.hide();
16492 this.trigger.show();
16495 this.tickableInputEl().dom.value = '';
16496 this.tickableInputEl().blur();
16501 Roo.get(document).un('mousedown', this.collapseIf, this);
16502 Roo.get(document).un('mousewheel', this.collapseIf, this);
16503 if (!this.editable) {
16504 Roo.get(document).un('keydown', this.listKeyPress, this);
16506 this.fireEvent('collapse', this);
16512 collapseIf : function(e){
16513 var in_combo = e.within(this.el);
16514 var in_list = e.within(this.list);
16515 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16517 if (in_combo || in_list || is_list) {
16518 //e.stopPropagation();
16523 this.onTickableFooterButtonClick(e, false, false);
16531 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16533 expand : function(){
16535 if(this.isExpanded() || !this.hasFocus){
16539 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16540 this.list.setWidth(lw);
16546 this.restrictHeight();
16550 this.tickItems = Roo.apply([], this.item);
16553 this.cancelBtn.show();
16554 this.trigger.hide();
16557 this.tickableInputEl().focus();
16562 Roo.get(document).on('mousedown', this.collapseIf, this);
16563 Roo.get(document).on('mousewheel', this.collapseIf, this);
16564 if (!this.editable) {
16565 Roo.get(document).on('keydown', this.listKeyPress, this);
16568 this.fireEvent('expand', this);
16572 // Implements the default empty TriggerField.onTriggerClick function
16573 onTriggerClick : function(e)
16575 Roo.log('trigger click');
16577 if(this.disabled || !this.triggerList){
16582 this.loadNext = false;
16584 if(this.isExpanded()){
16586 if (!this.blockFocus) {
16587 this.inputEl().focus();
16591 this.hasFocus = true;
16592 if(this.triggerAction == 'all') {
16593 this.doQuery(this.allQuery, true);
16595 this.doQuery(this.getRawValue());
16597 if (!this.blockFocus) {
16598 this.inputEl().focus();
16603 onTickableTriggerClick : function(e)
16610 this.loadNext = false;
16611 this.hasFocus = true;
16613 if(this.triggerAction == 'all') {
16614 this.doQuery(this.allQuery, true);
16616 this.doQuery(this.getRawValue());
16620 onSearchFieldClick : function(e)
16622 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16623 this.onTickableFooterButtonClick(e, false, false);
16627 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16632 this.loadNext = false;
16633 this.hasFocus = true;
16635 if(this.triggerAction == 'all') {
16636 this.doQuery(this.allQuery, true);
16638 this.doQuery(this.getRawValue());
16642 listKeyPress : function(e)
16644 //Roo.log('listkeypress');
16645 // scroll to first matching element based on key pres..
16646 if (e.isSpecialKey()) {
16649 var k = String.fromCharCode(e.getKey()).toUpperCase();
16652 var csel = this.view.getSelectedNodes();
16653 var cselitem = false;
16655 var ix = this.view.indexOf(csel[0]);
16656 cselitem = this.store.getAt(ix);
16657 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16663 this.store.each(function(v) {
16665 // start at existing selection.
16666 if (cselitem.id == v.id) {
16672 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16673 match = this.store.indexOf(v);
16679 if (match === false) {
16680 return true; // no more action?
16683 this.view.select(match);
16684 var sn = Roo.get(this.view.getSelectedNodes()[0]);
16685 sn.scrollIntoView(sn.dom.parentNode, false);
16688 onViewScroll : function(e, t){
16690 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){
16694 this.hasQuery = true;
16696 this.loading = this.list.select('.loading', true).first();
16698 if(this.loading === null){
16699 this.list.createChild({
16701 cls: 'loading roo-select2-more-results roo-select2-active',
16702 html: 'Loading more results...'
16705 this.loading = this.list.select('.loading', true).first();
16707 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16709 this.loading.hide();
16712 this.loading.show();
16717 this.loadNext = true;
16719 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16724 addItem : function(o)
16726 var dv = ''; // display value
16728 if (this.displayField) {
16729 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16731 // this is an error condition!!!
16732 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16739 var choice = this.choices.createChild({
16741 cls: 'roo-select2-search-choice',
16750 cls: 'roo-select2-search-choice-close fa fa-times',
16755 }, this.searchField);
16757 var close = choice.select('a.roo-select2-search-choice-close', true).first();
16759 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16767 this.inputEl().dom.value = '';
16772 onRemoveItem : function(e, _self, o)
16774 e.preventDefault();
16776 this.lastItem = Roo.apply([], this.item);
16778 var index = this.item.indexOf(o.data) * 1;
16781 Roo.log('not this item?!');
16785 this.item.splice(index, 1);
16790 this.fireEvent('remove', this, e);
16796 syncValue : function()
16798 if(!this.item.length){
16805 Roo.each(this.item, function(i){
16806 if(_this.valueField){
16807 value.push(i[_this.valueField]);
16814 this.value = value.join(',');
16816 if(this.hiddenField){
16817 this.hiddenField.dom.value = this.value;
16820 this.store.fireEvent("datachanged", this.store);
16825 clearItem : function()
16827 if(!this.multiple){
16833 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
16841 if(this.tickable && !Roo.isTouch){
16842 this.view.refresh();
16846 inputEl: function ()
16848 if(Roo.isIOS && this.useNativeIOS){
16849 return this.el.select('select.roo-ios-select', true).first();
16852 if(Roo.isTouch && this.mobileTouchView){
16853 return this.el.select('input.form-control',true).first();
16857 return this.searchField;
16860 return this.el.select('input.form-control',true).first();
16863 onTickableFooterButtonClick : function(e, btn, el)
16865 e.preventDefault();
16867 this.lastItem = Roo.apply([], this.item);
16869 if(btn && btn.name == 'cancel'){
16870 this.tickItems = Roo.apply([], this.item);
16879 Roo.each(this.tickItems, function(o){
16887 validate : function()
16889 if(this.getVisibilityEl().hasClass('hidden')){
16893 var v = this.getRawValue();
16896 v = this.getValue();
16899 if(this.disabled || this.allowBlank || v.length){
16904 this.markInvalid();
16908 tickableInputEl : function()
16910 if(!this.tickable || !this.editable){
16911 return this.inputEl();
16914 return this.inputEl().select('.roo-select2-search-field-input', true).first();
16918 getAutoCreateTouchView : function()
16923 cls: 'form-group' //input-group
16929 type : this.inputType,
16930 cls : 'form-control x-combo-noedit',
16931 autocomplete: 'new-password',
16932 placeholder : this.placeholder || '',
16937 input.name = this.name;
16941 input.cls += ' input-' + this.size;
16944 if (this.disabled) {
16945 input.disabled = true;
16949 cls : 'roo-combobox-wrap',
16956 inputblock.cls += ' input-group';
16958 inputblock.cn.unshift({
16960 cls : 'input-group-addon input-group-prepend input-group-text',
16965 if(this.removable && !this.multiple){
16966 inputblock.cls += ' roo-removable';
16968 inputblock.cn.push({
16971 cls : 'roo-combo-removable-btn close'
16975 if(this.hasFeedback && !this.allowBlank){
16977 inputblock.cls += ' has-feedback';
16979 inputblock.cn.push({
16981 cls: 'glyphicon form-control-feedback'
16988 inputblock.cls += (this.before) ? '' : ' input-group';
16990 inputblock.cn.push({
16992 cls : 'input-group-addon input-group-append input-group-text',
16998 var ibwrap = inputblock;
17003 cls: 'roo-select2-choices',
17007 cls: 'roo-select2-search-field',
17020 cls: 'roo-select2-container input-group roo-touchview-combobox ',
17025 cls: 'form-hidden-field'
17031 if(!this.multiple && this.showToggleBtn){
17037 if (this.caret != false) {
17040 cls: 'fa fa-' + this.caret
17047 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17049 Roo.bootstrap.version == 3 ? caret : '',
17052 cls: 'combobox-clear',
17066 combobox.cls += ' roo-select2-container-multi';
17069 var align = this.labelAlign || this.parentLabelAlign();
17071 if (align ==='left' && this.fieldLabel.length) {
17076 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17077 tooltip : 'This field is required'
17081 cls : 'control-label col-form-label',
17082 html : this.fieldLabel
17086 cls : 'roo-combobox-wrap ',
17093 var labelCfg = cfg.cn[1];
17094 var contentCfg = cfg.cn[2];
17097 if(this.indicatorpos == 'right'){
17102 cls : 'control-label col-form-label',
17106 html : this.fieldLabel
17110 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17111 tooltip : 'This field is required'
17116 cls : "roo-combobox-wrap ",
17124 labelCfg = cfg.cn[0];
17125 contentCfg = cfg.cn[1];
17130 if(this.labelWidth > 12){
17131 labelCfg.style = "width: " + this.labelWidth + 'px';
17134 if(this.labelWidth < 13 && this.labelmd == 0){
17135 this.labelmd = this.labelWidth;
17138 if(this.labellg > 0){
17139 labelCfg.cls += ' col-lg-' + this.labellg;
17140 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17143 if(this.labelmd > 0){
17144 labelCfg.cls += ' col-md-' + this.labelmd;
17145 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17148 if(this.labelsm > 0){
17149 labelCfg.cls += ' col-sm-' + this.labelsm;
17150 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17153 if(this.labelxs > 0){
17154 labelCfg.cls += ' col-xs-' + this.labelxs;
17155 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17159 } else if ( this.fieldLabel.length) {
17163 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17164 tooltip : 'This field is required'
17168 cls : 'control-label',
17169 html : this.fieldLabel
17180 if(this.indicatorpos == 'right'){
17184 cls : 'control-label',
17185 html : this.fieldLabel,
17189 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17190 tooltip : 'This field is required'
17207 var settings = this;
17209 ['xs','sm','md','lg'].map(function(size){
17210 if (settings[size]) {
17211 cfg.cls += ' col-' + size + '-' + settings[size];
17218 initTouchView : function()
17220 this.renderTouchView();
17222 this.touchViewEl.on('scroll', function(){
17223 this.el.dom.scrollTop = 0;
17226 this.originalValue = this.getValue();
17228 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17230 this.inputEl().on("click", this.showTouchView, this);
17231 if (this.triggerEl) {
17232 this.triggerEl.on("click", this.showTouchView, this);
17236 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17237 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17239 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17241 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17242 this.store.on('load', this.onTouchViewLoad, this);
17243 this.store.on('loadexception', this.onTouchViewLoadException, this);
17245 if(this.hiddenName){
17247 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17249 this.hiddenField.dom.value =
17250 this.hiddenValue !== undefined ? this.hiddenValue :
17251 this.value !== undefined ? this.value : '';
17253 this.el.dom.removeAttribute('name');
17254 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17258 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17259 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17262 if(this.removable && !this.multiple){
17263 var close = this.closeTriggerEl();
17265 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17266 close.on('click', this.removeBtnClick, this, close);
17270 * fix the bug in Safari iOS8
17272 this.inputEl().on("focus", function(e){
17273 document.activeElement.blur();
17276 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17283 renderTouchView : function()
17285 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17286 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17288 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17289 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17291 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17292 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17293 this.touchViewBodyEl.setStyle('overflow', 'auto');
17295 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17296 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17298 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17299 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17303 showTouchView : function()
17309 this.touchViewHeaderEl.hide();
17311 if(this.modalTitle.length){
17312 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17313 this.touchViewHeaderEl.show();
17316 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17317 this.touchViewEl.show();
17319 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17321 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17322 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17324 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17326 if(this.modalTitle.length){
17327 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17330 this.touchViewBodyEl.setHeight(bodyHeight);
17334 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17336 this.touchViewEl.addClass(['in','show']);
17339 if(this._touchViewMask){
17340 Roo.get(document.body).addClass("x-body-masked");
17341 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17342 this._touchViewMask.setStyle('z-index', 10000);
17343 this._touchViewMask.addClass('show');
17346 this.doTouchViewQuery();
17350 hideTouchView : function()
17352 this.touchViewEl.removeClass(['in','show']);
17356 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17358 this.touchViewEl.setStyle('display', 'none');
17361 if(this._touchViewMask){
17362 this._touchViewMask.removeClass('show');
17363 Roo.get(document.body).removeClass("x-body-masked");
17367 setTouchViewValue : function()
17374 Roo.each(this.tickItems, function(o){
17379 this.hideTouchView();
17382 doTouchViewQuery : function()
17391 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17395 if(!this.alwaysQuery || this.mode == 'local'){
17396 this.onTouchViewLoad();
17403 onTouchViewBeforeLoad : function(combo,opts)
17409 onTouchViewLoad : function()
17411 if(this.store.getCount() < 1){
17412 this.onTouchViewEmptyResults();
17416 this.clearTouchView();
17418 var rawValue = this.getRawValue();
17420 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17422 this.tickItems = [];
17424 this.store.data.each(function(d, rowIndex){
17425 var row = this.touchViewListGroup.createChild(template);
17427 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17428 row.addClass(d.data.cls);
17431 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17434 html : d.data[this.displayField]
17437 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17438 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17441 row.removeClass('selected');
17442 if(!this.multiple && this.valueField &&
17443 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17446 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17447 row.addClass('selected');
17450 if(this.multiple && this.valueField &&
17451 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17455 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17456 this.tickItems.push(d.data);
17459 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17463 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17465 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17467 if(this.modalTitle.length){
17468 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17471 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17473 if(this.mobile_restrict_height && listHeight < bodyHeight){
17474 this.touchViewBodyEl.setHeight(listHeight);
17479 if(firstChecked && listHeight > bodyHeight){
17480 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17485 onTouchViewLoadException : function()
17487 this.hideTouchView();
17490 onTouchViewEmptyResults : function()
17492 this.clearTouchView();
17494 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17496 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17500 clearTouchView : function()
17502 this.touchViewListGroup.dom.innerHTML = '';
17505 onTouchViewClick : function(e, el, o)
17507 e.preventDefault();
17510 var rowIndex = o.rowIndex;
17512 var r = this.store.getAt(rowIndex);
17514 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17516 if(!this.multiple){
17517 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17518 c.dom.removeAttribute('checked');
17521 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17523 this.setFromData(r.data);
17525 var close = this.closeTriggerEl();
17531 this.hideTouchView();
17533 this.fireEvent('select', this, r, rowIndex);
17538 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17539 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17540 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17544 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17545 this.addItem(r.data);
17546 this.tickItems.push(r.data);
17550 getAutoCreateNativeIOS : function()
17553 cls: 'form-group' //input-group,
17558 cls : 'roo-ios-select'
17562 combobox.name = this.name;
17565 if (this.disabled) {
17566 combobox.disabled = true;
17569 var settings = this;
17571 ['xs','sm','md','lg'].map(function(size){
17572 if (settings[size]) {
17573 cfg.cls += ' col-' + size + '-' + settings[size];
17583 initIOSView : function()
17585 this.store.on('load', this.onIOSViewLoad, this);
17590 onIOSViewLoad : function()
17592 if(this.store.getCount() < 1){
17596 this.clearIOSView();
17598 if(this.allowBlank) {
17600 var default_text = '-- SELECT --';
17602 if(this.placeholder.length){
17603 default_text = this.placeholder;
17606 if(this.emptyTitle.length){
17607 default_text += ' - ' + this.emptyTitle + ' -';
17610 var opt = this.inputEl().createChild({
17613 html : default_text
17617 o[this.valueField] = 0;
17618 o[this.displayField] = default_text;
17620 this.ios_options.push({
17627 this.store.data.each(function(d, rowIndex){
17631 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17632 html = d.data[this.displayField];
17637 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17638 value = d.data[this.valueField];
17647 if(this.value == d.data[this.valueField]){
17648 option['selected'] = true;
17651 var opt = this.inputEl().createChild(option);
17653 this.ios_options.push({
17660 this.inputEl().on('change', function(){
17661 this.fireEvent('select', this);
17666 clearIOSView: function()
17668 this.inputEl().dom.innerHTML = '';
17670 this.ios_options = [];
17673 setIOSValue: function(v)
17677 if(!this.ios_options){
17681 Roo.each(this.ios_options, function(opts){
17683 opts.el.dom.removeAttribute('selected');
17685 if(opts.data[this.valueField] != v){
17689 opts.el.dom.setAttribute('selected', true);
17695 * @cfg {Boolean} grow
17699 * @cfg {Number} growMin
17703 * @cfg {Number} growMax
17712 Roo.apply(Roo.bootstrap.ComboBox, {
17716 cls: 'modal-header',
17738 cls: 'list-group-item',
17742 cls: 'roo-combobox-list-group-item-value'
17746 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17760 listItemCheckbox : {
17762 cls: 'list-group-item',
17766 cls: 'roo-combobox-list-group-item-value'
17770 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17786 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17791 cls: 'modal-footer',
17799 cls: 'col-xs-6 text-left',
17802 cls: 'btn btn-danger roo-touch-view-cancel',
17808 cls: 'col-xs-6 text-right',
17811 cls: 'btn btn-success roo-touch-view-ok',
17822 Roo.apply(Roo.bootstrap.ComboBox, {
17824 touchViewTemplate : {
17826 cls: 'modal fade roo-combobox-touch-view',
17830 cls: 'modal-dialog',
17831 style : 'position:fixed', // we have to fix position....
17835 cls: 'modal-content',
17837 Roo.bootstrap.ComboBox.header,
17838 Roo.bootstrap.ComboBox.body,
17839 Roo.bootstrap.ComboBox.footer
17848 * Ext JS Library 1.1.1
17849 * Copyright(c) 2006-2007, Ext JS, LLC.
17851 * Originally Released Under LGPL - original licence link has changed is not relivant.
17854 * <script type="text/javascript">
17859 * @extends Roo.util.Observable
17860 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
17861 * This class also supports single and multi selection modes. <br>
17862 * Create a data model bound view:
17864 var store = new Roo.data.Store(...);
17866 var view = new Roo.View({
17868 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
17870 singleSelect: true,
17871 selectedClass: "ydataview-selected",
17875 // listen for node click?
17876 view.on("click", function(vw, index, node, e){
17877 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
17881 dataModel.load("foobar.xml");
17883 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
17885 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
17886 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
17888 * Note: old style constructor is still suported (container, template, config)
17891 * Create a new View
17892 * @param {Object} config The config object
17895 Roo.View = function(config, depreciated_tpl, depreciated_config){
17897 this.parent = false;
17899 if (typeof(depreciated_tpl) == 'undefined') {
17900 // new way.. - universal constructor.
17901 Roo.apply(this, config);
17902 this.el = Roo.get(this.el);
17905 this.el = Roo.get(config);
17906 this.tpl = depreciated_tpl;
17907 Roo.apply(this, depreciated_config);
17909 this.wrapEl = this.el.wrap().wrap();
17910 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
17913 if(typeof(this.tpl) == "string"){
17914 this.tpl = new Roo.Template(this.tpl);
17916 // support xtype ctors..
17917 this.tpl = new Roo.factory(this.tpl, Roo);
17921 this.tpl.compile();
17926 * @event beforeclick
17927 * Fires before a click is processed. Returns false to cancel the default action.
17928 * @param {Roo.View} this
17929 * @param {Number} index The index of the target node
17930 * @param {HTMLElement} node The target node
17931 * @param {Roo.EventObject} e The raw event object
17933 "beforeclick" : true,
17936 * Fires when a template node is clicked.
17937 * @param {Roo.View} this
17938 * @param {Number} index The index of the target node
17939 * @param {HTMLElement} node The target node
17940 * @param {Roo.EventObject} e The raw event object
17945 * Fires when a template node is double clicked.
17946 * @param {Roo.View} this
17947 * @param {Number} index The index of the target node
17948 * @param {HTMLElement} node The target node
17949 * @param {Roo.EventObject} e The raw event object
17953 * @event contextmenu
17954 * Fires when a template node is right clicked.
17955 * @param {Roo.View} this
17956 * @param {Number} index The index of the target node
17957 * @param {HTMLElement} node The target node
17958 * @param {Roo.EventObject} e The raw event object
17960 "contextmenu" : true,
17962 * @event selectionchange
17963 * Fires when the selected nodes change.
17964 * @param {Roo.View} this
17965 * @param {Array} selections Array of the selected nodes
17967 "selectionchange" : true,
17970 * @event beforeselect
17971 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
17972 * @param {Roo.View} this
17973 * @param {HTMLElement} node The node to be selected
17974 * @param {Array} selections Array of currently selected nodes
17976 "beforeselect" : true,
17978 * @event preparedata
17979 * Fires on every row to render, to allow you to change the data.
17980 * @param {Roo.View} this
17981 * @param {Object} data to be rendered (change this)
17983 "preparedata" : true
17991 "click": this.onClick,
17992 "dblclick": this.onDblClick,
17993 "contextmenu": this.onContextMenu,
17997 this.selections = [];
17999 this.cmp = new Roo.CompositeElementLite([]);
18001 this.store = Roo.factory(this.store, Roo.data);
18002 this.setStore(this.store, true);
18005 if ( this.footer && this.footer.xtype) {
18007 var fctr = this.wrapEl.appendChild(document.createElement("div"));
18009 this.footer.dataSource = this.store;
18010 this.footer.container = fctr;
18011 this.footer = Roo.factory(this.footer, Roo);
18012 fctr.insertFirst(this.el);
18014 // this is a bit insane - as the paging toolbar seems to detach the el..
18015 // dom.parentNode.parentNode.parentNode
18016 // they get detached?
18020 Roo.View.superclass.constructor.call(this);
18025 Roo.extend(Roo.View, Roo.util.Observable, {
18028 * @cfg {Roo.data.Store} store Data store to load data from.
18033 * @cfg {String|Roo.Element} el The container element.
18038 * @cfg {String|Roo.Template} tpl The template used by this View
18042 * @cfg {String} dataName the named area of the template to use as the data area
18043 * Works with domtemplates roo-name="name"
18047 * @cfg {String} selectedClass The css class to add to selected nodes
18049 selectedClass : "x-view-selected",
18051 * @cfg {String} emptyText The empty text to show when nothing is loaded.
18056 * @cfg {String} text to display on mask (default Loading)
18060 * @cfg {Boolean} multiSelect Allow multiple selection
18062 multiSelect : false,
18064 * @cfg {Boolean} singleSelect Allow single selection
18066 singleSelect: false,
18069 * @cfg {Boolean} toggleSelect - selecting
18071 toggleSelect : false,
18074 * @cfg {Boolean} tickable - selecting
18079 * Returns the element this view is bound to.
18080 * @return {Roo.Element}
18082 getEl : function(){
18083 return this.wrapEl;
18089 * Refreshes the view. - called by datachanged on the store. - do not call directly.
18091 refresh : function(){
18092 //Roo.log('refresh');
18095 // if we are using something like 'domtemplate', then
18096 // the what gets used is:
18097 // t.applySubtemplate(NAME, data, wrapping data..)
18098 // the outer template then get' applied with
18099 // the store 'extra data'
18100 // and the body get's added to the
18101 // roo-name="data" node?
18102 // <span class='roo-tpl-{name}'></span> ?????
18106 this.clearSelections();
18107 this.el.update("");
18109 var records = this.store.getRange();
18110 if(records.length < 1) {
18112 // is this valid?? = should it render a template??
18114 this.el.update(this.emptyText);
18118 if (this.dataName) {
18119 this.el.update(t.apply(this.store.meta)); //????
18120 el = this.el.child('.roo-tpl-' + this.dataName);
18123 for(var i = 0, len = records.length; i < len; i++){
18124 var data = this.prepareData(records[i].data, i, records[i]);
18125 this.fireEvent("preparedata", this, data, i, records[i]);
18127 var d = Roo.apply({}, data);
18130 Roo.apply(d, {'roo-id' : Roo.id()});
18134 Roo.each(this.parent.item, function(item){
18135 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18138 Roo.apply(d, {'roo-data-checked' : 'checked'});
18142 html[html.length] = Roo.util.Format.trim(
18144 t.applySubtemplate(this.dataName, d, this.store.meta) :
18151 el.update(html.join(""));
18152 this.nodes = el.dom.childNodes;
18153 this.updateIndexes(0);
18158 * Function to override to reformat the data that is sent to
18159 * the template for each node.
18160 * DEPRICATED - use the preparedata event handler.
18161 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18162 * a JSON object for an UpdateManager bound view).
18164 prepareData : function(data, index, record)
18166 this.fireEvent("preparedata", this, data, index, record);
18170 onUpdate : function(ds, record){
18171 // Roo.log('on update');
18172 this.clearSelections();
18173 var index = this.store.indexOf(record);
18174 var n = this.nodes[index];
18175 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18176 n.parentNode.removeChild(n);
18177 this.updateIndexes(index, index);
18183 onAdd : function(ds, records, index)
18185 //Roo.log(['on Add', ds, records, index] );
18186 this.clearSelections();
18187 if(this.nodes.length == 0){
18191 var n = this.nodes[index];
18192 for(var i = 0, len = records.length; i < len; i++){
18193 var d = this.prepareData(records[i].data, i, records[i]);
18195 this.tpl.insertBefore(n, d);
18198 this.tpl.append(this.el, d);
18201 this.updateIndexes(index);
18204 onRemove : function(ds, record, index){
18205 // Roo.log('onRemove');
18206 this.clearSelections();
18207 var el = this.dataName ?
18208 this.el.child('.roo-tpl-' + this.dataName) :
18211 el.dom.removeChild(this.nodes[index]);
18212 this.updateIndexes(index);
18216 * Refresh an individual node.
18217 * @param {Number} index
18219 refreshNode : function(index){
18220 this.onUpdate(this.store, this.store.getAt(index));
18223 updateIndexes : function(startIndex, endIndex){
18224 var ns = this.nodes;
18225 startIndex = startIndex || 0;
18226 endIndex = endIndex || ns.length - 1;
18227 for(var i = startIndex; i <= endIndex; i++){
18228 ns[i].nodeIndex = i;
18233 * Changes the data store this view uses and refresh the view.
18234 * @param {Store} store
18236 setStore : function(store, initial){
18237 if(!initial && this.store){
18238 this.store.un("datachanged", this.refresh);
18239 this.store.un("add", this.onAdd);
18240 this.store.un("remove", this.onRemove);
18241 this.store.un("update", this.onUpdate);
18242 this.store.un("clear", this.refresh);
18243 this.store.un("beforeload", this.onBeforeLoad);
18244 this.store.un("load", this.onLoad);
18245 this.store.un("loadexception", this.onLoad);
18249 store.on("datachanged", this.refresh, this);
18250 store.on("add", this.onAdd, this);
18251 store.on("remove", this.onRemove, this);
18252 store.on("update", this.onUpdate, this);
18253 store.on("clear", this.refresh, this);
18254 store.on("beforeload", this.onBeforeLoad, this);
18255 store.on("load", this.onLoad, this);
18256 store.on("loadexception", this.onLoad, this);
18264 * onbeforeLoad - masks the loading area.
18267 onBeforeLoad : function(store,opts)
18269 //Roo.log('onBeforeLoad');
18271 this.el.update("");
18273 this.el.mask(this.mask ? this.mask : "Loading" );
18275 onLoad : function ()
18282 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18283 * @param {HTMLElement} node
18284 * @return {HTMLElement} The template node
18286 findItemFromChild : function(node){
18287 var el = this.dataName ?
18288 this.el.child('.roo-tpl-' + this.dataName,true) :
18291 if(!node || node.parentNode == el){
18294 var p = node.parentNode;
18295 while(p && p != el){
18296 if(p.parentNode == el){
18305 onClick : function(e){
18306 var item = this.findItemFromChild(e.getTarget());
18308 var index = this.indexOf(item);
18309 if(this.onItemClick(item, index, e) !== false){
18310 this.fireEvent("click", this, index, item, e);
18313 this.clearSelections();
18318 onContextMenu : function(e){
18319 var item = this.findItemFromChild(e.getTarget());
18321 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18326 onDblClick : function(e){
18327 var item = this.findItemFromChild(e.getTarget());
18329 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18333 onItemClick : function(item, index, e)
18335 if(this.fireEvent("beforeclick", this, index, item, e) === false){
18338 if (this.toggleSelect) {
18339 var m = this.isSelected(item) ? 'unselect' : 'select';
18342 _t[m](item, true, false);
18345 if(this.multiSelect || this.singleSelect){
18346 if(this.multiSelect && e.shiftKey && this.lastSelection){
18347 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18349 this.select(item, this.multiSelect && e.ctrlKey);
18350 this.lastSelection = item;
18353 if(!this.tickable){
18354 e.preventDefault();
18362 * Get the number of selected nodes.
18365 getSelectionCount : function(){
18366 return this.selections.length;
18370 * Get the currently selected nodes.
18371 * @return {Array} An array of HTMLElements
18373 getSelectedNodes : function(){
18374 return this.selections;
18378 * Get the indexes of the selected nodes.
18381 getSelectedIndexes : function(){
18382 var indexes = [], s = this.selections;
18383 for(var i = 0, len = s.length; i < len; i++){
18384 indexes.push(s[i].nodeIndex);
18390 * Clear all selections
18391 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18393 clearSelections : function(suppressEvent){
18394 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18395 this.cmp.elements = this.selections;
18396 this.cmp.removeClass(this.selectedClass);
18397 this.selections = [];
18398 if(!suppressEvent){
18399 this.fireEvent("selectionchange", this, this.selections);
18405 * Returns true if the passed node is selected
18406 * @param {HTMLElement/Number} node The node or node index
18407 * @return {Boolean}
18409 isSelected : function(node){
18410 var s = this.selections;
18414 node = this.getNode(node);
18415 return s.indexOf(node) !== -1;
18420 * @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
18421 * @param {Boolean} keepExisting (optional) true to keep existing selections
18422 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18424 select : function(nodeInfo, keepExisting, suppressEvent){
18425 if(nodeInfo instanceof Array){
18427 this.clearSelections(true);
18429 for(var i = 0, len = nodeInfo.length; i < len; i++){
18430 this.select(nodeInfo[i], true, true);
18434 var node = this.getNode(nodeInfo);
18435 if(!node || this.isSelected(node)){
18436 return; // already selected.
18439 this.clearSelections(true);
18442 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18443 Roo.fly(node).addClass(this.selectedClass);
18444 this.selections.push(node);
18445 if(!suppressEvent){
18446 this.fireEvent("selectionchange", this, this.selections);
18454 * @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
18455 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18456 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18458 unselect : function(nodeInfo, keepExisting, suppressEvent)
18460 if(nodeInfo instanceof Array){
18461 Roo.each(this.selections, function(s) {
18462 this.unselect(s, nodeInfo);
18466 var node = this.getNode(nodeInfo);
18467 if(!node || !this.isSelected(node)){
18468 //Roo.log("not selected");
18469 return; // not selected.
18473 Roo.each(this.selections, function(s) {
18475 Roo.fly(node).removeClass(this.selectedClass);
18482 this.selections= ns;
18483 this.fireEvent("selectionchange", this, this.selections);
18487 * Gets a template node.
18488 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18489 * @return {HTMLElement} The node or null if it wasn't found
18491 getNode : function(nodeInfo){
18492 if(typeof nodeInfo == "string"){
18493 return document.getElementById(nodeInfo);
18494 }else if(typeof nodeInfo == "number"){
18495 return this.nodes[nodeInfo];
18501 * Gets a range template nodes.
18502 * @param {Number} startIndex
18503 * @param {Number} endIndex
18504 * @return {Array} An array of nodes
18506 getNodes : function(start, end){
18507 var ns = this.nodes;
18508 start = start || 0;
18509 end = typeof end == "undefined" ? ns.length - 1 : end;
18512 for(var i = start; i <= end; i++){
18516 for(var i = start; i >= end; i--){
18524 * Finds the index of the passed node
18525 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18526 * @return {Number} The index of the node or -1
18528 indexOf : function(node){
18529 node = this.getNode(node);
18530 if(typeof node.nodeIndex == "number"){
18531 return node.nodeIndex;
18533 var ns = this.nodes;
18534 for(var i = 0, len = ns.length; i < len; i++){
18545 * based on jquery fullcalendar
18549 Roo.bootstrap = Roo.bootstrap || {};
18551 * @class Roo.bootstrap.Calendar
18552 * @extends Roo.bootstrap.Component
18553 * Bootstrap Calendar class
18554 * @cfg {Boolean} loadMask (true|false) default false
18555 * @cfg {Object} header generate the user specific header of the calendar, default false
18558 * Create a new Container
18559 * @param {Object} config The config object
18564 Roo.bootstrap.Calendar = function(config){
18565 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18569 * Fires when a date is selected
18570 * @param {DatePicker} this
18571 * @param {Date} date The selected date
18575 * @event monthchange
18576 * Fires when the displayed month changes
18577 * @param {DatePicker} this
18578 * @param {Date} date The selected month
18580 'monthchange': true,
18582 * @event evententer
18583 * Fires when mouse over an event
18584 * @param {Calendar} this
18585 * @param {event} Event
18587 'evententer': true,
18589 * @event eventleave
18590 * Fires when the mouse leaves an
18591 * @param {Calendar} this
18594 'eventleave': true,
18596 * @event eventclick
18597 * Fires when the mouse click an
18598 * @param {Calendar} this
18607 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
18610 * @cfg {Number} startDay
18611 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18619 getAutoCreate : function(){
18622 var fc_button = function(name, corner, style, content ) {
18623 return Roo.apply({},{
18625 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
18627 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18630 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18641 style : 'width:100%',
18648 cls : 'fc-header-left',
18650 fc_button('prev', 'left', 'arrow', '‹' ),
18651 fc_button('next', 'right', 'arrow', '›' ),
18652 { tag: 'span', cls: 'fc-header-space' },
18653 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
18661 cls : 'fc-header-center',
18665 cls: 'fc-header-title',
18668 html : 'month / year'
18676 cls : 'fc-header-right',
18678 /* fc_button('month', 'left', '', 'month' ),
18679 fc_button('week', '', '', 'week' ),
18680 fc_button('day', 'right', '', 'day' )
18692 header = this.header;
18695 var cal_heads = function() {
18697 // fixme - handle this.
18699 for (var i =0; i < Date.dayNames.length; i++) {
18700 var d = Date.dayNames[i];
18703 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18704 html : d.substring(0,3)
18708 ret[0].cls += ' fc-first';
18709 ret[6].cls += ' fc-last';
18712 var cal_cell = function(n) {
18715 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18720 cls: 'fc-day-number',
18724 cls: 'fc-day-content',
18728 style: 'position: relative;' // height: 17px;
18740 var cal_rows = function() {
18743 for (var r = 0; r < 6; r++) {
18750 for (var i =0; i < Date.dayNames.length; i++) {
18751 var d = Date.dayNames[i];
18752 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18755 row.cn[0].cls+=' fc-first';
18756 row.cn[0].cn[0].style = 'min-height:90px';
18757 row.cn[6].cls+=' fc-last';
18761 ret[0].cls += ' fc-first';
18762 ret[4].cls += ' fc-prev-last';
18763 ret[5].cls += ' fc-last';
18770 cls: 'fc-border-separate',
18771 style : 'width:100%',
18779 cls : 'fc-first fc-last',
18797 cls : 'fc-content',
18798 style : "position: relative;",
18801 cls : 'fc-view fc-view-month fc-grid',
18802 style : 'position: relative',
18803 unselectable : 'on',
18806 cls : 'fc-event-container',
18807 style : 'position:absolute;z-index:8;top:0;left:0;'
18825 initEvents : function()
18828 throw "can not find store for calendar";
18834 style: "text-align:center",
18838 style: "background-color:white;width:50%;margin:250 auto",
18842 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
18853 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
18855 var size = this.el.select('.fc-content', true).first().getSize();
18856 this.maskEl.setSize(size.width, size.height);
18857 this.maskEl.enableDisplayMode("block");
18858 if(!this.loadMask){
18859 this.maskEl.hide();
18862 this.store = Roo.factory(this.store, Roo.data);
18863 this.store.on('load', this.onLoad, this);
18864 this.store.on('beforeload', this.onBeforeLoad, this);
18868 this.cells = this.el.select('.fc-day',true);
18869 //Roo.log(this.cells);
18870 this.textNodes = this.el.query('.fc-day-number');
18871 this.cells.addClassOnOver('fc-state-hover');
18873 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
18874 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
18875 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
18876 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
18878 this.on('monthchange', this.onMonthChange, this);
18880 this.update(new Date().clearTime());
18883 resize : function() {
18884 var sz = this.el.getSize();
18886 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
18887 this.el.select('.fc-day-content div',true).setHeight(34);
18892 showPrevMonth : function(e){
18893 this.update(this.activeDate.add("mo", -1));
18895 showToday : function(e){
18896 this.update(new Date().clearTime());
18899 showNextMonth : function(e){
18900 this.update(this.activeDate.add("mo", 1));
18904 showPrevYear : function(){
18905 this.update(this.activeDate.add("y", -1));
18909 showNextYear : function(){
18910 this.update(this.activeDate.add("y", 1));
18915 update : function(date)
18917 var vd = this.activeDate;
18918 this.activeDate = date;
18919 // if(vd && this.el){
18920 // var t = date.getTime();
18921 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
18922 // Roo.log('using add remove');
18924 // this.fireEvent('monthchange', this, date);
18926 // this.cells.removeClass("fc-state-highlight");
18927 // this.cells.each(function(c){
18928 // if(c.dateValue == t){
18929 // c.addClass("fc-state-highlight");
18930 // setTimeout(function(){
18931 // try{c.dom.firstChild.focus();}catch(e){}
18941 var days = date.getDaysInMonth();
18943 var firstOfMonth = date.getFirstDateOfMonth();
18944 var startingPos = firstOfMonth.getDay()-this.startDay;
18946 if(startingPos < this.startDay){
18950 var pm = date.add(Date.MONTH, -1);
18951 var prevStart = pm.getDaysInMonth()-startingPos;
18953 this.cells = this.el.select('.fc-day',true);
18954 this.textNodes = this.el.query('.fc-day-number');
18955 this.cells.addClassOnOver('fc-state-hover');
18957 var cells = this.cells.elements;
18958 var textEls = this.textNodes;
18960 Roo.each(cells, function(cell){
18961 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
18964 days += startingPos;
18966 // convert everything to numbers so it's fast
18967 var day = 86400000;
18968 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
18971 //Roo.log(prevStart);
18973 var today = new Date().clearTime().getTime();
18974 var sel = date.clearTime().getTime();
18975 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
18976 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
18977 var ddMatch = this.disabledDatesRE;
18978 var ddText = this.disabledDatesText;
18979 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
18980 var ddaysText = this.disabledDaysText;
18981 var format = this.format;
18983 var setCellClass = function(cal, cell){
18987 //Roo.log('set Cell Class');
18989 var t = d.getTime();
18993 cell.dateValue = t;
18995 cell.className += " fc-today";
18996 cell.className += " fc-state-highlight";
18997 cell.title = cal.todayText;
19000 // disable highlight in other month..
19001 //cell.className += " fc-state-highlight";
19006 cell.className = " fc-state-disabled";
19007 cell.title = cal.minText;
19011 cell.className = " fc-state-disabled";
19012 cell.title = cal.maxText;
19016 if(ddays.indexOf(d.getDay()) != -1){
19017 cell.title = ddaysText;
19018 cell.className = " fc-state-disabled";
19021 if(ddMatch && format){
19022 var fvalue = d.dateFormat(format);
19023 if(ddMatch.test(fvalue)){
19024 cell.title = ddText.replace("%0", fvalue);
19025 cell.className = " fc-state-disabled";
19029 if (!cell.initialClassName) {
19030 cell.initialClassName = cell.dom.className;
19033 cell.dom.className = cell.initialClassName + ' ' + cell.className;
19038 for(; i < startingPos; i++) {
19039 textEls[i].innerHTML = (++prevStart);
19040 d.setDate(d.getDate()+1);
19042 cells[i].className = "fc-past fc-other-month";
19043 setCellClass(this, cells[i]);
19048 for(; i < days; i++){
19049 intDay = i - startingPos + 1;
19050 textEls[i].innerHTML = (intDay);
19051 d.setDate(d.getDate()+1);
19053 cells[i].className = ''; // "x-date-active";
19054 setCellClass(this, cells[i]);
19058 for(; i < 42; i++) {
19059 textEls[i].innerHTML = (++extraDays);
19060 d.setDate(d.getDate()+1);
19062 cells[i].className = "fc-future fc-other-month";
19063 setCellClass(this, cells[i]);
19066 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19068 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19070 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19071 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19073 if(totalRows != 6){
19074 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19075 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19078 this.fireEvent('monthchange', this, date);
19082 if(!this.internalRender){
19083 var main = this.el.dom.firstChild;
19084 var w = main.offsetWidth;
19085 this.el.setWidth(w + this.el.getBorderWidth("lr"));
19086 Roo.fly(main).setWidth(w);
19087 this.internalRender = true;
19088 // opera does not respect the auto grow header center column
19089 // then, after it gets a width opera refuses to recalculate
19090 // without a second pass
19091 if(Roo.isOpera && !this.secondPass){
19092 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19093 this.secondPass = true;
19094 this.update.defer(10, this, [date]);
19101 findCell : function(dt) {
19102 dt = dt.clearTime().getTime();
19104 this.cells.each(function(c){
19105 //Roo.log("check " +c.dateValue + '?=' + dt);
19106 if(c.dateValue == dt){
19116 findCells : function(ev) {
19117 var s = ev.start.clone().clearTime().getTime();
19119 var e= ev.end.clone().clearTime().getTime();
19122 this.cells.each(function(c){
19123 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19125 if(c.dateValue > e){
19128 if(c.dateValue < s){
19137 // findBestRow: function(cells)
19141 // for (var i =0 ; i < cells.length;i++) {
19142 // ret = Math.max(cells[i].rows || 0,ret);
19149 addItem : function(ev)
19151 // look for vertical location slot in
19152 var cells = this.findCells(ev);
19154 // ev.row = this.findBestRow(cells);
19156 // work out the location.
19160 for(var i =0; i < cells.length; i++) {
19162 cells[i].row = cells[0].row;
19165 cells[i].row = cells[i].row + 1;
19175 if (crow.start.getY() == cells[i].getY()) {
19177 crow.end = cells[i];
19194 cells[0].events.push(ev);
19196 this.calevents.push(ev);
19199 clearEvents: function() {
19201 if(!this.calevents){
19205 Roo.each(this.cells.elements, function(c){
19211 Roo.each(this.calevents, function(e) {
19212 Roo.each(e.els, function(el) {
19213 el.un('mouseenter' ,this.onEventEnter, this);
19214 el.un('mouseleave' ,this.onEventLeave, this);
19219 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19225 renderEvents: function()
19229 this.cells.each(function(c) {
19238 if(c.row != c.events.length){
19239 r = 4 - (4 - (c.row - c.events.length));
19242 c.events = ev.slice(0, r);
19243 c.more = ev.slice(r);
19245 if(c.more.length && c.more.length == 1){
19246 c.events.push(c.more.pop());
19249 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19253 this.cells.each(function(c) {
19255 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19258 for (var e = 0; e < c.events.length; e++){
19259 var ev = c.events[e];
19260 var rows = ev.rows;
19262 for(var i = 0; i < rows.length; i++) {
19264 // how many rows should it span..
19267 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19268 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19270 unselectable : "on",
19273 cls: 'fc-event-inner',
19277 // cls: 'fc-event-time',
19278 // html : cells.length > 1 ? '' : ev.time
19282 cls: 'fc-event-title',
19283 html : String.format('{0}', ev.title)
19290 cls: 'ui-resizable-handle ui-resizable-e',
19291 html : '  '
19298 cfg.cls += ' fc-event-start';
19300 if ((i+1) == rows.length) {
19301 cfg.cls += ' fc-event-end';
19304 var ctr = _this.el.select('.fc-event-container',true).first();
19305 var cg = ctr.createChild(cfg);
19307 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19308 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19310 var r = (c.more.length) ? 1 : 0;
19311 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
19312 cg.setWidth(ebox.right - sbox.x -2);
19314 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19315 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19316 cg.on('click', _this.onEventClick, _this, ev);
19327 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19328 style : 'position: absolute',
19329 unselectable : "on",
19332 cls: 'fc-event-inner',
19336 cls: 'fc-event-title',
19344 cls: 'ui-resizable-handle ui-resizable-e',
19345 html : '  '
19351 var ctr = _this.el.select('.fc-event-container',true).first();
19352 var cg = ctr.createChild(cfg);
19354 var sbox = c.select('.fc-day-content',true).first().getBox();
19355 var ebox = c.select('.fc-day-content',true).first().getBox();
19357 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
19358 cg.setWidth(ebox.right - sbox.x -2);
19360 cg.on('click', _this.onMoreEventClick, _this, c.more);
19370 onEventEnter: function (e, el,event,d) {
19371 this.fireEvent('evententer', this, el, event);
19374 onEventLeave: function (e, el,event,d) {
19375 this.fireEvent('eventleave', this, el, event);
19378 onEventClick: function (e, el,event,d) {
19379 this.fireEvent('eventclick', this, el, event);
19382 onMonthChange: function () {
19386 onMoreEventClick: function(e, el, more)
19390 this.calpopover.placement = 'right';
19391 this.calpopover.setTitle('More');
19393 this.calpopover.setContent('');
19395 var ctr = this.calpopover.el.select('.popover-content', true).first();
19397 Roo.each(more, function(m){
19399 cls : 'fc-event-hori fc-event-draggable',
19402 var cg = ctr.createChild(cfg);
19404 cg.on('click', _this.onEventClick, _this, m);
19407 this.calpopover.show(el);
19412 onLoad: function ()
19414 this.calevents = [];
19417 if(this.store.getCount() > 0){
19418 this.store.data.each(function(d){
19421 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19422 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19423 time : d.data.start_time,
19424 title : d.data.title,
19425 description : d.data.description,
19426 venue : d.data.venue
19431 this.renderEvents();
19433 if(this.calevents.length && this.loadMask){
19434 this.maskEl.hide();
19438 onBeforeLoad: function()
19440 this.clearEvents();
19442 this.maskEl.show();
19456 * @class Roo.bootstrap.Popover
19457 * @extends Roo.bootstrap.Component
19458 * Bootstrap Popover class
19459 * @cfg {String} html contents of the popover (or false to use children..)
19460 * @cfg {String} title of popover (or false to hide)
19461 * @cfg {String} placement how it is placed
19462 * @cfg {String} trigger click || hover (or false to trigger manually)
19463 * @cfg {String} over what (parent or false to trigger manually.)
19464 * @cfg {Number} delay - delay before showing
19467 * Create a new Popover
19468 * @param {Object} config The config object
19471 Roo.bootstrap.Popover = function(config){
19472 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19478 * After the popover show
19480 * @param {Roo.bootstrap.Popover} this
19485 * After the popover hide
19487 * @param {Roo.bootstrap.Popover} this
19493 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
19495 title: 'Fill in a title',
19498 placement : 'right',
19499 trigger : 'hover', // hover
19505 can_build_overlaid : false,
19507 getChildContainer : function()
19509 return this.el.select('.popover-content',true).first();
19512 getAutoCreate : function(){
19515 cls : 'popover roo-dynamic',
19516 style: 'display:block',
19522 cls : 'popover-inner',
19526 cls: 'popover-title popover-header',
19530 cls : 'popover-content popover-body',
19541 setTitle: function(str)
19544 this.el.select('.popover-title',true).first().dom.innerHTML = str;
19546 setContent: function(str)
19549 this.el.select('.popover-content',true).first().dom.innerHTML = str;
19551 // as it get's added to the bottom of the page.
19552 onRender : function(ct, position)
19554 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19556 var cfg = Roo.apply({}, this.getAutoCreate());
19560 cfg.cls += ' ' + this.cls;
19563 cfg.style = this.style;
19565 //Roo.log("adding to ");
19566 this.el = Roo.get(document.body).createChild(cfg, position);
19567 // Roo.log(this.el);
19572 initEvents : function()
19574 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
19575 this.el.enableDisplayMode('block');
19577 if (this.over === false) {
19580 if (this.triggers === false) {
19583 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
19584 var triggers = this.trigger ? this.trigger.split(' ') : [];
19585 Roo.each(triggers, function(trigger) {
19587 if (trigger == 'click') {
19588 on_el.on('click', this.toggle, this);
19589 } else if (trigger != 'manual') {
19590 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
19591 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19593 on_el.on(eventIn ,this.enter, this);
19594 on_el.on(eventOut, this.leave, this);
19605 toggle : function () {
19606 this.hoverState == 'in' ? this.leave() : this.enter();
19609 enter : function () {
19611 clearTimeout(this.timeout);
19613 this.hoverState = 'in';
19615 if (!this.delay || !this.delay.show) {
19620 this.timeout = setTimeout(function () {
19621 if (_t.hoverState == 'in') {
19624 }, this.delay.show)
19627 leave : function() {
19628 clearTimeout(this.timeout);
19630 this.hoverState = 'out';
19632 if (!this.delay || !this.delay.hide) {
19637 this.timeout = setTimeout(function () {
19638 if (_t.hoverState == 'out') {
19641 }, this.delay.hide)
19644 show : function (on_el)
19647 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
19651 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
19652 if (this.html !== false) {
19653 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
19655 this.el.removeClass([
19656 'fade','top','bottom', 'left', 'right','in',
19657 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19659 if (!this.title.length) {
19660 this.el.select('.popover-title',true).hide();
19663 var placement = typeof this.placement == 'function' ?
19664 this.placement.call(this, this.el, on_el) :
19667 var autoToken = /\s?auto?\s?/i;
19668 var autoPlace = autoToken.test(placement);
19670 placement = placement.replace(autoToken, '') || 'top';
19674 //this.el.setXY([0,0]);
19676 this.el.dom.style.display='block';
19677 this.el.addClass(placement);
19679 //this.el.appendTo(on_el);
19681 var p = this.getPosition();
19682 var box = this.el.getBox();
19687 var align = Roo.bootstrap.Popover.alignment[placement];
19690 this.el.alignTo(on_el, align[0],align[1]);
19691 //var arrow = this.el.select('.arrow',true).first();
19692 //arrow.set(align[2],
19694 this.el.addClass('in');
19697 if (this.el.hasClass('fade')) {
19701 this.hoverState = 'in';
19703 this.fireEvent('show', this);
19708 this.el.setXY([0,0]);
19709 this.el.removeClass('in');
19711 this.hoverState = null;
19713 this.fireEvent('hide', this);
19718 Roo.bootstrap.Popover.alignment = {
19719 'left' : ['r-l', [-10,0], 'right bs-popover-right'],
19720 'right' : ['l-r', [10,0], 'left bs-popover-left'],
19721 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
19722 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
19733 * @class Roo.bootstrap.Progress
19734 * @extends Roo.bootstrap.Component
19735 * Bootstrap Progress class
19736 * @cfg {Boolean} striped striped of the progress bar
19737 * @cfg {Boolean} active animated of the progress bar
19741 * Create a new Progress
19742 * @param {Object} config The config object
19745 Roo.bootstrap.Progress = function(config){
19746 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
19749 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
19754 getAutoCreate : function(){
19762 cfg.cls += ' progress-striped';
19766 cfg.cls += ' active';
19785 * @class Roo.bootstrap.ProgressBar
19786 * @extends Roo.bootstrap.Component
19787 * Bootstrap ProgressBar class
19788 * @cfg {Number} aria_valuenow aria-value now
19789 * @cfg {Number} aria_valuemin aria-value min
19790 * @cfg {Number} aria_valuemax aria-value max
19791 * @cfg {String} label label for the progress bar
19792 * @cfg {String} panel (success | info | warning | danger )
19793 * @cfg {String} role role of the progress bar
19794 * @cfg {String} sr_only text
19798 * Create a new ProgressBar
19799 * @param {Object} config The config object
19802 Roo.bootstrap.ProgressBar = function(config){
19803 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
19806 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
19810 aria_valuemax : 100,
19816 getAutoCreate : function()
19821 cls: 'progress-bar',
19822 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
19834 cfg.role = this.role;
19837 if(this.aria_valuenow){
19838 cfg['aria-valuenow'] = this.aria_valuenow;
19841 if(this.aria_valuemin){
19842 cfg['aria-valuemin'] = this.aria_valuemin;
19845 if(this.aria_valuemax){
19846 cfg['aria-valuemax'] = this.aria_valuemax;
19849 if(this.label && !this.sr_only){
19850 cfg.html = this.label;
19854 cfg.cls += ' progress-bar-' + this.panel;
19860 update : function(aria_valuenow)
19862 this.aria_valuenow = aria_valuenow;
19864 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
19879 * @class Roo.bootstrap.TabGroup
19880 * @extends Roo.bootstrap.Column
19881 * Bootstrap Column class
19882 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
19883 * @cfg {Boolean} carousel true to make the group behave like a carousel
19884 * @cfg {Boolean} bullets show bullets for the panels
19885 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
19886 * @cfg {Number} timer auto slide timer .. default 0 millisecond
19887 * @cfg {Boolean} showarrow (true|false) show arrow default true
19890 * Create a new TabGroup
19891 * @param {Object} config The config object
19894 Roo.bootstrap.TabGroup = function(config){
19895 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
19897 this.navId = Roo.id();
19900 Roo.bootstrap.TabGroup.register(this);
19904 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
19907 transition : false,
19912 slideOnTouch : false,
19915 getAutoCreate : function()
19917 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
19919 cfg.cls += ' tab-content';
19921 if (this.carousel) {
19922 cfg.cls += ' carousel slide';
19925 cls : 'carousel-inner',
19929 if(this.bullets && !Roo.isTouch){
19932 cls : 'carousel-bullets',
19936 if(this.bullets_cls){
19937 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
19944 cfg.cn[0].cn.push(bullets);
19947 if(this.showarrow){
19948 cfg.cn[0].cn.push({
19950 class : 'carousel-arrow',
19954 class : 'carousel-prev',
19958 class : 'fa fa-chevron-left'
19964 class : 'carousel-next',
19968 class : 'fa fa-chevron-right'
19981 initEvents: function()
19983 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
19984 // this.el.on("touchstart", this.onTouchStart, this);
19987 if(this.autoslide){
19990 this.slideFn = window.setInterval(function() {
19991 _this.showPanelNext();
19995 if(this.showarrow){
19996 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
19997 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20003 // onTouchStart : function(e, el, o)
20005 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20009 // this.showPanelNext();
20013 getChildContainer : function()
20015 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20019 * register a Navigation item
20020 * @param {Roo.bootstrap.NavItem} the navitem to add
20022 register : function(item)
20024 this.tabs.push( item);
20025 item.navId = this.navId; // not really needed..
20030 getActivePanel : function()
20033 Roo.each(this.tabs, function(t) {
20043 getPanelByName : function(n)
20046 Roo.each(this.tabs, function(t) {
20047 if (t.tabId == n) {
20055 indexOfPanel : function(p)
20058 Roo.each(this.tabs, function(t,i) {
20059 if (t.tabId == p.tabId) {
20068 * show a specific panel
20069 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20070 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20072 showPanel : function (pan)
20074 if(this.transition || typeof(pan) == 'undefined'){
20075 Roo.log("waiting for the transitionend");
20079 if (typeof(pan) == 'number') {
20080 pan = this.tabs[pan];
20083 if (typeof(pan) == 'string') {
20084 pan = this.getPanelByName(pan);
20087 var cur = this.getActivePanel();
20090 Roo.log('pan or acitve pan is undefined');
20094 if (pan.tabId == this.getActivePanel().tabId) {
20098 if (false === cur.fireEvent('beforedeactivate')) {
20102 if(this.bullets > 0 && !Roo.isTouch){
20103 this.setActiveBullet(this.indexOfPanel(pan));
20106 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20108 //class="carousel-item carousel-item-next carousel-item-left"
20110 this.transition = true;
20111 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
20112 var lr = dir == 'next' ? 'left' : 'right';
20113 pan.el.addClass(dir); // or prev
20114 pan.el.addClass('carousel-item-' + dir); // or prev
20115 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20116 cur.el.addClass(lr); // or right
20117 pan.el.addClass(lr);
20118 cur.el.addClass('carousel-item-' +lr); // or right
20119 pan.el.addClass('carousel-item-' +lr);
20123 cur.el.on('transitionend', function() {
20124 Roo.log("trans end?");
20126 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20127 pan.setActive(true);
20129 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20130 cur.setActive(false);
20132 _this.transition = false;
20134 }, this, { single: true } );
20139 cur.setActive(false);
20140 pan.setActive(true);
20145 showPanelNext : function()
20147 var i = this.indexOfPanel(this.getActivePanel());
20149 if (i >= this.tabs.length - 1 && !this.autoslide) {
20153 if (i >= this.tabs.length - 1 && this.autoslide) {
20157 this.showPanel(this.tabs[i+1]);
20160 showPanelPrev : function()
20162 var i = this.indexOfPanel(this.getActivePanel());
20164 if (i < 1 && !this.autoslide) {
20168 if (i < 1 && this.autoslide) {
20169 i = this.tabs.length;
20172 this.showPanel(this.tabs[i-1]);
20176 addBullet: function()
20178 if(!this.bullets || Roo.isTouch){
20181 var ctr = this.el.select('.carousel-bullets',true).first();
20182 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20183 var bullet = ctr.createChild({
20184 cls : 'bullet bullet-' + i
20185 },ctr.dom.lastChild);
20190 bullet.on('click', (function(e, el, o, ii, t){
20192 e.preventDefault();
20194 this.showPanel(ii);
20196 if(this.autoslide && this.slideFn){
20197 clearInterval(this.slideFn);
20198 this.slideFn = window.setInterval(function() {
20199 _this.showPanelNext();
20203 }).createDelegate(this, [i, bullet], true));
20208 setActiveBullet : function(i)
20214 Roo.each(this.el.select('.bullet', true).elements, function(el){
20215 el.removeClass('selected');
20218 var bullet = this.el.select('.bullet-' + i, true).first();
20224 bullet.addClass('selected');
20235 Roo.apply(Roo.bootstrap.TabGroup, {
20239 * register a Navigation Group
20240 * @param {Roo.bootstrap.NavGroup} the navgroup to add
20242 register : function(navgrp)
20244 this.groups[navgrp.navId] = navgrp;
20248 * fetch a Navigation Group based on the navigation ID
20249 * if one does not exist , it will get created.
20250 * @param {string} the navgroup to add
20251 * @returns {Roo.bootstrap.NavGroup} the navgroup
20253 get: function(navId) {
20254 if (typeof(this.groups[navId]) == 'undefined') {
20255 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20257 return this.groups[navId] ;
20272 * @class Roo.bootstrap.TabPanel
20273 * @extends Roo.bootstrap.Component
20274 * Bootstrap TabPanel class
20275 * @cfg {Boolean} active panel active
20276 * @cfg {String} html panel content
20277 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20278 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20279 * @cfg {String} href click to link..
20280 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20284 * Create a new TabPanel
20285 * @param {Object} config The config object
20288 Roo.bootstrap.TabPanel = function(config){
20289 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20293 * Fires when the active status changes
20294 * @param {Roo.bootstrap.TabPanel} this
20295 * @param {Boolean} state the new state
20300 * @event beforedeactivate
20301 * Fires before a tab is de-activated - can be used to do validation on a form.
20302 * @param {Roo.bootstrap.TabPanel} this
20303 * @return {Boolean} false if there is an error
20306 'beforedeactivate': true
20309 this.tabId = this.tabId || Roo.id();
20313 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
20320 touchSlide : false,
20321 getAutoCreate : function(){
20326 // item is needed for carousel - not sure if it has any effect otherwise
20327 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20328 html: this.html || ''
20332 cfg.cls += ' active';
20336 cfg.tabId = this.tabId;
20344 initEvents: function()
20346 var p = this.parent();
20348 this.navId = this.navId || p.navId;
20350 if (typeof(this.navId) != 'undefined') {
20351 // not really needed.. but just in case.. parent should be a NavGroup.
20352 var tg = Roo.bootstrap.TabGroup.get(this.navId);
20356 var i = tg.tabs.length - 1;
20358 if(this.active && tg.bullets > 0 && i < tg.bullets){
20359 tg.setActiveBullet(i);
20363 this.el.on('click', this.onClick, this);
20365 if(Roo.isTouch && this.touchSlide){
20366 this.el.on("touchstart", this.onTouchStart, this);
20367 this.el.on("touchmove", this.onTouchMove, this);
20368 this.el.on("touchend", this.onTouchEnd, this);
20373 onRender : function(ct, position)
20375 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20378 setActive : function(state)
20380 Roo.log("panel - set active " + this.tabId + "=" + state);
20382 this.active = state;
20384 this.el.removeClass('active');
20386 } else if (!this.el.hasClass('active')) {
20387 this.el.addClass('active');
20390 this.fireEvent('changed', this, state);
20393 onClick : function(e)
20395 e.preventDefault();
20397 if(!this.href.length){
20401 window.location.href = this.href;
20410 onTouchStart : function(e)
20412 this.swiping = false;
20414 this.startX = e.browserEvent.touches[0].clientX;
20415 this.startY = e.browserEvent.touches[0].clientY;
20418 onTouchMove : function(e)
20420 this.swiping = true;
20422 this.endX = e.browserEvent.touches[0].clientX;
20423 this.endY = e.browserEvent.touches[0].clientY;
20426 onTouchEnd : function(e)
20433 var tabGroup = this.parent();
20435 if(this.endX > this.startX){ // swiping right
20436 tabGroup.showPanelPrev();
20440 if(this.startX > this.endX){ // swiping left
20441 tabGroup.showPanelNext();
20460 * @class Roo.bootstrap.DateField
20461 * @extends Roo.bootstrap.Input
20462 * Bootstrap DateField class
20463 * @cfg {Number} weekStart default 0
20464 * @cfg {String} viewMode default empty, (months|years)
20465 * @cfg {String} minViewMode default empty, (months|years)
20466 * @cfg {Number} startDate default -Infinity
20467 * @cfg {Number} endDate default Infinity
20468 * @cfg {Boolean} todayHighlight default false
20469 * @cfg {Boolean} todayBtn default false
20470 * @cfg {Boolean} calendarWeeks default false
20471 * @cfg {Object} daysOfWeekDisabled default empty
20472 * @cfg {Boolean} singleMode default false (true | false)
20474 * @cfg {Boolean} keyboardNavigation default true
20475 * @cfg {String} language default en
20478 * Create a new DateField
20479 * @param {Object} config The config object
20482 Roo.bootstrap.DateField = function(config){
20483 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20487 * Fires when this field show.
20488 * @param {Roo.bootstrap.DateField} this
20489 * @param {Mixed} date The date value
20494 * Fires when this field hide.
20495 * @param {Roo.bootstrap.DateField} this
20496 * @param {Mixed} date The date value
20501 * Fires when select a date.
20502 * @param {Roo.bootstrap.DateField} this
20503 * @param {Mixed} date The date value
20507 * @event beforeselect
20508 * Fires when before select a date.
20509 * @param {Roo.bootstrap.DateField} this
20510 * @param {Mixed} date The date value
20512 beforeselect : true
20516 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
20519 * @cfg {String} format
20520 * The default date format string which can be overriden for localization support. The format must be
20521 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20525 * @cfg {String} altFormats
20526 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20527 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20529 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20537 todayHighlight : false,
20543 keyboardNavigation: true,
20545 calendarWeeks: false,
20547 startDate: -Infinity,
20551 daysOfWeekDisabled: [],
20555 singleMode : false,
20557 UTCDate: function()
20559 return new Date(Date.UTC.apply(Date, arguments));
20562 UTCToday: function()
20564 var today = new Date();
20565 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20568 getDate: function() {
20569 var d = this.getUTCDate();
20570 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20573 getUTCDate: function() {
20577 setDate: function(d) {
20578 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20581 setUTCDate: function(d) {
20583 this.setValue(this.formatDate(this.date));
20586 onRender: function(ct, position)
20589 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20591 this.language = this.language || 'en';
20592 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20593 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20595 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20596 this.format = this.format || 'm/d/y';
20597 this.isInline = false;
20598 this.isInput = true;
20599 this.component = this.el.select('.add-on', true).first() || false;
20600 this.component = (this.component && this.component.length === 0) ? false : this.component;
20601 this.hasInput = this.component && this.inputEl().length;
20603 if (typeof(this.minViewMode === 'string')) {
20604 switch (this.minViewMode) {
20606 this.minViewMode = 1;
20609 this.minViewMode = 2;
20612 this.minViewMode = 0;
20617 if (typeof(this.viewMode === 'string')) {
20618 switch (this.viewMode) {
20631 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
20633 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
20635 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20637 this.picker().on('mousedown', this.onMousedown, this);
20638 this.picker().on('click', this.onClick, this);
20640 this.picker().addClass('datepicker-dropdown');
20642 this.startViewMode = this.viewMode;
20644 if(this.singleMode){
20645 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
20646 v.setVisibilityMode(Roo.Element.DISPLAY);
20650 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20651 v.setStyle('width', '189px');
20655 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
20656 if(!this.calendarWeeks){
20661 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20662 v.attr('colspan', function(i, val){
20663 return parseInt(val) + 1;
20668 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
20670 this.setStartDate(this.startDate);
20671 this.setEndDate(this.endDate);
20673 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
20680 if(this.isInline) {
20685 picker : function()
20687 return this.pickerEl;
20688 // return this.el.select('.datepicker', true).first();
20691 fillDow: function()
20693 var dowCnt = this.weekStart;
20702 if(this.calendarWeeks){
20710 while (dowCnt < this.weekStart + 7) {
20714 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
20718 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
20721 fillMonths: function()
20724 var months = this.picker().select('>.datepicker-months td', true).first();
20726 months.dom.innerHTML = '';
20732 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
20735 months.createChild(month);
20742 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;
20744 if (this.date < this.startDate) {
20745 this.viewDate = new Date(this.startDate);
20746 } else if (this.date > this.endDate) {
20747 this.viewDate = new Date(this.endDate);
20749 this.viewDate = new Date(this.date);
20757 var d = new Date(this.viewDate),
20758 year = d.getUTCFullYear(),
20759 month = d.getUTCMonth(),
20760 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
20761 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
20762 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
20763 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
20764 currentDate = this.date && this.date.valueOf(),
20765 today = this.UTCToday();
20767 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
20769 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20771 // this.picker.select('>tfoot th.today').
20772 // .text(dates[this.language].today)
20773 // .toggle(this.todayBtn !== false);
20775 this.updateNavArrows();
20778 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
20780 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
20782 prevMonth.setUTCDate(day);
20784 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
20786 var nextMonth = new Date(prevMonth);
20788 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
20790 nextMonth = nextMonth.valueOf();
20792 var fillMonths = false;
20794 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
20796 while(prevMonth.valueOf() <= nextMonth) {
20799 if (prevMonth.getUTCDay() === this.weekStart) {
20801 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
20809 if(this.calendarWeeks){
20810 // ISO 8601: First week contains first thursday.
20811 // ISO also states week starts on Monday, but we can be more abstract here.
20813 // Start of current week: based on weekstart/current date
20814 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
20815 // Thursday of this week
20816 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
20817 // First Thursday of year, year from thursday
20818 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
20819 // Calendar week: ms between thursdays, div ms per day, div 7 days
20820 calWeek = (th - yth) / 864e5 / 7 + 1;
20822 fillMonths.cn.push({
20830 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
20832 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
20835 if (this.todayHighlight &&
20836 prevMonth.getUTCFullYear() == today.getFullYear() &&
20837 prevMonth.getUTCMonth() == today.getMonth() &&
20838 prevMonth.getUTCDate() == today.getDate()) {
20839 clsName += ' today';
20842 if (currentDate && prevMonth.valueOf() === currentDate) {
20843 clsName += ' active';
20846 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
20847 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
20848 clsName += ' disabled';
20851 fillMonths.cn.push({
20853 cls: 'day ' + clsName,
20854 html: prevMonth.getDate()
20857 prevMonth.setDate(prevMonth.getDate()+1);
20860 var currentYear = this.date && this.date.getUTCFullYear();
20861 var currentMonth = this.date && this.date.getUTCMonth();
20863 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
20865 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
20866 v.removeClass('active');
20868 if(currentYear === year && k === currentMonth){
20869 v.addClass('active');
20872 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
20873 v.addClass('disabled');
20879 year = parseInt(year/10, 10) * 10;
20881 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
20883 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
20886 for (var i = -1; i < 11; i++) {
20887 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
20889 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
20897 showMode: function(dir)
20900 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
20903 Roo.each(this.picker().select('>div',true).elements, function(v){
20904 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20907 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
20912 if(this.isInline) {
20916 this.picker().removeClass(['bottom', 'top']);
20918 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20920 * place to the top of element!
20924 this.picker().addClass('top');
20925 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20930 this.picker().addClass('bottom');
20932 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20935 parseDate : function(value)
20937 if(!value || value instanceof Date){
20940 var v = Date.parseDate(value, this.format);
20941 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
20942 v = Date.parseDate(value, 'Y-m-d');
20944 if(!v && this.altFormats){
20945 if(!this.altFormatsArray){
20946 this.altFormatsArray = this.altFormats.split("|");
20948 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
20949 v = Date.parseDate(value, this.altFormatsArray[i]);
20955 formatDate : function(date, fmt)
20957 return (!date || !(date instanceof Date)) ?
20958 date : date.dateFormat(fmt || this.format);
20961 onFocus : function()
20963 Roo.bootstrap.DateField.superclass.onFocus.call(this);
20967 onBlur : function()
20969 Roo.bootstrap.DateField.superclass.onBlur.call(this);
20971 var d = this.inputEl().getValue();
20978 showPopup : function()
20980 this.picker().show();
20984 this.fireEvent('showpopup', this, this.date);
20987 hidePopup : function()
20989 if(this.isInline) {
20992 this.picker().hide();
20993 this.viewMode = this.startViewMode;
20996 this.fireEvent('hidepopup', this, this.date);
21000 onMousedown: function(e)
21002 e.stopPropagation();
21003 e.preventDefault();
21008 Roo.bootstrap.DateField.superclass.keyup.call(this);
21012 setValue: function(v)
21014 if(this.fireEvent('beforeselect', this, v) !== false){
21015 var d = new Date(this.parseDate(v) ).clearTime();
21017 if(isNaN(d.getTime())){
21018 this.date = this.viewDate = '';
21019 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21023 v = this.formatDate(d);
21025 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21027 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21031 this.fireEvent('select', this, this.date);
21035 getValue: function()
21037 return this.formatDate(this.date);
21040 fireKey: function(e)
21042 if (!this.picker().isVisible()){
21043 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21049 var dateChanged = false,
21051 newDate, newViewDate;
21056 e.preventDefault();
21060 if (!this.keyboardNavigation) {
21063 dir = e.keyCode == 37 ? -1 : 1;
21066 newDate = this.moveYear(this.date, dir);
21067 newViewDate = this.moveYear(this.viewDate, dir);
21068 } else if (e.shiftKey){
21069 newDate = this.moveMonth(this.date, dir);
21070 newViewDate = this.moveMonth(this.viewDate, dir);
21072 newDate = new Date(this.date);
21073 newDate.setUTCDate(this.date.getUTCDate() + dir);
21074 newViewDate = new Date(this.viewDate);
21075 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21077 if (this.dateWithinRange(newDate)){
21078 this.date = newDate;
21079 this.viewDate = newViewDate;
21080 this.setValue(this.formatDate(this.date));
21082 e.preventDefault();
21083 dateChanged = true;
21088 if (!this.keyboardNavigation) {
21091 dir = e.keyCode == 38 ? -1 : 1;
21093 newDate = this.moveYear(this.date, dir);
21094 newViewDate = this.moveYear(this.viewDate, dir);
21095 } else if (e.shiftKey){
21096 newDate = this.moveMonth(this.date, dir);
21097 newViewDate = this.moveMonth(this.viewDate, dir);
21099 newDate = new Date(this.date);
21100 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21101 newViewDate = new Date(this.viewDate);
21102 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21104 if (this.dateWithinRange(newDate)){
21105 this.date = newDate;
21106 this.viewDate = newViewDate;
21107 this.setValue(this.formatDate(this.date));
21109 e.preventDefault();
21110 dateChanged = true;
21114 this.setValue(this.formatDate(this.date));
21116 e.preventDefault();
21119 this.setValue(this.formatDate(this.date));
21133 onClick: function(e)
21135 e.stopPropagation();
21136 e.preventDefault();
21138 var target = e.getTarget();
21140 if(target.nodeName.toLowerCase() === 'i'){
21141 target = Roo.get(target).dom.parentNode;
21144 var nodeName = target.nodeName;
21145 var className = target.className;
21146 var html = target.innerHTML;
21147 //Roo.log(nodeName);
21149 switch(nodeName.toLowerCase()) {
21151 switch(className) {
21157 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21158 switch(this.viewMode){
21160 this.viewDate = this.moveMonth(this.viewDate, dir);
21164 this.viewDate = this.moveYear(this.viewDate, dir);
21170 var date = new Date();
21171 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21173 this.setValue(this.formatDate(this.date));
21180 if (className.indexOf('disabled') < 0) {
21181 this.viewDate.setUTCDate(1);
21182 if (className.indexOf('month') > -1) {
21183 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21185 var year = parseInt(html, 10) || 0;
21186 this.viewDate.setUTCFullYear(year);
21190 if(this.singleMode){
21191 this.setValue(this.formatDate(this.viewDate));
21202 //Roo.log(className);
21203 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21204 var day = parseInt(html, 10) || 1;
21205 var year = this.viewDate.getUTCFullYear(),
21206 month = this.viewDate.getUTCMonth();
21208 if (className.indexOf('old') > -1) {
21215 } else if (className.indexOf('new') > -1) {
21223 //Roo.log([year,month,day]);
21224 this.date = this.UTCDate(year, month, day,0,0,0,0);
21225 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21227 //Roo.log(this.formatDate(this.date));
21228 this.setValue(this.formatDate(this.date));
21235 setStartDate: function(startDate)
21237 this.startDate = startDate || -Infinity;
21238 if (this.startDate !== -Infinity) {
21239 this.startDate = this.parseDate(this.startDate);
21242 this.updateNavArrows();
21245 setEndDate: function(endDate)
21247 this.endDate = endDate || Infinity;
21248 if (this.endDate !== Infinity) {
21249 this.endDate = this.parseDate(this.endDate);
21252 this.updateNavArrows();
21255 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21257 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21258 if (typeof(this.daysOfWeekDisabled) !== 'object') {
21259 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21261 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21262 return parseInt(d, 10);
21265 this.updateNavArrows();
21268 updateNavArrows: function()
21270 if(this.singleMode){
21274 var d = new Date(this.viewDate),
21275 year = d.getUTCFullYear(),
21276 month = d.getUTCMonth();
21278 Roo.each(this.picker().select('.prev', true).elements, function(v){
21280 switch (this.viewMode) {
21283 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21289 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21296 Roo.each(this.picker().select('.next', true).elements, function(v){
21298 switch (this.viewMode) {
21301 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21307 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21315 moveMonth: function(date, dir)
21320 var new_date = new Date(date.valueOf()),
21321 day = new_date.getUTCDate(),
21322 month = new_date.getUTCMonth(),
21323 mag = Math.abs(dir),
21325 dir = dir > 0 ? 1 : -1;
21328 // If going back one month, make sure month is not current month
21329 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21331 return new_date.getUTCMonth() == month;
21333 // If going forward one month, make sure month is as expected
21334 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21336 return new_date.getUTCMonth() != new_month;
21338 new_month = month + dir;
21339 new_date.setUTCMonth(new_month);
21340 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21341 if (new_month < 0 || new_month > 11) {
21342 new_month = (new_month + 12) % 12;
21345 // For magnitudes >1, move one month at a time...
21346 for (var i=0; i<mag; i++) {
21347 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21348 new_date = this.moveMonth(new_date, dir);
21350 // ...then reset the day, keeping it in the new month
21351 new_month = new_date.getUTCMonth();
21352 new_date.setUTCDate(day);
21354 return new_month != new_date.getUTCMonth();
21357 // Common date-resetting loop -- if date is beyond end of month, make it
21360 new_date.setUTCDate(--day);
21361 new_date.setUTCMonth(new_month);
21366 moveYear: function(date, dir)
21368 return this.moveMonth(date, dir*12);
21371 dateWithinRange: function(date)
21373 return date >= this.startDate && date <= this.endDate;
21379 this.picker().remove();
21382 validateValue : function(value)
21384 if(this.getVisibilityEl().hasClass('hidden')){
21388 if(value.length < 1) {
21389 if(this.allowBlank){
21395 if(value.length < this.minLength){
21398 if(value.length > this.maxLength){
21402 var vt = Roo.form.VTypes;
21403 if(!vt[this.vtype](value, this)){
21407 if(typeof this.validator == "function"){
21408 var msg = this.validator(value);
21414 if(this.regex && !this.regex.test(value)){
21418 if(typeof(this.parseDate(value)) == 'undefined'){
21422 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21426 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21436 this.date = this.viewDate = '';
21438 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21443 Roo.apply(Roo.bootstrap.DateField, {
21454 html: '<i class="fa fa-arrow-left"/>'
21464 html: '<i class="fa fa-arrow-right"/>'
21506 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21507 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21508 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21509 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21510 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21523 navFnc: 'FullYear',
21528 navFnc: 'FullYear',
21533 Roo.apply(Roo.bootstrap.DateField, {
21537 cls: 'datepicker dropdown-menu roo-dynamic',
21541 cls: 'datepicker-days',
21545 cls: 'table-condensed',
21547 Roo.bootstrap.DateField.head,
21551 Roo.bootstrap.DateField.footer
21558 cls: 'datepicker-months',
21562 cls: 'table-condensed',
21564 Roo.bootstrap.DateField.head,
21565 Roo.bootstrap.DateField.content,
21566 Roo.bootstrap.DateField.footer
21573 cls: 'datepicker-years',
21577 cls: 'table-condensed',
21579 Roo.bootstrap.DateField.head,
21580 Roo.bootstrap.DateField.content,
21581 Roo.bootstrap.DateField.footer
21600 * @class Roo.bootstrap.TimeField
21601 * @extends Roo.bootstrap.Input
21602 * Bootstrap DateField class
21606 * Create a new TimeField
21607 * @param {Object} config The config object
21610 Roo.bootstrap.TimeField = function(config){
21611 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
21615 * Fires when this field show.
21616 * @param {Roo.bootstrap.DateField} thisthis
21617 * @param {Mixed} date The date value
21622 * Fires when this field hide.
21623 * @param {Roo.bootstrap.DateField} this
21624 * @param {Mixed} date The date value
21629 * Fires when select a date.
21630 * @param {Roo.bootstrap.DateField} this
21631 * @param {Mixed} date The date value
21637 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
21640 * @cfg {String} format
21641 * The default time format string which can be overriden for localization support. The format must be
21642 * valid according to {@link Date#parseDate} (defaults to 'H:i').
21646 onRender: function(ct, position)
21649 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
21651 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
21653 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21655 this.pop = this.picker().select('>.datepicker-time',true).first();
21656 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21658 this.picker().on('mousedown', this.onMousedown, this);
21659 this.picker().on('click', this.onClick, this);
21661 this.picker().addClass('datepicker-dropdown');
21666 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
21667 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
21668 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
21669 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
21670 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
21671 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
21675 fireKey: function(e){
21676 if (!this.picker().isVisible()){
21677 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21683 e.preventDefault();
21691 this.onTogglePeriod();
21694 this.onIncrementMinutes();
21697 this.onDecrementMinutes();
21706 onClick: function(e) {
21707 e.stopPropagation();
21708 e.preventDefault();
21711 picker : function()
21713 return this.el.select('.datepicker', true).first();
21716 fillTime: function()
21718 var time = this.pop.select('tbody', true).first();
21720 time.dom.innerHTML = '';
21735 cls: 'hours-up glyphicon glyphicon-chevron-up'
21755 cls: 'minutes-up glyphicon glyphicon-chevron-up'
21776 cls: 'timepicker-hour',
21791 cls: 'timepicker-minute',
21806 cls: 'btn btn-primary period',
21828 cls: 'hours-down glyphicon glyphicon-chevron-down'
21848 cls: 'minutes-down glyphicon glyphicon-chevron-down'
21866 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
21873 var hours = this.time.getHours();
21874 var minutes = this.time.getMinutes();
21887 hours = hours - 12;
21891 hours = '0' + hours;
21895 minutes = '0' + minutes;
21898 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
21899 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
21900 this.pop.select('button', true).first().dom.innerHTML = period;
21906 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
21908 var cls = ['bottom'];
21910 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
21917 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
21922 this.picker().addClass(cls.join('-'));
21926 Roo.each(cls, function(c){
21928 _this.picker().setTop(_this.inputEl().getHeight());
21932 _this.picker().setTop(0 - _this.picker().getHeight());
21937 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
21941 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
21948 onFocus : function()
21950 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
21954 onBlur : function()
21956 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
21962 this.picker().show();
21967 this.fireEvent('show', this, this.date);
21972 this.picker().hide();
21975 this.fireEvent('hide', this, this.date);
21978 setTime : function()
21981 this.setValue(this.time.format(this.format));
21983 this.fireEvent('select', this, this.date);
21988 onMousedown: function(e){
21989 e.stopPropagation();
21990 e.preventDefault();
21993 onIncrementHours: function()
21995 Roo.log('onIncrementHours');
21996 this.time = this.time.add(Date.HOUR, 1);
22001 onDecrementHours: function()
22003 Roo.log('onDecrementHours');
22004 this.time = this.time.add(Date.HOUR, -1);
22008 onIncrementMinutes: function()
22010 Roo.log('onIncrementMinutes');
22011 this.time = this.time.add(Date.MINUTE, 1);
22015 onDecrementMinutes: function()
22017 Roo.log('onDecrementMinutes');
22018 this.time = this.time.add(Date.MINUTE, -1);
22022 onTogglePeriod: function()
22024 Roo.log('onTogglePeriod');
22025 this.time = this.time.add(Date.HOUR, 12);
22032 Roo.apply(Roo.bootstrap.TimeField, {
22062 cls: 'btn btn-info ok',
22074 Roo.apply(Roo.bootstrap.TimeField, {
22078 cls: 'datepicker dropdown-menu',
22082 cls: 'datepicker-time',
22086 cls: 'table-condensed',
22088 Roo.bootstrap.TimeField.content,
22089 Roo.bootstrap.TimeField.footer
22108 * @class Roo.bootstrap.MonthField
22109 * @extends Roo.bootstrap.Input
22110 * Bootstrap MonthField class
22112 * @cfg {String} language default en
22115 * Create a new MonthField
22116 * @param {Object} config The config object
22119 Roo.bootstrap.MonthField = function(config){
22120 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22125 * Fires when this field show.
22126 * @param {Roo.bootstrap.MonthField} this
22127 * @param {Mixed} date The date value
22132 * Fires when this field hide.
22133 * @param {Roo.bootstrap.MonthField} this
22134 * @param {Mixed} date The date value
22139 * Fires when select a date.
22140 * @param {Roo.bootstrap.MonthField} this
22141 * @param {String} oldvalue The old value
22142 * @param {String} newvalue The new value
22148 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
22150 onRender: function(ct, position)
22153 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22155 this.language = this.language || 'en';
22156 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22157 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22159 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22160 this.isInline = false;
22161 this.isInput = true;
22162 this.component = this.el.select('.add-on', true).first() || false;
22163 this.component = (this.component && this.component.length === 0) ? false : this.component;
22164 this.hasInput = this.component && this.inputEL().length;
22166 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22168 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22170 this.picker().on('mousedown', this.onMousedown, this);
22171 this.picker().on('click', this.onClick, this);
22173 this.picker().addClass('datepicker-dropdown');
22175 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22176 v.setStyle('width', '189px');
22183 if(this.isInline) {
22189 setValue: function(v, suppressEvent)
22191 var o = this.getValue();
22193 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22197 if(suppressEvent !== true){
22198 this.fireEvent('select', this, o, v);
22203 getValue: function()
22208 onClick: function(e)
22210 e.stopPropagation();
22211 e.preventDefault();
22213 var target = e.getTarget();
22215 if(target.nodeName.toLowerCase() === 'i'){
22216 target = Roo.get(target).dom.parentNode;
22219 var nodeName = target.nodeName;
22220 var className = target.className;
22221 var html = target.innerHTML;
22223 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22227 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22229 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22235 picker : function()
22237 return this.pickerEl;
22240 fillMonths: function()
22243 var months = this.picker().select('>.datepicker-months td', true).first();
22245 months.dom.innerHTML = '';
22251 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22254 months.createChild(month);
22263 if(typeof(this.vIndex) == 'undefined' && this.value.length){
22264 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22267 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22268 e.removeClass('active');
22270 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22271 e.addClass('active');
22278 if(this.isInline) {
22282 this.picker().removeClass(['bottom', 'top']);
22284 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22286 * place to the top of element!
22290 this.picker().addClass('top');
22291 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22296 this.picker().addClass('bottom');
22298 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22301 onFocus : function()
22303 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22307 onBlur : function()
22309 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22311 var d = this.inputEl().getValue();
22320 this.picker().show();
22321 this.picker().select('>.datepicker-months', true).first().show();
22325 this.fireEvent('show', this, this.date);
22330 if(this.isInline) {
22333 this.picker().hide();
22334 this.fireEvent('hide', this, this.date);
22338 onMousedown: function(e)
22340 e.stopPropagation();
22341 e.preventDefault();
22346 Roo.bootstrap.MonthField.superclass.keyup.call(this);
22350 fireKey: function(e)
22352 if (!this.picker().isVisible()){
22353 if (e.keyCode == 27) {// allow escape to hide and re-show picker
22364 e.preventDefault();
22368 dir = e.keyCode == 37 ? -1 : 1;
22370 this.vIndex = this.vIndex + dir;
22372 if(this.vIndex < 0){
22376 if(this.vIndex > 11){
22380 if(isNaN(this.vIndex)){
22384 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22390 dir = e.keyCode == 38 ? -1 : 1;
22392 this.vIndex = this.vIndex + dir * 4;
22394 if(this.vIndex < 0){
22398 if(this.vIndex > 11){
22402 if(isNaN(this.vIndex)){
22406 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22411 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22412 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22416 e.preventDefault();
22419 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22420 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22436 this.picker().remove();
22441 Roo.apply(Roo.bootstrap.MonthField, {
22460 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22461 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22466 Roo.apply(Roo.bootstrap.MonthField, {
22470 cls: 'datepicker dropdown-menu roo-dynamic',
22474 cls: 'datepicker-months',
22478 cls: 'table-condensed',
22480 Roo.bootstrap.DateField.content
22500 * @class Roo.bootstrap.CheckBox
22501 * @extends Roo.bootstrap.Input
22502 * Bootstrap CheckBox class
22504 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22505 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22506 * @cfg {String} boxLabel The text that appears beside the checkbox
22507 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22508 * @cfg {Boolean} checked initnal the element
22509 * @cfg {Boolean} inline inline the element (default false)
22510 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22511 * @cfg {String} tooltip label tooltip
22514 * Create a new CheckBox
22515 * @param {Object} config The config object
22518 Roo.bootstrap.CheckBox = function(config){
22519 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22524 * Fires when the element is checked or unchecked.
22525 * @param {Roo.bootstrap.CheckBox} this This input
22526 * @param {Boolean} checked The new checked value
22531 * Fires when the element is click.
22532 * @param {Roo.bootstrap.CheckBox} this This input
22539 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
22541 inputType: 'checkbox',
22550 // checkbox success does not make any sense really..
22555 getAutoCreate : function()
22557 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22563 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22566 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
22572 type : this.inputType,
22573 value : this.inputValue,
22574 cls : 'roo-' + this.inputType, //'form-box',
22575 placeholder : this.placeholder || ''
22579 if(this.inputType != 'radio'){
22583 cls : 'roo-hidden-value',
22584 value : this.checked ? this.inputValue : this.valueOff
22589 if (this.weight) { // Validity check?
22590 cfg.cls += " " + this.inputType + "-" + this.weight;
22593 if (this.disabled) {
22594 input.disabled=true;
22598 input.checked = this.checked;
22603 input.name = this.name;
22605 if(this.inputType != 'radio'){
22606 hidden.name = this.name;
22607 input.name = '_hidden_' + this.name;
22612 input.cls += ' input-' + this.size;
22617 ['xs','sm','md','lg'].map(function(size){
22618 if (settings[size]) {
22619 cfg.cls += ' col-' + size + '-' + settings[size];
22623 var inputblock = input;
22625 if (this.before || this.after) {
22628 cls : 'input-group',
22633 inputblock.cn.push({
22635 cls : 'input-group-addon',
22640 inputblock.cn.push(input);
22642 if(this.inputType != 'radio'){
22643 inputblock.cn.push(hidden);
22647 inputblock.cn.push({
22649 cls : 'input-group-addon',
22655 var boxLabelCfg = false;
22661 //'for': id, // box label is handled by onclick - so no for...
22663 html: this.boxLabel
22666 boxLabelCfg.tooltip = this.tooltip;
22672 if (align ==='left' && this.fieldLabel.length) {
22673 // Roo.log("left and has label");
22678 cls : 'control-label',
22679 html : this.fieldLabel
22690 cfg.cn[1].cn.push(boxLabelCfg);
22693 if(this.labelWidth > 12){
22694 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
22697 if(this.labelWidth < 13 && this.labelmd == 0){
22698 this.labelmd = this.labelWidth;
22701 if(this.labellg > 0){
22702 cfg.cn[0].cls += ' col-lg-' + this.labellg;
22703 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
22706 if(this.labelmd > 0){
22707 cfg.cn[0].cls += ' col-md-' + this.labelmd;
22708 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
22711 if(this.labelsm > 0){
22712 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
22713 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
22716 if(this.labelxs > 0){
22717 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
22718 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
22721 } else if ( this.fieldLabel.length) {
22722 // Roo.log(" label");
22726 tag: this.boxLabel ? 'span' : 'label',
22728 cls: 'control-label box-input-label',
22729 //cls : 'input-group-addon',
22730 html : this.fieldLabel
22737 cfg.cn.push(boxLabelCfg);
22742 // Roo.log(" no label && no align");
22743 cfg.cn = [ inputblock ] ;
22745 cfg.cn.push(boxLabelCfg);
22753 if(this.inputType != 'radio'){
22754 cfg.cn.push(hidden);
22762 * return the real input element.
22764 inputEl: function ()
22766 return this.el.select('input.roo-' + this.inputType,true).first();
22768 hiddenEl: function ()
22770 return this.el.select('input.roo-hidden-value',true).first();
22773 labelEl: function()
22775 return this.el.select('label.control-label',true).first();
22777 /* depricated... */
22781 return this.labelEl();
22784 boxLabelEl: function()
22786 return this.el.select('label.box-label',true).first();
22789 initEvents : function()
22791 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
22793 this.inputEl().on('click', this.onClick, this);
22795 if (this.boxLabel) {
22796 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
22799 this.startValue = this.getValue();
22802 Roo.bootstrap.CheckBox.register(this);
22806 onClick : function(e)
22808 if(this.fireEvent('click', this, e) !== false){
22809 this.setChecked(!this.checked);
22814 setChecked : function(state,suppressEvent)
22816 this.startValue = this.getValue();
22818 if(this.inputType == 'radio'){
22820 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22821 e.dom.checked = false;
22824 this.inputEl().dom.checked = true;
22826 this.inputEl().dom.value = this.inputValue;
22828 if(suppressEvent !== true){
22829 this.fireEvent('check', this, true);
22837 this.checked = state;
22839 this.inputEl().dom.checked = state;
22842 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
22844 if(suppressEvent !== true){
22845 this.fireEvent('check', this, state);
22851 getValue : function()
22853 if(this.inputType == 'radio'){
22854 return this.getGroupValue();
22857 return this.hiddenEl().dom.value;
22861 getGroupValue : function()
22863 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
22867 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
22870 setValue : function(v,suppressEvent)
22872 if(this.inputType == 'radio'){
22873 this.setGroupValue(v, suppressEvent);
22877 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
22882 setGroupValue : function(v, suppressEvent)
22884 this.startValue = this.getValue();
22886 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22887 e.dom.checked = false;
22889 if(e.dom.value == v){
22890 e.dom.checked = true;
22894 if(suppressEvent !== true){
22895 this.fireEvent('check', this, true);
22903 validate : function()
22905 if(this.getVisibilityEl().hasClass('hidden')){
22911 (this.inputType == 'radio' && this.validateRadio()) ||
22912 (this.inputType == 'checkbox' && this.validateCheckbox())
22918 this.markInvalid();
22922 validateRadio : function()
22924 if(this.getVisibilityEl().hasClass('hidden')){
22928 if(this.allowBlank){
22934 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22935 if(!e.dom.checked){
22947 validateCheckbox : function()
22950 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
22951 //return (this.getValue() == this.inputValue) ? true : false;
22954 var group = Roo.bootstrap.CheckBox.get(this.groupId);
22962 for(var i in group){
22963 if(group[i].el.isVisible(true)){
22971 for(var i in group){
22976 r = (group[i].getValue() == group[i].inputValue) ? true : false;
22983 * Mark this field as valid
22985 markValid : function()
22989 this.fireEvent('valid', this);
22991 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
22994 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23001 if(this.inputType == 'radio'){
23002 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23003 var fg = e.findParent('.form-group', false, true);
23004 if (Roo.bootstrap.version == 3) {
23005 fg.removeClass([_this.invalidClass, _this.validClass]);
23006 fg.addClass(_this.validClass);
23008 fg.removeClass(['is-valid', 'is-invalid']);
23009 fg.addClass('is-valid');
23017 var fg = this.el.findParent('.form-group', false, true);
23018 if (Roo.bootstrap.version == 3) {
23019 fg.removeClass([this.invalidClass, this.validClass]);
23020 fg.addClass(this.validClass);
23022 fg.removeClass(['is-valid', 'is-invalid']);
23023 fg.addClass('is-valid');
23028 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23034 for(var i in group){
23035 var fg = group[i].el.findParent('.form-group', false, true);
23036 if (Roo.bootstrap.version == 3) {
23037 fg.removeClass([this.invalidClass, this.validClass]);
23038 fg.addClass(this.validClass);
23040 fg.removeClass(['is-valid', 'is-invalid']);
23041 fg.addClass('is-valid');
23047 * Mark this field as invalid
23048 * @param {String} msg The validation message
23050 markInvalid : function(msg)
23052 if(this.allowBlank){
23058 this.fireEvent('invalid', this, msg);
23060 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23063 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23067 label.markInvalid();
23070 if(this.inputType == 'radio'){
23072 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23073 var fg = e.findParent('.form-group', false, true);
23074 if (Roo.bootstrap.version == 3) {
23075 fg.removeClass([_this.invalidClass, _this.validClass]);
23076 fg.addClass(_this.invalidClass);
23078 fg.removeClass(['is-invalid', 'is-valid']);
23079 fg.addClass('is-invalid');
23087 var fg = this.el.findParent('.form-group', false, true);
23088 if (Roo.bootstrap.version == 3) {
23089 fg.removeClass([_this.invalidClass, _this.validClass]);
23090 fg.addClass(_this.invalidClass);
23092 fg.removeClass(['is-invalid', 'is-valid']);
23093 fg.addClass('is-invalid');
23098 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23104 for(var i in group){
23105 var fg = group[i].el.findParent('.form-group', false, true);
23106 if (Roo.bootstrap.version == 3) {
23107 fg.removeClass([_this.invalidClass, _this.validClass]);
23108 fg.addClass(_this.invalidClass);
23110 fg.removeClass(['is-invalid', 'is-valid']);
23111 fg.addClass('is-invalid');
23117 clearInvalid : function()
23119 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23121 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23123 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23125 if (label && label.iconEl) {
23126 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23127 label.iconEl.removeClass(['is-invalid', 'is-valid']);
23131 disable : function()
23133 if(this.inputType != 'radio'){
23134 Roo.bootstrap.CheckBox.superclass.disable.call(this);
23141 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23142 _this.getActionEl().addClass(this.disabledClass);
23143 e.dom.disabled = true;
23147 this.disabled = true;
23148 this.fireEvent("disable", this);
23152 enable : function()
23154 if(this.inputType != 'radio'){
23155 Roo.bootstrap.CheckBox.superclass.enable.call(this);
23162 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23163 _this.getActionEl().removeClass(this.disabledClass);
23164 e.dom.disabled = false;
23168 this.disabled = false;
23169 this.fireEvent("enable", this);
23173 setBoxLabel : function(v)
23178 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23184 Roo.apply(Roo.bootstrap.CheckBox, {
23189 * register a CheckBox Group
23190 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23192 register : function(checkbox)
23194 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23195 this.groups[checkbox.groupId] = {};
23198 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23202 this.groups[checkbox.groupId][checkbox.name] = checkbox;
23206 * fetch a CheckBox Group based on the group ID
23207 * @param {string} the group ID
23208 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23210 get: function(groupId) {
23211 if (typeof(this.groups[groupId]) == 'undefined') {
23215 return this.groups[groupId] ;
23228 * @class Roo.bootstrap.Radio
23229 * @extends Roo.bootstrap.Component
23230 * Bootstrap Radio class
23231 * @cfg {String} boxLabel - the label associated
23232 * @cfg {String} value - the value of radio
23235 * Create a new Radio
23236 * @param {Object} config The config object
23238 Roo.bootstrap.Radio = function(config){
23239 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23243 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23249 getAutoCreate : function()
23253 cls : 'form-group radio',
23258 html : this.boxLabel
23266 initEvents : function()
23268 this.parent().register(this);
23270 this.el.on('click', this.onClick, this);
23274 onClick : function(e)
23276 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23277 this.setChecked(true);
23281 setChecked : function(state, suppressEvent)
23283 this.parent().setValue(this.value, suppressEvent);
23287 setBoxLabel : function(v)
23292 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23307 * @class Roo.bootstrap.SecurePass
23308 * @extends Roo.bootstrap.Input
23309 * Bootstrap SecurePass class
23313 * Create a new SecurePass
23314 * @param {Object} config The config object
23317 Roo.bootstrap.SecurePass = function (config) {
23318 // these go here, so the translation tool can replace them..
23320 PwdEmpty: "Please type a password, and then retype it to confirm.",
23321 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23322 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23323 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23324 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23325 FNInPwd: "Your password can't contain your first name. Please type a different password.",
23326 LNInPwd: "Your password can't contain your last name. Please type a different password.",
23327 TooWeak: "Your password is Too Weak."
23329 this.meterLabel = "Password strength:";
23330 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23331 this.meterClass = [
23332 "roo-password-meter-tooweak",
23333 "roo-password-meter-weak",
23334 "roo-password-meter-medium",
23335 "roo-password-meter-strong",
23336 "roo-password-meter-grey"
23341 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23344 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23346 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23348 * PwdEmpty: "Please type a password, and then retype it to confirm.",
23349 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23350 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23351 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23352 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23353 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
23354 * LNInPwd: "Your password can't contain your last name. Please type a different password."
23364 * @cfg {String/Object} Label for the strength meter (defaults to
23365 * 'Password strength:')
23370 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23371 * ['Weak', 'Medium', 'Strong'])
23374 pwdStrengths: false,
23387 initEvents: function ()
23389 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23391 if (this.el.is('input[type=password]') && Roo.isSafari) {
23392 this.el.on('keydown', this.SafariOnKeyDown, this);
23395 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23398 onRender: function (ct, position)
23400 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23401 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23402 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23404 this.trigger.createChild({
23409 cls: 'roo-password-meter-grey col-xs-12',
23412 //width: this.meterWidth + 'px'
23416 cls: 'roo-password-meter-text'
23422 if (this.hideTrigger) {
23423 this.trigger.setDisplayed(false);
23425 this.setSize(this.width || '', this.height || '');
23428 onDestroy: function ()
23430 if (this.trigger) {
23431 this.trigger.removeAllListeners();
23432 this.trigger.remove();
23435 this.wrap.remove();
23437 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23440 checkStrength: function ()
23442 var pwd = this.inputEl().getValue();
23443 if (pwd == this._lastPwd) {
23448 if (this.ClientSideStrongPassword(pwd)) {
23450 } else if (this.ClientSideMediumPassword(pwd)) {
23452 } else if (this.ClientSideWeakPassword(pwd)) {
23458 Roo.log('strength1: ' + strength);
23460 //var pm = this.trigger.child('div/div/div').dom;
23461 var pm = this.trigger.child('div/div');
23462 pm.removeClass(this.meterClass);
23463 pm.addClass(this.meterClass[strength]);
23466 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23468 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
23470 this._lastPwd = pwd;
23474 Roo.bootstrap.SecurePass.superclass.reset.call(this);
23476 this._lastPwd = '';
23478 var pm = this.trigger.child('div/div');
23479 pm.removeClass(this.meterClass);
23480 pm.addClass('roo-password-meter-grey');
23483 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23486 this.inputEl().dom.type='password';
23489 validateValue: function (value)
23491 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23494 if (value.length == 0) {
23495 if (this.allowBlank) {
23496 this.clearInvalid();
23500 this.markInvalid(this.errors.PwdEmpty);
23501 this.errorMsg = this.errors.PwdEmpty;
23509 if (!value.match(/[\x21-\x7e]+/)) {
23510 this.markInvalid(this.errors.PwdBadChar);
23511 this.errorMsg = this.errors.PwdBadChar;
23514 if (value.length < 6) {
23515 this.markInvalid(this.errors.PwdShort);
23516 this.errorMsg = this.errors.PwdShort;
23519 if (value.length > 16) {
23520 this.markInvalid(this.errors.PwdLong);
23521 this.errorMsg = this.errors.PwdLong;
23525 if (this.ClientSideStrongPassword(value)) {
23527 } else if (this.ClientSideMediumPassword(value)) {
23529 } else if (this.ClientSideWeakPassword(value)) {
23536 if (strength < 2) {
23537 //this.markInvalid(this.errors.TooWeak);
23538 this.errorMsg = this.errors.TooWeak;
23543 console.log('strength2: ' + strength);
23545 //var pm = this.trigger.child('div/div/div').dom;
23547 var pm = this.trigger.child('div/div');
23548 pm.removeClass(this.meterClass);
23549 pm.addClass(this.meterClass[strength]);
23551 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23553 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
23555 this.errorMsg = '';
23559 CharacterSetChecks: function (type)
23562 this.fResult = false;
23565 isctype: function (character, type)
23568 case this.kCapitalLetter:
23569 if (character >= 'A' && character <= 'Z') {
23574 case this.kSmallLetter:
23575 if (character >= 'a' && character <= 'z') {
23581 if (character >= '0' && character <= '9') {
23586 case this.kPunctuation:
23587 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
23598 IsLongEnough: function (pwd, size)
23600 return !(pwd == null || isNaN(size) || pwd.length < size);
23603 SpansEnoughCharacterSets: function (word, nb)
23605 if (!this.IsLongEnough(word, nb))
23610 var characterSetChecks = new Array(
23611 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
23612 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
23615 for (var index = 0; index < word.length; ++index) {
23616 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23617 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
23618 characterSetChecks[nCharSet].fResult = true;
23625 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23626 if (characterSetChecks[nCharSet].fResult) {
23631 if (nCharSets < nb) {
23637 ClientSideStrongPassword: function (pwd)
23639 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
23642 ClientSideMediumPassword: function (pwd)
23644 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
23647 ClientSideWeakPassword: function (pwd)
23649 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
23652 })//<script type="text/javascript">
23655 * Based Ext JS Library 1.1.1
23656 * Copyright(c) 2006-2007, Ext JS, LLC.
23662 * @class Roo.HtmlEditorCore
23663 * @extends Roo.Component
23664 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
23666 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23669 Roo.HtmlEditorCore = function(config){
23672 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
23677 * @event initialize
23678 * Fires when the editor is fully initialized (including the iframe)
23679 * @param {Roo.HtmlEditorCore} this
23684 * Fires when the editor is first receives the focus. Any insertion must wait
23685 * until after this event.
23686 * @param {Roo.HtmlEditorCore} this
23690 * @event beforesync
23691 * Fires before the textarea is updated with content from the editor iframe. Return false
23692 * to cancel the sync.
23693 * @param {Roo.HtmlEditorCore} this
23694 * @param {String} html
23698 * @event beforepush
23699 * Fires before the iframe editor is updated with content from the textarea. Return false
23700 * to cancel the push.
23701 * @param {Roo.HtmlEditorCore} this
23702 * @param {String} html
23707 * Fires when the textarea is updated with content from the editor iframe.
23708 * @param {Roo.HtmlEditorCore} this
23709 * @param {String} html
23714 * Fires when the iframe editor is updated with content from the textarea.
23715 * @param {Roo.HtmlEditorCore} this
23716 * @param {String} html
23721 * @event editorevent
23722 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23723 * @param {Roo.HtmlEditorCore} this
23729 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
23731 // defaults : white / black...
23732 this.applyBlacklists();
23739 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
23743 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
23749 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23754 * @cfg {Number} height (in pixels)
23758 * @cfg {Number} width (in pixels)
23763 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23766 stylesheets: false,
23771 // private properties
23772 validationEvent : false,
23774 initialized : false,
23776 sourceEditMode : false,
23777 onFocus : Roo.emptyFn,
23779 hideMode:'offsets',
23783 // blacklist + whitelisted elements..
23790 * Protected method that will not generally be called directly. It
23791 * is called when the editor initializes the iframe with HTML contents. Override this method if you
23792 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
23794 getDocMarkup : function(){
23798 // inherit styels from page...??
23799 if (this.stylesheets === false) {
23801 Roo.get(document.head).select('style').each(function(node) {
23802 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23805 Roo.get(document.head).select('link').each(function(node) {
23806 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23809 } else if (!this.stylesheets.length) {
23811 st = '<style type="text/css">' +
23812 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23815 for (var i in this.stylesheets) {
23816 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
23821 st += '<style type="text/css">' +
23822 'IMG { cursor: pointer } ' +
23825 var cls = 'roo-htmleditor-body';
23827 if(this.bodyCls.length){
23828 cls += ' ' + this.bodyCls;
23831 return '<html><head>' + st +
23832 //<style type="text/css">' +
23833 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23835 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
23839 onRender : function(ct, position)
23842 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
23843 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
23846 this.el.dom.style.border = '0 none';
23847 this.el.dom.setAttribute('tabIndex', -1);
23848 this.el.addClass('x-hidden hide');
23852 if(Roo.isIE){ // fix IE 1px bogus margin
23853 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
23857 this.frameId = Roo.id();
23861 var iframe = this.owner.wrap.createChild({
23863 cls: 'form-control', // bootstrap..
23865 name: this.frameId,
23866 frameBorder : 'no',
23867 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
23872 this.iframe = iframe.dom;
23874 this.assignDocWin();
23876 this.doc.designMode = 'on';
23879 this.doc.write(this.getDocMarkup());
23883 var task = { // must defer to wait for browser to be ready
23885 //console.log("run task?" + this.doc.readyState);
23886 this.assignDocWin();
23887 if(this.doc.body || this.doc.readyState == 'complete'){
23889 this.doc.designMode="on";
23893 Roo.TaskMgr.stop(task);
23894 this.initEditor.defer(10, this);
23901 Roo.TaskMgr.start(task);
23906 onResize : function(w, h)
23908 Roo.log('resize: ' +w + ',' + h );
23909 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
23913 if(typeof w == 'number'){
23915 this.iframe.style.width = w + 'px';
23917 if(typeof h == 'number'){
23919 this.iframe.style.height = h + 'px';
23921 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
23928 * Toggles the editor between standard and source edit mode.
23929 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23931 toggleSourceEdit : function(sourceEditMode){
23933 this.sourceEditMode = sourceEditMode === true;
23935 if(this.sourceEditMode){
23937 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
23940 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
23941 //this.iframe.className = '';
23944 //this.setSize(this.owner.wrap.getSize());
23945 //this.fireEvent('editmodechange', this, this.sourceEditMode);
23952 * Protected method that will not generally be called directly. If you need/want
23953 * custom HTML cleanup, this is the method you should override.
23954 * @param {String} html The HTML to be cleaned
23955 * return {String} The cleaned HTML
23957 cleanHtml : function(html){
23958 html = String(html);
23959 if(html.length > 5){
23960 if(Roo.isSafari){ // strip safari nonsense
23961 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
23964 if(html == ' '){
23971 * HTML Editor -> Textarea
23972 * Protected method that will not generally be called directly. Syncs the contents
23973 * of the editor iframe with the textarea.
23975 syncValue : function(){
23976 if(this.initialized){
23977 var bd = (this.doc.body || this.doc.documentElement);
23978 //this.cleanUpPaste(); -- this is done else where and causes havoc..
23979 var html = bd.innerHTML;
23981 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
23982 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
23984 html = '<div style="'+m[0]+'">' + html + '</div>';
23987 html = this.cleanHtml(html);
23988 // fix up the special chars.. normaly like back quotes in word...
23989 // however we do not want to do this with chinese..
23990 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
23992 var cc = match.charCodeAt();
23994 // Get the character value, handling surrogate pairs
23995 if (match.length == 2) {
23996 // It's a surrogate pair, calculate the Unicode code point
23997 var high = match.charCodeAt(0) - 0xD800;
23998 var low = match.charCodeAt(1) - 0xDC00;
23999 cc = (high * 0x400) + low + 0x10000;
24001 (cc >= 0x4E00 && cc < 0xA000 ) ||
24002 (cc >= 0x3400 && cc < 0x4E00 ) ||
24003 (cc >= 0xf900 && cc < 0xfb00 )
24008 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24009 return "&#" + cc + ";";
24016 if(this.owner.fireEvent('beforesync', this, html) !== false){
24017 this.el.dom.value = html;
24018 this.owner.fireEvent('sync', this, html);
24024 * Protected method that will not generally be called directly. Pushes the value of the textarea
24025 * into the iframe editor.
24027 pushValue : function(){
24028 if(this.initialized){
24029 var v = this.el.dom.value.trim();
24031 // if(v.length < 1){
24035 if(this.owner.fireEvent('beforepush', this, v) !== false){
24036 var d = (this.doc.body || this.doc.documentElement);
24038 this.cleanUpPaste();
24039 this.el.dom.value = d.innerHTML;
24040 this.owner.fireEvent('push', this, v);
24046 deferFocus : function(){
24047 this.focus.defer(10, this);
24051 focus : function(){
24052 if(this.win && !this.sourceEditMode){
24059 assignDocWin: function()
24061 var iframe = this.iframe;
24064 this.doc = iframe.contentWindow.document;
24065 this.win = iframe.contentWindow;
24067 // if (!Roo.get(this.frameId)) {
24070 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24071 // this.win = Roo.get(this.frameId).dom.contentWindow;
24073 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24077 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24078 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24083 initEditor : function(){
24084 //console.log("INIT EDITOR");
24085 this.assignDocWin();
24089 this.doc.designMode="on";
24091 this.doc.write(this.getDocMarkup());
24094 var dbody = (this.doc.body || this.doc.documentElement);
24095 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24096 // this copies styles from the containing element into thsi one..
24097 // not sure why we need all of this..
24098 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24100 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24101 //ss['background-attachment'] = 'fixed'; // w3c
24102 dbody.bgProperties = 'fixed'; // ie
24103 //Roo.DomHelper.applyStyles(dbody, ss);
24104 Roo.EventManager.on(this.doc, {
24105 //'mousedown': this.onEditorEvent,
24106 'mouseup': this.onEditorEvent,
24107 'dblclick': this.onEditorEvent,
24108 'click': this.onEditorEvent,
24109 'keyup': this.onEditorEvent,
24114 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24116 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24117 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24119 this.initialized = true;
24121 this.owner.fireEvent('initialize', this);
24126 onDestroy : function(){
24132 //for (var i =0; i < this.toolbars.length;i++) {
24133 // // fixme - ask toolbars for heights?
24134 // this.toolbars[i].onDestroy();
24137 //this.wrap.dom.innerHTML = '';
24138 //this.wrap.remove();
24143 onFirstFocus : function(){
24145 this.assignDocWin();
24148 this.activated = true;
24151 if(Roo.isGecko){ // prevent silly gecko errors
24153 var s = this.win.getSelection();
24154 if(!s.focusNode || s.focusNode.nodeType != 3){
24155 var r = s.getRangeAt(0);
24156 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24161 this.execCmd('useCSS', true);
24162 this.execCmd('styleWithCSS', false);
24165 this.owner.fireEvent('activate', this);
24169 adjustFont: function(btn){
24170 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24171 //if(Roo.isSafari){ // safari
24174 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24175 if(Roo.isSafari){ // safari
24176 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24177 v = (v < 10) ? 10 : v;
24178 v = (v > 48) ? 48 : v;
24179 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24184 v = Math.max(1, v+adjust);
24186 this.execCmd('FontSize', v );
24189 onEditorEvent : function(e)
24191 this.owner.fireEvent('editorevent', this, e);
24192 // this.updateToolbar();
24193 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24196 insertTag : function(tg)
24198 // could be a bit smarter... -> wrap the current selected tRoo..
24199 if (tg.toLowerCase() == 'span' ||
24200 tg.toLowerCase() == 'code' ||
24201 tg.toLowerCase() == 'sup' ||
24202 tg.toLowerCase() == 'sub'
24205 range = this.createRange(this.getSelection());
24206 var wrappingNode = this.doc.createElement(tg.toLowerCase());
24207 wrappingNode.appendChild(range.extractContents());
24208 range.insertNode(wrappingNode);
24215 this.execCmd("formatblock", tg);
24219 insertText : function(txt)
24223 var range = this.createRange();
24224 range.deleteContents();
24225 //alert(Sender.getAttribute('label'));
24227 range.insertNode(this.doc.createTextNode(txt));
24233 * Executes a Midas editor command on the editor document and performs necessary focus and
24234 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24235 * @param {String} cmd The Midas command
24236 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24238 relayCmd : function(cmd, value){
24240 this.execCmd(cmd, value);
24241 this.owner.fireEvent('editorevent', this);
24242 //this.updateToolbar();
24243 this.owner.deferFocus();
24247 * Executes a Midas editor command directly on the editor document.
24248 * For visual commands, you should use {@link #relayCmd} instead.
24249 * <b>This should only be called after the editor is initialized.</b>
24250 * @param {String} cmd The Midas command
24251 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24253 execCmd : function(cmd, value){
24254 this.doc.execCommand(cmd, false, value === undefined ? null : value);
24261 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24263 * @param {String} text | dom node..
24265 insertAtCursor : function(text)
24268 if(!this.activated){
24274 var r = this.doc.selection.createRange();
24285 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24289 // from jquery ui (MIT licenced)
24291 var win = this.win;
24293 if (win.getSelection && win.getSelection().getRangeAt) {
24294 range = win.getSelection().getRangeAt(0);
24295 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24296 range.insertNode(node);
24297 } else if (win.document.selection && win.document.selection.createRange) {
24298 // no firefox support
24299 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24300 win.document.selection.createRange().pasteHTML(txt);
24302 // no firefox support
24303 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24304 this.execCmd('InsertHTML', txt);
24313 mozKeyPress : function(e){
24315 var c = e.getCharCode(), cmd;
24318 c = String.fromCharCode(c).toLowerCase();
24332 this.cleanUpPaste.defer(100, this);
24340 e.preventDefault();
24348 fixKeys : function(){ // load time branching for fastest keydown performance
24350 return function(e){
24351 var k = e.getKey(), r;
24354 r = this.doc.selection.createRange();
24357 r.pasteHTML('    ');
24364 r = this.doc.selection.createRange();
24366 var target = r.parentElement();
24367 if(!target || target.tagName.toLowerCase() != 'li'){
24369 r.pasteHTML('<br />');
24375 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24376 this.cleanUpPaste.defer(100, this);
24382 }else if(Roo.isOpera){
24383 return function(e){
24384 var k = e.getKey();
24388 this.execCmd('InsertHTML','    ');
24391 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24392 this.cleanUpPaste.defer(100, this);
24397 }else if(Roo.isSafari){
24398 return function(e){
24399 var k = e.getKey();
24403 this.execCmd('InsertText','\t');
24407 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24408 this.cleanUpPaste.defer(100, this);
24416 getAllAncestors: function()
24418 var p = this.getSelectedNode();
24421 a.push(p); // push blank onto stack..
24422 p = this.getParentElement();
24426 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24430 a.push(this.doc.body);
24434 lastSelNode : false,
24437 getSelection : function()
24439 this.assignDocWin();
24440 return Roo.isIE ? this.doc.selection : this.win.getSelection();
24443 getSelectedNode: function()
24445 // this may only work on Gecko!!!
24447 // should we cache this!!!!
24452 var range = this.createRange(this.getSelection()).cloneRange();
24455 var parent = range.parentElement();
24457 var testRange = range.duplicate();
24458 testRange.moveToElementText(parent);
24459 if (testRange.inRange(range)) {
24462 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24465 parent = parent.parentElement;
24470 // is ancestor a text element.
24471 var ac = range.commonAncestorContainer;
24472 if (ac.nodeType == 3) {
24473 ac = ac.parentNode;
24476 var ar = ac.childNodes;
24479 var other_nodes = [];
24480 var has_other_nodes = false;
24481 for (var i=0;i<ar.length;i++) {
24482 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
24485 // fullly contained node.
24487 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24492 // probably selected..
24493 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24494 other_nodes.push(ar[i]);
24498 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
24503 has_other_nodes = true;
24505 if (!nodes.length && other_nodes.length) {
24506 nodes= other_nodes;
24508 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24514 createRange: function(sel)
24516 // this has strange effects when using with
24517 // top toolbar - not sure if it's a great idea.
24518 //this.editor.contentWindow.focus();
24519 if (typeof sel != "undefined") {
24521 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24523 return this.doc.createRange();
24526 return this.doc.createRange();
24529 getParentElement: function()
24532 this.assignDocWin();
24533 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24535 var range = this.createRange(sel);
24538 var p = range.commonAncestorContainer;
24539 while (p.nodeType == 3) { // text node
24550 * Range intersection.. the hard stuff...
24554 * [ -- selected range --- ]
24558 * if end is before start or hits it. fail.
24559 * if start is after end or hits it fail.
24561 * if either hits (but other is outside. - then it's not
24567 // @see http://www.thismuchiknow.co.uk/?p=64.
24568 rangeIntersectsNode : function(range, node)
24570 var nodeRange = node.ownerDocument.createRange();
24572 nodeRange.selectNode(node);
24574 nodeRange.selectNodeContents(node);
24577 var rangeStartRange = range.cloneRange();
24578 rangeStartRange.collapse(true);
24580 var rangeEndRange = range.cloneRange();
24581 rangeEndRange.collapse(false);
24583 var nodeStartRange = nodeRange.cloneRange();
24584 nodeStartRange.collapse(true);
24586 var nodeEndRange = nodeRange.cloneRange();
24587 nodeEndRange.collapse(false);
24589 return rangeStartRange.compareBoundaryPoints(
24590 Range.START_TO_START, nodeEndRange) == -1 &&
24591 rangeEndRange.compareBoundaryPoints(
24592 Range.START_TO_START, nodeStartRange) == 1;
24596 rangeCompareNode : function(range, node)
24598 var nodeRange = node.ownerDocument.createRange();
24600 nodeRange.selectNode(node);
24602 nodeRange.selectNodeContents(node);
24606 range.collapse(true);
24608 nodeRange.collapse(true);
24610 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
24611 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
24613 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
24615 var nodeIsBefore = ss == 1;
24616 var nodeIsAfter = ee == -1;
24618 if (nodeIsBefore && nodeIsAfter) {
24621 if (!nodeIsBefore && nodeIsAfter) {
24622 return 1; //right trailed.
24625 if (nodeIsBefore && !nodeIsAfter) {
24626 return 2; // left trailed.
24632 // private? - in a new class?
24633 cleanUpPaste : function()
24635 // cleans up the whole document..
24636 Roo.log('cleanuppaste');
24638 this.cleanUpChildren(this.doc.body);
24639 var clean = this.cleanWordChars(this.doc.body.innerHTML);
24640 if (clean != this.doc.body.innerHTML) {
24641 this.doc.body.innerHTML = clean;
24646 cleanWordChars : function(input) {// change the chars to hex code
24647 var he = Roo.HtmlEditorCore;
24649 var output = input;
24650 Roo.each(he.swapCodes, function(sw) {
24651 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
24653 output = output.replace(swapper, sw[1]);
24660 cleanUpChildren : function (n)
24662 if (!n.childNodes.length) {
24665 for (var i = n.childNodes.length-1; i > -1 ; i--) {
24666 this.cleanUpChild(n.childNodes[i]);
24673 cleanUpChild : function (node)
24676 //console.log(node);
24677 if (node.nodeName == "#text") {
24678 // clean up silly Windows -- stuff?
24681 if (node.nodeName == "#comment") {
24682 node.parentNode.removeChild(node);
24683 // clean up silly Windows -- stuff?
24686 var lcname = node.tagName.toLowerCase();
24687 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
24688 // whitelist of tags..
24690 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
24692 node.parentNode.removeChild(node);
24697 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
24699 // spans with no attributes - just remove them..
24700 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
24701 remove_keep_children = true;
24704 // remove <a name=....> as rendering on yahoo mailer is borked with this.
24705 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
24707 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
24708 // remove_keep_children = true;
24711 if (remove_keep_children) {
24712 this.cleanUpChildren(node);
24713 // inserts everything just before this node...
24714 while (node.childNodes.length) {
24715 var cn = node.childNodes[0];
24716 node.removeChild(cn);
24717 node.parentNode.insertBefore(cn, node);
24719 node.parentNode.removeChild(node);
24723 if (!node.attributes || !node.attributes.length) {
24728 this.cleanUpChildren(node);
24732 function cleanAttr(n,v)
24735 if (v.match(/^\./) || v.match(/^\//)) {
24738 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
24741 if (v.match(/^#/)) {
24744 if (v.match(/^\{/)) { // allow template editing.
24747 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
24748 node.removeAttribute(n);
24752 var cwhite = this.cwhite;
24753 var cblack = this.cblack;
24755 function cleanStyle(n,v)
24757 if (v.match(/expression/)) { //XSS?? should we even bother..
24758 node.removeAttribute(n);
24762 var parts = v.split(/;/);
24765 Roo.each(parts, function(p) {
24766 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
24770 var l = p.split(':').shift().replace(/\s+/g,'');
24771 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
24773 if ( cwhite.length && cblack.indexOf(l) > -1) {
24774 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24775 //node.removeAttribute(n);
24779 // only allow 'c whitelisted system attributes'
24780 if ( cwhite.length && cwhite.indexOf(l) < 0) {
24781 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24782 //node.removeAttribute(n);
24792 if (clean.length) {
24793 node.setAttribute(n, clean.join(';'));
24795 node.removeAttribute(n);
24801 for (var i = node.attributes.length-1; i > -1 ; i--) {
24802 var a = node.attributes[i];
24805 if (a.name.toLowerCase().substr(0,2)=='on') {
24806 node.removeAttribute(a.name);
24809 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
24810 node.removeAttribute(a.name);
24813 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
24814 cleanAttr(a.name,a.value); // fixme..
24817 if (a.name == 'style') {
24818 cleanStyle(a.name,a.value);
24821 /// clean up MS crap..
24822 // tecnically this should be a list of valid class'es..
24825 if (a.name == 'class') {
24826 if (a.value.match(/^Mso/)) {
24827 node.removeAttribute('class');
24830 if (a.value.match(/^body$/)) {
24831 node.removeAttribute('class');
24842 this.cleanUpChildren(node);
24848 * Clean up MS wordisms...
24850 cleanWord : function(node)
24853 this.cleanWord(this.doc.body);
24858 node.nodeName == 'SPAN' &&
24859 !node.hasAttributes() &&
24860 node.childNodes.length == 1 &&
24861 node.firstChild.nodeName == "#text"
24863 var textNode = node.firstChild;
24864 node.removeChild(textNode);
24865 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
24866 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
24868 node.parentNode.insertBefore(textNode, node);
24869 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
24870 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
24872 node.parentNode.removeChild(node);
24875 if (node.nodeName == "#text") {
24876 // clean up silly Windows -- stuff?
24879 if (node.nodeName == "#comment") {
24880 node.parentNode.removeChild(node);
24881 // clean up silly Windows -- stuff?
24885 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
24886 node.parentNode.removeChild(node);
24889 //Roo.log(node.tagName);
24890 // remove - but keep children..
24891 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
24892 //Roo.log('-- removed');
24893 while (node.childNodes.length) {
24894 var cn = node.childNodes[0];
24895 node.removeChild(cn);
24896 node.parentNode.insertBefore(cn, node);
24897 // move node to parent - and clean it..
24898 this.cleanWord(cn);
24900 node.parentNode.removeChild(node);
24901 /// no need to iterate chidlren = it's got none..
24902 //this.iterateChildren(node, this.cleanWord);
24906 if (node.className.length) {
24908 var cn = node.className.split(/\W+/);
24910 Roo.each(cn, function(cls) {
24911 if (cls.match(/Mso[a-zA-Z]+/)) {
24916 node.className = cna.length ? cna.join(' ') : '';
24918 node.removeAttribute("class");
24922 if (node.hasAttribute("lang")) {
24923 node.removeAttribute("lang");
24926 if (node.hasAttribute("style")) {
24928 var styles = node.getAttribute("style").split(";");
24930 Roo.each(styles, function(s) {
24931 if (!s.match(/:/)) {
24934 var kv = s.split(":");
24935 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
24938 // what ever is left... we allow.
24941 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
24942 if (!nstyle.length) {
24943 node.removeAttribute('style');
24946 this.iterateChildren(node, this.cleanWord);
24952 * iterateChildren of a Node, calling fn each time, using this as the scole..
24953 * @param {DomNode} node node to iterate children of.
24954 * @param {Function} fn method of this class to call on each item.
24956 iterateChildren : function(node, fn)
24958 if (!node.childNodes.length) {
24961 for (var i = node.childNodes.length-1; i > -1 ; i--) {
24962 fn.call(this, node.childNodes[i])
24968 * cleanTableWidths.
24970 * Quite often pasting from word etc.. results in tables with column and widths.
24971 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
24974 cleanTableWidths : function(node)
24979 this.cleanTableWidths(this.doc.body);
24984 if (node.nodeName == "#text" || node.nodeName == "#comment") {
24987 Roo.log(node.tagName);
24988 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
24989 this.iterateChildren(node, this.cleanTableWidths);
24992 if (node.hasAttribute('width')) {
24993 node.removeAttribute('width');
24997 if (node.hasAttribute("style")) {
25000 var styles = node.getAttribute("style").split(";");
25002 Roo.each(styles, function(s) {
25003 if (!s.match(/:/)) {
25006 var kv = s.split(":");
25007 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25010 // what ever is left... we allow.
25013 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25014 if (!nstyle.length) {
25015 node.removeAttribute('style');
25019 this.iterateChildren(node, this.cleanTableWidths);
25027 domToHTML : function(currentElement, depth, nopadtext) {
25029 depth = depth || 0;
25030 nopadtext = nopadtext || false;
25032 if (!currentElement) {
25033 return this.domToHTML(this.doc.body);
25036 //Roo.log(currentElement);
25038 var allText = false;
25039 var nodeName = currentElement.nodeName;
25040 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25042 if (nodeName == '#text') {
25044 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25049 if (nodeName != 'BODY') {
25052 // Prints the node tagName, such as <A>, <IMG>, etc
25055 for(i = 0; i < currentElement.attributes.length;i++) {
25057 var aname = currentElement.attributes.item(i).name;
25058 if (!currentElement.attributes.item(i).value.length) {
25061 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25064 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25073 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25076 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25081 // Traverse the tree
25083 var currentElementChild = currentElement.childNodes.item(i);
25084 var allText = true;
25085 var innerHTML = '';
25087 while (currentElementChild) {
25088 // Formatting code (indent the tree so it looks nice on the screen)
25089 var nopad = nopadtext;
25090 if (lastnode == 'SPAN') {
25094 if (currentElementChild.nodeName == '#text') {
25095 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25096 toadd = nopadtext ? toadd : toadd.trim();
25097 if (!nopad && toadd.length > 80) {
25098 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
25100 innerHTML += toadd;
25103 currentElementChild = currentElement.childNodes.item(i);
25109 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
25111 // Recursively traverse the tree structure of the child node
25112 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
25113 lastnode = currentElementChild.nodeName;
25115 currentElementChild=currentElement.childNodes.item(i);
25121 // The remaining code is mostly for formatting the tree
25122 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
25127 ret+= "</"+tagName+">";
25133 applyBlacklists : function()
25135 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
25136 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
25140 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25141 if (b.indexOf(tag) > -1) {
25144 this.white.push(tag);
25148 Roo.each(w, function(tag) {
25149 if (b.indexOf(tag) > -1) {
25152 if (this.white.indexOf(tag) > -1) {
25155 this.white.push(tag);
25160 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25161 if (w.indexOf(tag) > -1) {
25164 this.black.push(tag);
25168 Roo.each(b, function(tag) {
25169 if (w.indexOf(tag) > -1) {
25172 if (this.black.indexOf(tag) > -1) {
25175 this.black.push(tag);
25180 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
25181 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
25185 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25186 if (b.indexOf(tag) > -1) {
25189 this.cwhite.push(tag);
25193 Roo.each(w, function(tag) {
25194 if (b.indexOf(tag) > -1) {
25197 if (this.cwhite.indexOf(tag) > -1) {
25200 this.cwhite.push(tag);
25205 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25206 if (w.indexOf(tag) > -1) {
25209 this.cblack.push(tag);
25213 Roo.each(b, function(tag) {
25214 if (w.indexOf(tag) > -1) {
25217 if (this.cblack.indexOf(tag) > -1) {
25220 this.cblack.push(tag);
25225 setStylesheets : function(stylesheets)
25227 if(typeof(stylesheets) == 'string'){
25228 Roo.get(this.iframe.contentDocument.head).createChild({
25230 rel : 'stylesheet',
25239 Roo.each(stylesheets, function(s) {
25244 Roo.get(_this.iframe.contentDocument.head).createChild({
25246 rel : 'stylesheet',
25255 removeStylesheets : function()
25259 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25264 setStyle : function(style)
25266 Roo.get(this.iframe.contentDocument.head).createChild({
25275 // hide stuff that is not compatible
25289 * @event specialkey
25293 * @cfg {String} fieldClass @hide
25296 * @cfg {String} focusClass @hide
25299 * @cfg {String} autoCreate @hide
25302 * @cfg {String} inputType @hide
25305 * @cfg {String} invalidClass @hide
25308 * @cfg {String} invalidText @hide
25311 * @cfg {String} msgFx @hide
25314 * @cfg {String} validateOnBlur @hide
25318 Roo.HtmlEditorCore.white = [
25319 'area', 'br', 'img', 'input', 'hr', 'wbr',
25321 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
25322 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
25323 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
25324 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
25325 'table', 'ul', 'xmp',
25327 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
25330 'dir', 'menu', 'ol', 'ul', 'dl',
25336 Roo.HtmlEditorCore.black = [
25337 // 'embed', 'object', // enable - backend responsiblity to clean thiese
25339 'base', 'basefont', 'bgsound', 'blink', 'body',
25340 'frame', 'frameset', 'head', 'html', 'ilayer',
25341 'iframe', 'layer', 'link', 'meta', 'object',
25342 'script', 'style' ,'title', 'xml' // clean later..
25344 Roo.HtmlEditorCore.clean = [
25345 'script', 'style', 'title', 'xml'
25347 Roo.HtmlEditorCore.remove = [
25352 Roo.HtmlEditorCore.ablack = [
25356 Roo.HtmlEditorCore.aclean = [
25357 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
25361 Roo.HtmlEditorCore.pwhite= [
25362 'http', 'https', 'mailto'
25365 // white listed style attributes.
25366 Roo.HtmlEditorCore.cwhite= [
25367 // 'text-align', /// default is to allow most things..
25373 // black listed style attributes.
25374 Roo.HtmlEditorCore.cblack= [
25375 // 'font-size' -- this can be set by the project
25379 Roo.HtmlEditorCore.swapCodes =[
25398 * @class Roo.bootstrap.HtmlEditor
25399 * @extends Roo.bootstrap.TextArea
25400 * Bootstrap HtmlEditor class
25403 * Create a new HtmlEditor
25404 * @param {Object} config The config object
25407 Roo.bootstrap.HtmlEditor = function(config){
25408 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25409 if (!this.toolbars) {
25410 this.toolbars = [];
25413 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25416 * @event initialize
25417 * Fires when the editor is fully initialized (including the iframe)
25418 * @param {HtmlEditor} this
25423 * Fires when the editor is first receives the focus. Any insertion must wait
25424 * until after this event.
25425 * @param {HtmlEditor} this
25429 * @event beforesync
25430 * Fires before the textarea is updated with content from the editor iframe. Return false
25431 * to cancel the sync.
25432 * @param {HtmlEditor} this
25433 * @param {String} html
25437 * @event beforepush
25438 * Fires before the iframe editor is updated with content from the textarea. Return false
25439 * to cancel the push.
25440 * @param {HtmlEditor} this
25441 * @param {String} html
25446 * Fires when the textarea is updated with content from the editor iframe.
25447 * @param {HtmlEditor} this
25448 * @param {String} html
25453 * Fires when the iframe editor is updated with content from the textarea.
25454 * @param {HtmlEditor} this
25455 * @param {String} html
25459 * @event editmodechange
25460 * Fires when the editor switches edit modes
25461 * @param {HtmlEditor} this
25462 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25464 editmodechange: true,
25466 * @event editorevent
25467 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25468 * @param {HtmlEditor} this
25472 * @event firstfocus
25473 * Fires when on first focus - needed by toolbars..
25474 * @param {HtmlEditor} this
25479 * Auto save the htmlEditor value as a file into Events
25480 * @param {HtmlEditor} this
25484 * @event savedpreview
25485 * preview the saved version of htmlEditor
25486 * @param {HtmlEditor} this
25493 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
25497 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25502 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25507 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
25512 * @cfg {Number} height (in pixels)
25516 * @cfg {Number} width (in pixels)
25521 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25524 stylesheets: false,
25529 // private properties
25530 validationEvent : false,
25532 initialized : false,
25535 onFocus : Roo.emptyFn,
25537 hideMode:'offsets',
25539 tbContainer : false,
25543 toolbarContainer :function() {
25544 return this.wrap.select('.x-html-editor-tb',true).first();
25548 * Protected method that will not generally be called directly. It
25549 * is called when the editor creates its toolbar. Override this method if you need to
25550 * add custom toolbar buttons.
25551 * @param {HtmlEditor} editor
25553 createToolbar : function(){
25554 Roo.log('renewing');
25555 Roo.log("create toolbars");
25557 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25558 this.toolbars[0].render(this.toolbarContainer());
25562 // if (!editor.toolbars || !editor.toolbars.length) {
25563 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25566 // for (var i =0 ; i < editor.toolbars.length;i++) {
25567 // editor.toolbars[i] = Roo.factory(
25568 // typeof(editor.toolbars[i]) == 'string' ?
25569 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
25570 // Roo.bootstrap.HtmlEditor);
25571 // editor.toolbars[i].init(editor);
25577 onRender : function(ct, position)
25579 // Roo.log("Call onRender: " + this.xtype);
25581 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25583 this.wrap = this.inputEl().wrap({
25584 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25587 this.editorcore.onRender(ct, position);
25589 if (this.resizable) {
25590 this.resizeEl = new Roo.Resizable(this.wrap, {
25594 minHeight : this.height,
25595 height: this.height,
25596 handles : this.resizable,
25599 resize : function(r, w, h) {
25600 _t.onResize(w,h); // -something
25606 this.createToolbar(this);
25609 if(!this.width && this.resizable){
25610 this.setSize(this.wrap.getSize());
25612 if (this.resizeEl) {
25613 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25614 // should trigger onReize..
25620 onResize : function(w, h)
25622 Roo.log('resize: ' +w + ',' + h );
25623 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
25627 if(this.inputEl() ){
25628 if(typeof w == 'number'){
25629 var aw = w - this.wrap.getFrameWidth('lr');
25630 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
25633 if(typeof h == 'number'){
25634 var tbh = -11; // fixme it needs to tool bar size!
25635 for (var i =0; i < this.toolbars.length;i++) {
25636 // fixme - ask toolbars for heights?
25637 tbh += this.toolbars[i].el.getHeight();
25638 //if (this.toolbars[i].footer) {
25639 // tbh += this.toolbars[i].footer.el.getHeight();
25647 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25648 ah -= 5; // knock a few pixes off for look..
25649 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
25653 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
25654 this.editorcore.onResize(ew,eh);
25659 * Toggles the editor between standard and source edit mode.
25660 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25662 toggleSourceEdit : function(sourceEditMode)
25664 this.editorcore.toggleSourceEdit(sourceEditMode);
25666 if(this.editorcore.sourceEditMode){
25667 Roo.log('editor - showing textarea');
25670 // Roo.log(this.syncValue());
25672 this.inputEl().removeClass(['hide', 'x-hidden']);
25673 this.inputEl().dom.removeAttribute('tabIndex');
25674 this.inputEl().focus();
25676 Roo.log('editor - hiding textarea');
25678 // Roo.log(this.pushValue());
25681 this.inputEl().addClass(['hide', 'x-hidden']);
25682 this.inputEl().dom.setAttribute('tabIndex', -1);
25683 //this.deferFocus();
25686 if(this.resizable){
25687 this.setSize(this.wrap.getSize());
25690 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
25693 // private (for BoxComponent)
25694 adjustSize : Roo.BoxComponent.prototype.adjustSize,
25696 // private (for BoxComponent)
25697 getResizeEl : function(){
25701 // private (for BoxComponent)
25702 getPositionEl : function(){
25707 initEvents : function(){
25708 this.originalValue = this.getValue();
25712 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25715 // markInvalid : Roo.emptyFn,
25717 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25720 // clearInvalid : Roo.emptyFn,
25722 setValue : function(v){
25723 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
25724 this.editorcore.pushValue();
25729 deferFocus : function(){
25730 this.focus.defer(10, this);
25734 focus : function(){
25735 this.editorcore.focus();
25741 onDestroy : function(){
25747 for (var i =0; i < this.toolbars.length;i++) {
25748 // fixme - ask toolbars for heights?
25749 this.toolbars[i].onDestroy();
25752 this.wrap.dom.innerHTML = '';
25753 this.wrap.remove();
25758 onFirstFocus : function(){
25759 //Roo.log("onFirstFocus");
25760 this.editorcore.onFirstFocus();
25761 for (var i =0; i < this.toolbars.length;i++) {
25762 this.toolbars[i].onFirstFocus();
25768 syncValue : function()
25770 this.editorcore.syncValue();
25773 pushValue : function()
25775 this.editorcore.pushValue();
25779 // hide stuff that is not compatible
25793 * @event specialkey
25797 * @cfg {String} fieldClass @hide
25800 * @cfg {String} focusClass @hide
25803 * @cfg {String} autoCreate @hide
25806 * @cfg {String} inputType @hide
25810 * @cfg {String} invalidText @hide
25813 * @cfg {String} msgFx @hide
25816 * @cfg {String} validateOnBlur @hide
25825 Roo.namespace('Roo.bootstrap.htmleditor');
25827 * @class Roo.bootstrap.HtmlEditorToolbar1
25833 new Roo.bootstrap.HtmlEditor({
25836 new Roo.bootstrap.HtmlEditorToolbar1({
25837 disable : { fonts: 1 , format: 1, ..., ... , ...],
25843 * @cfg {Object} disable List of elements to disable..
25844 * @cfg {Array} btns List of additional buttons.
25848 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
25851 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
25854 Roo.apply(this, config);
25856 // default disabled, based on 'good practice'..
25857 this.disable = this.disable || {};
25858 Roo.applyIf(this.disable, {
25861 specialElements : true
25863 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
25865 this.editor = config.editor;
25866 this.editorcore = config.editor.editorcore;
25868 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
25870 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25871 // dont call parent... till later.
25873 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
25878 editorcore : false,
25883 "h1","h2","h3","h4","h5","h6",
25885 "abbr", "acronym", "address", "cite", "samp", "var",
25889 onRender : function(ct, position)
25891 // Roo.log("Call onRender: " + this.xtype);
25893 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
25895 this.el.dom.style.marginBottom = '0';
25897 var editorcore = this.editorcore;
25898 var editor= this.editor;
25901 var btn = function(id,cmd , toggle, handler, html){
25903 var event = toggle ? 'toggle' : 'click';
25908 xns: Roo.bootstrap,
25912 enableToggle:toggle !== false,
25914 pressed : toggle ? false : null,
25917 a.listeners[toggle ? 'toggle' : 'click'] = function() {
25918 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
25924 // var cb_box = function...
25929 xns: Roo.bootstrap,
25934 xns: Roo.bootstrap,
25938 Roo.each(this.formats, function(f) {
25939 style.menu.items.push({
25941 xns: Roo.bootstrap,
25942 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
25947 editorcore.insertTag(this.tagname);
25954 children.push(style);
25956 btn('bold',false,true);
25957 btn('italic',false,true);
25958 btn('align-left', 'justifyleft',true);
25959 btn('align-center', 'justifycenter',true);
25960 btn('align-right' , 'justifyright',true);
25961 btn('link', false, false, function(btn) {
25962 //Roo.log("create link?");
25963 var url = prompt(this.createLinkText, this.defaultLinkValue);
25964 if(url && url != 'http:/'+'/'){
25965 this.editorcore.relayCmd('createlink', url);
25968 btn('list','insertunorderedlist',true);
25969 btn('pencil', false,true, function(btn){
25971 this.toggleSourceEdit(btn.pressed);
25974 if (this.editor.btns.length > 0) {
25975 for (var i = 0; i<this.editor.btns.length; i++) {
25976 children.push(this.editor.btns[i]);
25984 xns: Roo.bootstrap,
25989 xns: Roo.bootstrap,
25994 cog.menu.items.push({
25996 xns: Roo.bootstrap,
25997 html : Clean styles,
26002 editorcore.insertTag(this.tagname);
26011 this.xtype = 'NavSimplebar';
26013 for(var i=0;i< children.length;i++) {
26015 this.buttons.add(this.addxtypeChild(children[i]));
26019 editor.on('editorevent', this.updateToolbar, this);
26021 onBtnClick : function(id)
26023 this.editorcore.relayCmd(id);
26024 this.editorcore.focus();
26028 * Protected method that will not generally be called directly. It triggers
26029 * a toolbar update by reading the markup state of the current selection in the editor.
26031 updateToolbar: function(){
26033 if(!this.editorcore.activated){
26034 this.editor.onFirstFocus(); // is this neeed?
26038 var btns = this.buttons;
26039 var doc = this.editorcore.doc;
26040 btns.get('bold').setActive(doc.queryCommandState('bold'));
26041 btns.get('italic').setActive(doc.queryCommandState('italic'));
26042 //btns.get('underline').setActive(doc.queryCommandState('underline'));
26044 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26045 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26046 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26048 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26049 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26052 var ans = this.editorcore.getAllAncestors();
26053 if (this.formatCombo) {
26056 var store = this.formatCombo.store;
26057 this.formatCombo.setValue("");
26058 for (var i =0; i < ans.length;i++) {
26059 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26061 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26069 // hides menus... - so this cant be on a menu...
26070 Roo.bootstrap.MenuMgr.hideAll();
26072 Roo.bootstrap.MenuMgr.hideAll();
26073 //this.editorsyncValue();
26075 onFirstFocus: function() {
26076 this.buttons.each(function(item){
26080 toggleSourceEdit : function(sourceEditMode){
26083 if(sourceEditMode){
26084 Roo.log("disabling buttons");
26085 this.buttons.each( function(item){
26086 if(item.cmd != 'pencil'){
26092 Roo.log("enabling buttons");
26093 if(this.editorcore.initialized){
26094 this.buttons.each( function(item){
26100 Roo.log("calling toggole on editor");
26101 // tell the editor that it's been pressed..
26102 this.editor.toggleSourceEdit(sourceEditMode);
26116 * @class Roo.bootstrap.Markdown
26117 * @extends Roo.bootstrap.TextArea
26118 * Bootstrap Showdown editable area
26119 * @cfg {string} content
26122 * Create a new Showdown
26125 Roo.bootstrap.Markdown = function(config){
26126 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26130 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
26134 initEvents : function()
26137 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26138 this.markdownEl = this.el.createChild({
26139 cls : 'roo-markdown-area'
26141 this.inputEl().addClass('d-none');
26142 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26143 this.markdownEl.on('click', this.toggleTextEdit, this);
26144 this.on('blur', this.toggleTextEdit, this);
26145 this.on('specialkey', this.resizeTextArea, this);
26148 toggleTextEdit : function()
26150 var sh = this.markdownEl.getHeight();
26151 this.inputEl().addClass('d-none');
26152 this.markdownEl.addClass('d-none');
26153 if (!this.editing) {
26155 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26156 this.inputEl().removeClass('d-none');
26157 this.inputEl().focus();
26158 this.editing = true;
26161 // show showdown...
26162 this.updateMarkdown();
26163 this.markdownEl.removeClass('d-none');
26164 this.editing = false;
26167 updateMarkdown : function()
26169 if (this.getValue() == '') {
26170 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder);
26173 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26176 resizeTextArea: function () {
26179 Roo.log([sh, this.getValue().split("\n").length * 30]);
26180 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26182 setValue : function(val)
26184 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26185 if (!this.editing) {
26186 this.updateMarkdown();
26192 if (!this.editing) {
26193 this.toggleTextEdit();
26201 * @class Roo.bootstrap.Table.AbstractSelectionModel
26202 * @extends Roo.util.Observable
26203 * Abstract base class for grid SelectionModels. It provides the interface that should be
26204 * implemented by descendant classes. This class should not be directly instantiated.
26207 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26208 this.locked = false;
26209 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26213 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
26214 /** @ignore Called by the grid automatically. Do not call directly. */
26215 init : function(grid){
26221 * Locks the selections.
26224 this.locked = true;
26228 * Unlocks the selections.
26230 unlock : function(){
26231 this.locked = false;
26235 * Returns true if the selections are locked.
26236 * @return {Boolean}
26238 isLocked : function(){
26239 return this.locked;
26243 initEvents : function ()
26249 * @extends Roo.bootstrap.Table.AbstractSelectionModel
26250 * @class Roo.bootstrap.Table.RowSelectionModel
26251 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26252 * It supports multiple selections and keyboard selection/navigation.
26254 * @param {Object} config
26257 Roo.bootstrap.Table.RowSelectionModel = function(config){
26258 Roo.apply(this, config);
26259 this.selections = new Roo.util.MixedCollection(false, function(o){
26264 this.lastActive = false;
26268 * @event selectionchange
26269 * Fires when the selection changes
26270 * @param {SelectionModel} this
26272 "selectionchange" : true,
26274 * @event afterselectionchange
26275 * Fires after the selection changes (eg. by key press or clicking)
26276 * @param {SelectionModel} this
26278 "afterselectionchange" : true,
26280 * @event beforerowselect
26281 * Fires when a row is selected being selected, return false to cancel.
26282 * @param {SelectionModel} this
26283 * @param {Number} rowIndex The selected index
26284 * @param {Boolean} keepExisting False if other selections will be cleared
26286 "beforerowselect" : true,
26289 * Fires when a row is selected.
26290 * @param {SelectionModel} this
26291 * @param {Number} rowIndex The selected index
26292 * @param {Roo.data.Record} r The record
26294 "rowselect" : true,
26296 * @event rowdeselect
26297 * Fires when a row is deselected.
26298 * @param {SelectionModel} this
26299 * @param {Number} rowIndex The selected index
26301 "rowdeselect" : true
26303 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26304 this.locked = false;
26307 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
26309 * @cfg {Boolean} singleSelect
26310 * True to allow selection of only one row at a time (defaults to false)
26312 singleSelect : false,
26315 initEvents : function()
26318 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26319 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
26320 //}else{ // allow click to work like normal
26321 // this.grid.on("rowclick", this.handleDragableRowClick, this);
26323 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26324 this.grid.on("rowclick", this.handleMouseDown, this);
26326 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26327 "up" : function(e){
26329 this.selectPrevious(e.shiftKey);
26330 }else if(this.last !== false && this.lastActive !== false){
26331 var last = this.last;
26332 this.selectRange(this.last, this.lastActive-1);
26333 this.grid.getView().focusRow(this.lastActive);
26334 if(last !== false){
26338 this.selectFirstRow();
26340 this.fireEvent("afterselectionchange", this);
26342 "down" : function(e){
26344 this.selectNext(e.shiftKey);
26345 }else if(this.last !== false && this.lastActive !== false){
26346 var last = this.last;
26347 this.selectRange(this.last, this.lastActive+1);
26348 this.grid.getView().focusRow(this.lastActive);
26349 if(last !== false){
26353 this.selectFirstRow();
26355 this.fireEvent("afterselectionchange", this);
26359 this.grid.store.on('load', function(){
26360 this.selections.clear();
26363 var view = this.grid.view;
26364 view.on("refresh", this.onRefresh, this);
26365 view.on("rowupdated", this.onRowUpdated, this);
26366 view.on("rowremoved", this.onRemove, this);
26371 onRefresh : function()
26373 var ds = this.grid.store, i, v = this.grid.view;
26374 var s = this.selections;
26375 s.each(function(r){
26376 if((i = ds.indexOfId(r.id)) != -1){
26385 onRemove : function(v, index, r){
26386 this.selections.remove(r);
26390 onRowUpdated : function(v, index, r){
26391 if(this.isSelected(r)){
26392 v.onRowSelect(index);
26398 * @param {Array} records The records to select
26399 * @param {Boolean} keepExisting (optional) True to keep existing selections
26401 selectRecords : function(records, keepExisting)
26404 this.clearSelections();
26406 var ds = this.grid.store;
26407 for(var i = 0, len = records.length; i < len; i++){
26408 this.selectRow(ds.indexOf(records[i]), true);
26413 * Gets the number of selected rows.
26416 getCount : function(){
26417 return this.selections.length;
26421 * Selects the first row in the grid.
26423 selectFirstRow : function(){
26428 * Select the last row.
26429 * @param {Boolean} keepExisting (optional) True to keep existing selections
26431 selectLastRow : function(keepExisting){
26432 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26433 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26437 * Selects the row immediately following the last selected row.
26438 * @param {Boolean} keepExisting (optional) True to keep existing selections
26440 selectNext : function(keepExisting)
26442 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26443 this.selectRow(this.last+1, keepExisting);
26444 this.grid.getView().focusRow(this.last);
26449 * Selects the row that precedes the last selected row.
26450 * @param {Boolean} keepExisting (optional) True to keep existing selections
26452 selectPrevious : function(keepExisting){
26454 this.selectRow(this.last-1, keepExisting);
26455 this.grid.getView().focusRow(this.last);
26460 * Returns the selected records
26461 * @return {Array} Array of selected records
26463 getSelections : function(){
26464 return [].concat(this.selections.items);
26468 * Returns the first selected record.
26471 getSelected : function(){
26472 return this.selections.itemAt(0);
26477 * Clears all selections.
26479 clearSelections : function(fast)
26485 var ds = this.grid.store;
26486 var s = this.selections;
26487 s.each(function(r){
26488 this.deselectRow(ds.indexOfId(r.id));
26492 this.selections.clear();
26499 * Selects all rows.
26501 selectAll : function(){
26505 this.selections.clear();
26506 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26507 this.selectRow(i, true);
26512 * Returns True if there is a selection.
26513 * @return {Boolean}
26515 hasSelection : function(){
26516 return this.selections.length > 0;
26520 * Returns True if the specified row is selected.
26521 * @param {Number/Record} record The record or index of the record to check
26522 * @return {Boolean}
26524 isSelected : function(index){
26525 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26526 return (r && this.selections.key(r.id) ? true : false);
26530 * Returns True if the specified record id is selected.
26531 * @param {String} id The id of record to check
26532 * @return {Boolean}
26534 isIdSelected : function(id){
26535 return (this.selections.key(id) ? true : false);
26540 handleMouseDBClick : function(e, t){
26544 handleMouseDown : function(e, t)
26546 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26547 if(this.isLocked() || rowIndex < 0 ){
26550 if(e.shiftKey && this.last !== false){
26551 var last = this.last;
26552 this.selectRange(last, rowIndex, e.ctrlKey);
26553 this.last = last; // reset the last
26557 var isSelected = this.isSelected(rowIndex);
26558 //Roo.log("select row:" + rowIndex);
26560 this.deselectRow(rowIndex);
26562 this.selectRow(rowIndex, true);
26566 if(e.button !== 0 && isSelected){
26567 alert('rowIndex 2: ' + rowIndex);
26568 view.focusRow(rowIndex);
26569 }else if(e.ctrlKey && isSelected){
26570 this.deselectRow(rowIndex);
26571 }else if(!isSelected){
26572 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26573 view.focusRow(rowIndex);
26577 this.fireEvent("afterselectionchange", this);
26580 handleDragableRowClick : function(grid, rowIndex, e)
26582 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26583 this.selectRow(rowIndex, false);
26584 grid.view.focusRow(rowIndex);
26585 this.fireEvent("afterselectionchange", this);
26590 * Selects multiple rows.
26591 * @param {Array} rows Array of the indexes of the row to select
26592 * @param {Boolean} keepExisting (optional) True to keep existing selections
26594 selectRows : function(rows, keepExisting){
26596 this.clearSelections();
26598 for(var i = 0, len = rows.length; i < len; i++){
26599 this.selectRow(rows[i], true);
26604 * Selects a range of rows. All rows in between startRow and endRow are also selected.
26605 * @param {Number} startRow The index of the first row in the range
26606 * @param {Number} endRow The index of the last row in the range
26607 * @param {Boolean} keepExisting (optional) True to retain existing selections
26609 selectRange : function(startRow, endRow, keepExisting){
26614 this.clearSelections();
26616 if(startRow <= endRow){
26617 for(var i = startRow; i <= endRow; i++){
26618 this.selectRow(i, true);
26621 for(var i = startRow; i >= endRow; i--){
26622 this.selectRow(i, true);
26628 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
26629 * @param {Number} startRow The index of the first row in the range
26630 * @param {Number} endRow The index of the last row in the range
26632 deselectRange : function(startRow, endRow, preventViewNotify){
26636 for(var i = startRow; i <= endRow; i++){
26637 this.deselectRow(i, preventViewNotify);
26643 * @param {Number} row The index of the row to select
26644 * @param {Boolean} keepExisting (optional) True to keep existing selections
26646 selectRow : function(index, keepExisting, preventViewNotify)
26648 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
26651 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
26652 if(!keepExisting || this.singleSelect){
26653 this.clearSelections();
26656 var r = this.grid.store.getAt(index);
26657 //console.log('selectRow - record id :' + r.id);
26659 this.selections.add(r);
26660 this.last = this.lastActive = index;
26661 if(!preventViewNotify){
26662 var proxy = new Roo.Element(
26663 this.grid.getRowDom(index)
26665 proxy.addClass('bg-info info');
26667 this.fireEvent("rowselect", this, index, r);
26668 this.fireEvent("selectionchange", this);
26674 * @param {Number} row The index of the row to deselect
26676 deselectRow : function(index, preventViewNotify)
26681 if(this.last == index){
26684 if(this.lastActive == index){
26685 this.lastActive = false;
26688 var r = this.grid.store.getAt(index);
26693 this.selections.remove(r);
26694 //.console.log('deselectRow - record id :' + r.id);
26695 if(!preventViewNotify){
26697 var proxy = new Roo.Element(
26698 this.grid.getRowDom(index)
26700 proxy.removeClass('bg-info info');
26702 this.fireEvent("rowdeselect", this, index);
26703 this.fireEvent("selectionchange", this);
26707 restoreLast : function(){
26709 this.last = this._last;
26714 acceptsNav : function(row, col, cm){
26715 return !cm.isHidden(col) && cm.isCellEditable(col, row);
26719 onEditorKey : function(field, e){
26720 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
26725 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
26727 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
26729 }else if(k == e.ENTER && !e.ctrlKey){
26733 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
26735 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
26737 }else if(k == e.ESC){
26741 g.startEditing(newCell[0], newCell[1]);
26747 * Ext JS Library 1.1.1
26748 * Copyright(c) 2006-2007, Ext JS, LLC.
26750 * Originally Released Under LGPL - original licence link has changed is not relivant.
26753 * <script type="text/javascript">
26757 * @class Roo.bootstrap.PagingToolbar
26758 * @extends Roo.bootstrap.NavSimplebar
26759 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
26761 * Create a new PagingToolbar
26762 * @param {Object} config The config object
26763 * @param {Roo.data.Store} store
26765 Roo.bootstrap.PagingToolbar = function(config)
26767 // old args format still supported... - xtype is prefered..
26768 // created from xtype...
26770 this.ds = config.dataSource;
26772 if (config.store && !this.ds) {
26773 this.store= Roo.factory(config.store, Roo.data);
26774 this.ds = this.store;
26775 this.ds.xmodule = this.xmodule || false;
26778 this.toolbarItems = [];
26779 if (config.items) {
26780 this.toolbarItems = config.items;
26783 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
26788 this.bind(this.ds);
26791 if (Roo.bootstrap.version == 4) {
26792 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
26794 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
26799 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
26801 * @cfg {Roo.data.Store} dataSource
26802 * The underlying data store providing the paged data
26805 * @cfg {String/HTMLElement/Element} container
26806 * container The id or element that will contain the toolbar
26809 * @cfg {Boolean} displayInfo
26810 * True to display the displayMsg (defaults to false)
26813 * @cfg {Number} pageSize
26814 * The number of records to display per page (defaults to 20)
26818 * @cfg {String} displayMsg
26819 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
26821 displayMsg : 'Displaying {0} - {1} of {2}',
26823 * @cfg {String} emptyMsg
26824 * The message to display when no records are found (defaults to "No data to display")
26826 emptyMsg : 'No data to display',
26828 * Customizable piece of the default paging text (defaults to "Page")
26831 beforePageText : "Page",
26833 * Customizable piece of the default paging text (defaults to "of %0")
26836 afterPageText : "of {0}",
26838 * Customizable piece of the default paging text (defaults to "First Page")
26841 firstText : "First Page",
26843 * Customizable piece of the default paging text (defaults to "Previous Page")
26846 prevText : "Previous Page",
26848 * Customizable piece of the default paging text (defaults to "Next Page")
26851 nextText : "Next Page",
26853 * Customizable piece of the default paging text (defaults to "Last Page")
26856 lastText : "Last Page",
26858 * Customizable piece of the default paging text (defaults to "Refresh")
26861 refreshText : "Refresh",
26865 onRender : function(ct, position)
26867 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
26868 this.navgroup.parentId = this.id;
26869 this.navgroup.onRender(this.el, null);
26870 // add the buttons to the navgroup
26872 if(this.displayInfo){
26873 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
26874 this.displayEl = this.el.select('.x-paging-info', true).first();
26875 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
26876 // this.displayEl = navel.el.select('span',true).first();
26882 Roo.each(_this.buttons, function(e){ // this might need to use render????
26883 Roo.factory(e).render(_this.el);
26887 Roo.each(_this.toolbarItems, function(e) {
26888 _this.navgroup.addItem(e);
26892 this.first = this.navgroup.addItem({
26893 tooltip: this.firstText,
26894 cls: "prev btn-outline-secondary",
26895 html : ' <i class="fa fa-step-backward"></i>',
26897 preventDefault: true,
26898 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
26901 this.prev = this.navgroup.addItem({
26902 tooltip: this.prevText,
26903 cls: "prev btn-outline-secondary",
26904 html : ' <i class="fa fa-backward"></i>',
26906 preventDefault: true,
26907 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
26909 //this.addSeparator();
26912 var field = this.navgroup.addItem( {
26914 cls : 'x-paging-position btn-outline-secondary',
26916 html : this.beforePageText +
26917 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
26918 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
26921 this.field = field.el.select('input', true).first();
26922 this.field.on("keydown", this.onPagingKeydown, this);
26923 this.field.on("focus", function(){this.dom.select();});
26926 this.afterTextEl = field.el.select('.x-paging-after',true).first();
26927 //this.field.setHeight(18);
26928 //this.addSeparator();
26929 this.next = this.navgroup.addItem({
26930 tooltip: this.nextText,
26931 cls: "next btn-outline-secondary",
26932 html : ' <i class="fa fa-forward"></i>',
26934 preventDefault: true,
26935 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
26937 this.last = this.navgroup.addItem({
26938 tooltip: this.lastText,
26939 html : ' <i class="fa fa-step-forward"></i>',
26940 cls: "next btn-outline-secondary",
26942 preventDefault: true,
26943 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
26945 //this.addSeparator();
26946 this.loading = this.navgroup.addItem({
26947 tooltip: this.refreshText,
26948 cls: "btn-outline-secondary",
26949 html : ' <i class="fa fa-refresh"></i>',
26950 preventDefault: true,
26951 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
26957 updateInfo : function(){
26958 if(this.displayEl){
26959 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
26960 var msg = count == 0 ?
26964 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
26966 this.displayEl.update(msg);
26971 onLoad : function(ds, r, o)
26973 this.cursor = o.params.start ? o.params.start : 0;
26975 var d = this.getPageData(),
26980 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
26981 this.field.dom.value = ap;
26982 this.first.setDisabled(ap == 1);
26983 this.prev.setDisabled(ap == 1);
26984 this.next.setDisabled(ap == ps);
26985 this.last.setDisabled(ap == ps);
26986 this.loading.enable();
26991 getPageData : function(){
26992 var total = this.ds.getTotalCount();
26995 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
26996 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27001 onLoadError : function(){
27002 this.loading.enable();
27006 onPagingKeydown : function(e){
27007 var k = e.getKey();
27008 var d = this.getPageData();
27010 var v = this.field.dom.value, pageNum;
27011 if(!v || isNaN(pageNum = parseInt(v, 10))){
27012 this.field.dom.value = d.activePage;
27015 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27016 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27019 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))
27021 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27022 this.field.dom.value = pageNum;
27023 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27026 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27028 var v = this.field.dom.value, pageNum;
27029 var increment = (e.shiftKey) ? 10 : 1;
27030 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27033 if(!v || isNaN(pageNum = parseInt(v, 10))) {
27034 this.field.dom.value = d.activePage;
27037 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27039 this.field.dom.value = parseInt(v, 10) + increment;
27040 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27041 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27048 beforeLoad : function(){
27050 this.loading.disable();
27055 onClick : function(which){
27064 ds.load({params:{start: 0, limit: this.pageSize}});
27067 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27070 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27073 var total = ds.getTotalCount();
27074 var extra = total % this.pageSize;
27075 var lastStart = extra ? (total - extra) : total-this.pageSize;
27076 ds.load({params:{start: lastStart, limit: this.pageSize}});
27079 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27085 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27086 * @param {Roo.data.Store} store The data store to unbind
27088 unbind : function(ds){
27089 ds.un("beforeload", this.beforeLoad, this);
27090 ds.un("load", this.onLoad, this);
27091 ds.un("loadexception", this.onLoadError, this);
27092 ds.un("remove", this.updateInfo, this);
27093 ds.un("add", this.updateInfo, this);
27094 this.ds = undefined;
27098 * Binds the paging toolbar to the specified {@link Roo.data.Store}
27099 * @param {Roo.data.Store} store The data store to bind
27101 bind : function(ds){
27102 ds.on("beforeload", this.beforeLoad, this);
27103 ds.on("load", this.onLoad, this);
27104 ds.on("loadexception", this.onLoadError, this);
27105 ds.on("remove", this.updateInfo, this);
27106 ds.on("add", this.updateInfo, this);
27117 * @class Roo.bootstrap.MessageBar
27118 * @extends Roo.bootstrap.Component
27119 * Bootstrap MessageBar class
27120 * @cfg {String} html contents of the MessageBar
27121 * @cfg {String} weight (info | success | warning | danger) default info
27122 * @cfg {String} beforeClass insert the bar before the given class
27123 * @cfg {Boolean} closable (true | false) default false
27124 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27127 * Create a new Element
27128 * @param {Object} config The config object
27131 Roo.bootstrap.MessageBar = function(config){
27132 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27135 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
27141 beforeClass: 'bootstrap-sticky-wrap',
27143 getAutoCreate : function(){
27147 cls: 'alert alert-dismissable alert-' + this.weight,
27152 html: this.html || ''
27158 cfg.cls += ' alert-messages-fixed';
27172 onRender : function(ct, position)
27174 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27177 var cfg = Roo.apply({}, this.getAutoCreate());
27181 cfg.cls += ' ' + this.cls;
27184 cfg.style = this.style;
27186 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27188 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27191 this.el.select('>button.close').on('click', this.hide, this);
27197 if (!this.rendered) {
27203 this.fireEvent('show', this);
27209 if (!this.rendered) {
27215 this.fireEvent('hide', this);
27218 update : function()
27220 // var e = this.el.dom.firstChild;
27222 // if(this.closable){
27223 // e = e.nextSibling;
27226 // e.data = this.html || '';
27228 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27244 * @class Roo.bootstrap.Graph
27245 * @extends Roo.bootstrap.Component
27246 * Bootstrap Graph class
27250 @cfg {String} graphtype bar | vbar | pie
27251 @cfg {number} g_x coodinator | centre x (pie)
27252 @cfg {number} g_y coodinator | centre y (pie)
27253 @cfg {number} g_r radius (pie)
27254 @cfg {number} g_height height of the chart (respected by all elements in the set)
27255 @cfg {number} g_width width of the chart (respected by all elements in the set)
27256 @cfg {Object} title The title of the chart
27259 -opts (object) options for the chart
27261 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27262 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27264 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.
27265 o stacked (boolean) whether or not to tread values as in a stacked bar chart
27267 o stretch (boolean)
27269 -opts (object) options for the pie
27272 o startAngle (number)
27273 o endAngle (number)
27277 * Create a new Input
27278 * @param {Object} config The config object
27281 Roo.bootstrap.Graph = function(config){
27282 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27288 * The img click event for the img.
27289 * @param {Roo.EventObject} e
27295 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
27306 //g_colors: this.colors,
27313 getAutoCreate : function(){
27324 onRender : function(ct,position){
27327 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27329 if (typeof(Raphael) == 'undefined') {
27330 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27334 this.raphael = Raphael(this.el.dom);
27336 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27337 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27338 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27339 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27341 r.text(160, 10, "Single Series Chart").attr(txtattr);
27342 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27343 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27344 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27346 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27347 r.barchart(330, 10, 300, 220, data1);
27348 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27349 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27352 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27353 // r.barchart(30, 30, 560, 250, xdata, {
27354 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27355 // axis : "0 0 1 1",
27356 // axisxlabels : xdata
27357 // //yvalues : cols,
27360 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27362 // this.load(null,xdata,{
27363 // axis : "0 0 1 1",
27364 // axisxlabels : xdata
27369 load : function(graphtype,xdata,opts)
27371 this.raphael.clear();
27373 graphtype = this.graphtype;
27378 var r = this.raphael,
27379 fin = function () {
27380 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27382 fout = function () {
27383 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27385 pfin = function() {
27386 this.sector.stop();
27387 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27390 this.label[0].stop();
27391 this.label[0].attr({ r: 7.5 });
27392 this.label[1].attr({ "font-weight": 800 });
27395 pfout = function() {
27396 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27399 this.label[0].animate({ r: 5 }, 500, "bounce");
27400 this.label[1].attr({ "font-weight": 400 });
27406 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27409 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27412 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
27413 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27415 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27422 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27427 setTitle: function(o)
27432 initEvents: function() {
27435 this.el.on('click', this.onClick, this);
27439 onClick : function(e)
27441 Roo.log('img onclick');
27442 this.fireEvent('click', this, e);
27454 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27457 * @class Roo.bootstrap.dash.NumberBox
27458 * @extends Roo.bootstrap.Component
27459 * Bootstrap NumberBox class
27460 * @cfg {String} headline Box headline
27461 * @cfg {String} content Box content
27462 * @cfg {String} icon Box icon
27463 * @cfg {String} footer Footer text
27464 * @cfg {String} fhref Footer href
27467 * Create a new NumberBox
27468 * @param {Object} config The config object
27472 Roo.bootstrap.dash.NumberBox = function(config){
27473 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27477 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
27486 getAutoCreate : function(){
27490 cls : 'small-box ',
27498 cls : 'roo-headline',
27499 html : this.headline
27503 cls : 'roo-content',
27504 html : this.content
27518 cls : 'ion ' + this.icon
27527 cls : 'small-box-footer',
27528 href : this.fhref || '#',
27532 cfg.cn.push(footer);
27539 onRender : function(ct,position){
27540 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27547 setHeadline: function (value)
27549 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27552 setFooter: function (value, href)
27554 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27557 this.el.select('a.small-box-footer',true).first().attr('href', href);
27562 setContent: function (value)
27564 this.el.select('.roo-content',true).first().dom.innerHTML = value;
27567 initEvents: function()
27581 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27584 * @class Roo.bootstrap.dash.TabBox
27585 * @extends Roo.bootstrap.Component
27586 * Bootstrap TabBox class
27587 * @cfg {String} title Title of the TabBox
27588 * @cfg {String} icon Icon of the TabBox
27589 * @cfg {Boolean} showtabs (true|false) show the tabs default true
27590 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
27593 * Create a new TabBox
27594 * @param {Object} config The config object
27598 Roo.bootstrap.dash.TabBox = function(config){
27599 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
27604 * When a pane is added
27605 * @param {Roo.bootstrap.dash.TabPane} pane
27609 * @event activatepane
27610 * When a pane is activated
27611 * @param {Roo.bootstrap.dash.TabPane} pane
27613 "activatepane" : true
27621 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
27626 tabScrollable : false,
27628 getChildContainer : function()
27630 return this.el.select('.tab-content', true).first();
27633 getAutoCreate : function(){
27637 cls: 'pull-left header',
27645 cls: 'fa ' + this.icon
27651 cls: 'nav nav-tabs pull-right',
27657 if(this.tabScrollable){
27664 cls: 'nav nav-tabs pull-right',
27675 cls: 'nav-tabs-custom',
27680 cls: 'tab-content no-padding',
27688 initEvents : function()
27690 //Roo.log('add add pane handler');
27691 this.on('addpane', this.onAddPane, this);
27694 * Updates the box title
27695 * @param {String} html to set the title to.
27697 setTitle : function(value)
27699 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
27701 onAddPane : function(pane)
27703 this.panes.push(pane);
27704 //Roo.log('addpane');
27706 // tabs are rendere left to right..
27707 if(!this.showtabs){
27711 var ctr = this.el.select('.nav-tabs', true).first();
27714 var existing = ctr.select('.nav-tab',true);
27715 var qty = existing.getCount();;
27718 var tab = ctr.createChild({
27720 cls : 'nav-tab' + (qty ? '' : ' active'),
27728 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
27731 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
27733 pane.el.addClass('active');
27738 onTabClick : function(ev,un,ob,pane)
27740 //Roo.log('tab - prev default');
27741 ev.preventDefault();
27744 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
27745 pane.tab.addClass('active');
27746 //Roo.log(pane.title);
27747 this.getChildContainer().select('.tab-pane',true).removeClass('active');
27748 // technically we should have a deactivate event.. but maybe add later.
27749 // and it should not de-activate the selected tab...
27750 this.fireEvent('activatepane', pane);
27751 pane.el.addClass('active');
27752 pane.fireEvent('activate');
27757 getActivePane : function()
27760 Roo.each(this.panes, function(p) {
27761 if(p.el.hasClass('active')){
27782 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27784 * @class Roo.bootstrap.TabPane
27785 * @extends Roo.bootstrap.Component
27786 * Bootstrap TabPane class
27787 * @cfg {Boolean} active (false | true) Default false
27788 * @cfg {String} title title of panel
27792 * Create a new TabPane
27793 * @param {Object} config The config object
27796 Roo.bootstrap.dash.TabPane = function(config){
27797 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
27803 * When a pane is activated
27804 * @param {Roo.bootstrap.dash.TabPane} pane
27811 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
27816 // the tabBox that this is attached to.
27819 getAutoCreate : function()
27827 cfg.cls += ' active';
27832 initEvents : function()
27834 //Roo.log('trigger add pane handler');
27835 this.parent().fireEvent('addpane', this)
27839 * Updates the tab title
27840 * @param {String} html to set the title to.
27842 setTitle: function(str)
27848 this.tab.select('a', true).first().dom.innerHTML = str;
27865 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
27868 * @class Roo.bootstrap.menu.Menu
27869 * @extends Roo.bootstrap.Component
27870 * Bootstrap Menu class - container for Menu
27871 * @cfg {String} html Text of the menu
27872 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
27873 * @cfg {String} icon Font awesome icon
27874 * @cfg {String} pos Menu align to (top | bottom) default bottom
27878 * Create a new Menu
27879 * @param {Object} config The config object
27883 Roo.bootstrap.menu.Menu = function(config){
27884 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
27888 * @event beforeshow
27889 * Fires before this menu is displayed
27890 * @param {Roo.bootstrap.menu.Menu} this
27894 * @event beforehide
27895 * Fires before this menu is hidden
27896 * @param {Roo.bootstrap.menu.Menu} this
27901 * Fires after this menu is displayed
27902 * @param {Roo.bootstrap.menu.Menu} this
27907 * Fires after this menu is hidden
27908 * @param {Roo.bootstrap.menu.Menu} this
27913 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
27914 * @param {Roo.bootstrap.menu.Menu} this
27915 * @param {Roo.EventObject} e
27922 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
27926 weight : 'default',
27931 getChildContainer : function() {
27932 if(this.isSubMenu){
27936 return this.el.select('ul.dropdown-menu', true).first();
27939 getAutoCreate : function()
27944 cls : 'roo-menu-text',
27952 cls : 'fa ' + this.icon
27963 cls : 'dropdown-button btn btn-' + this.weight,
27968 cls : 'dropdown-toggle btn btn-' + this.weight,
27978 cls : 'dropdown-menu'
27984 if(this.pos == 'top'){
27985 cfg.cls += ' dropup';
27988 if(this.isSubMenu){
27991 cls : 'dropdown-menu'
27998 onRender : function(ct, position)
28000 this.isSubMenu = ct.hasClass('dropdown-submenu');
28002 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28005 initEvents : function()
28007 if(this.isSubMenu){
28011 this.hidden = true;
28013 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28014 this.triggerEl.on('click', this.onTriggerPress, this);
28016 this.buttonEl = this.el.select('button.dropdown-button', true).first();
28017 this.buttonEl.on('click', this.onClick, this);
28023 if(this.isSubMenu){
28027 return this.el.select('ul.dropdown-menu', true).first();
28030 onClick : function(e)
28032 this.fireEvent("click", this, e);
28035 onTriggerPress : function(e)
28037 if (this.isVisible()) {
28044 isVisible : function(){
28045 return !this.hidden;
28050 this.fireEvent("beforeshow", this);
28052 this.hidden = false;
28053 this.el.addClass('open');
28055 Roo.get(document).on("mouseup", this.onMouseUp, this);
28057 this.fireEvent("show", this);
28064 this.fireEvent("beforehide", this);
28066 this.hidden = true;
28067 this.el.removeClass('open');
28069 Roo.get(document).un("mouseup", this.onMouseUp);
28071 this.fireEvent("hide", this);
28074 onMouseUp : function()
28088 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28091 * @class Roo.bootstrap.menu.Item
28092 * @extends Roo.bootstrap.Component
28093 * Bootstrap MenuItem class
28094 * @cfg {Boolean} submenu (true | false) default false
28095 * @cfg {String} html text of the item
28096 * @cfg {String} href the link
28097 * @cfg {Boolean} disable (true | false) default false
28098 * @cfg {Boolean} preventDefault (true | false) default true
28099 * @cfg {String} icon Font awesome icon
28100 * @cfg {String} pos Submenu align to (left | right) default right
28104 * Create a new Item
28105 * @param {Object} config The config object
28109 Roo.bootstrap.menu.Item = function(config){
28110 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28114 * Fires when the mouse is hovering over this menu
28115 * @param {Roo.bootstrap.menu.Item} this
28116 * @param {Roo.EventObject} e
28121 * Fires when the mouse exits this menu
28122 * @param {Roo.bootstrap.menu.Item} this
28123 * @param {Roo.EventObject} e
28129 * The raw click event for the entire grid.
28130 * @param {Roo.EventObject} e
28136 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
28141 preventDefault: true,
28146 getAutoCreate : function()
28151 cls : 'roo-menu-item-text',
28159 cls : 'fa ' + this.icon
28168 href : this.href || '#',
28175 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28179 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28181 if(this.pos == 'left'){
28182 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28189 initEvents : function()
28191 this.el.on('mouseover', this.onMouseOver, this);
28192 this.el.on('mouseout', this.onMouseOut, this);
28194 this.el.select('a', true).first().on('click', this.onClick, this);
28198 onClick : function(e)
28200 if(this.preventDefault){
28201 e.preventDefault();
28204 this.fireEvent("click", this, e);
28207 onMouseOver : function(e)
28209 if(this.submenu && this.pos == 'left'){
28210 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28213 this.fireEvent("mouseover", this, e);
28216 onMouseOut : function(e)
28218 this.fireEvent("mouseout", this, e);
28230 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28233 * @class Roo.bootstrap.menu.Separator
28234 * @extends Roo.bootstrap.Component
28235 * Bootstrap Separator class
28238 * Create a new Separator
28239 * @param {Object} config The config object
28243 Roo.bootstrap.menu.Separator = function(config){
28244 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28247 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
28249 getAutoCreate : function(){
28270 * @class Roo.bootstrap.Tooltip
28271 * Bootstrap Tooltip class
28272 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28273 * to determine which dom element triggers the tooltip.
28275 * It needs to add support for additional attributes like tooltip-position
28278 * Create a new Toolti
28279 * @param {Object} config The config object
28282 Roo.bootstrap.Tooltip = function(config){
28283 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28285 this.alignment = Roo.bootstrap.Tooltip.alignment;
28287 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28288 this.alignment = config.alignment;
28293 Roo.apply(Roo.bootstrap.Tooltip, {
28295 * @function init initialize tooltip monitoring.
28299 currentTip : false,
28300 currentRegion : false,
28306 Roo.get(document).on('mouseover', this.enter ,this);
28307 Roo.get(document).on('mouseout', this.leave, this);
28310 this.currentTip = new Roo.bootstrap.Tooltip();
28313 enter : function(ev)
28315 var dom = ev.getTarget();
28317 //Roo.log(['enter',dom]);
28318 var el = Roo.fly(dom);
28319 if (this.currentEl) {
28321 //Roo.log(this.currentEl);
28322 //Roo.log(this.currentEl.contains(dom));
28323 if (this.currentEl == el) {
28326 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28332 if (this.currentTip.el) {
28333 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28337 if(!el || el.dom == document){
28343 // you can not look for children, as if el is the body.. then everythign is the child..
28344 if (!el.attr('tooltip')) { //
28345 if (!el.select("[tooltip]").elements.length) {
28348 // is the mouse over this child...?
28349 bindEl = el.select("[tooltip]").first();
28350 var xy = ev.getXY();
28351 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28352 //Roo.log("not in region.");
28355 //Roo.log("child element over..");
28358 this.currentEl = bindEl;
28359 this.currentTip.bind(bindEl);
28360 this.currentRegion = Roo.lib.Region.getRegion(dom);
28361 this.currentTip.enter();
28364 leave : function(ev)
28366 var dom = ev.getTarget();
28367 //Roo.log(['leave',dom]);
28368 if (!this.currentEl) {
28373 if (dom != this.currentEl.dom) {
28376 var xy = ev.getXY();
28377 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
28380 // only activate leave if mouse cursor is outside... bounding box..
28385 if (this.currentTip) {
28386 this.currentTip.leave();
28388 //Roo.log('clear currentEl');
28389 this.currentEl = false;
28394 'left' : ['r-l', [-2,0], 'right'],
28395 'right' : ['l-r', [2,0], 'left'],
28396 'bottom' : ['t-b', [0,2], 'top'],
28397 'top' : [ 'b-t', [0,-2], 'bottom']
28403 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
28408 delay : null, // can be { show : 300 , hide: 500}
28412 hoverState : null, //???
28414 placement : 'bottom',
28418 getAutoCreate : function(){
28425 cls : 'tooltip-arrow arrow'
28428 cls : 'tooltip-inner'
28435 bind : function(el)
28440 initEvents : function()
28442 this.arrowEl = this.el.select('.arrow', true).first();
28443 this.innerEl = this.el.select('.tooltip-inner', true).first();
28446 enter : function () {
28448 if (this.timeout != null) {
28449 clearTimeout(this.timeout);
28452 this.hoverState = 'in';
28453 //Roo.log("enter - show");
28454 if (!this.delay || !this.delay.show) {
28459 this.timeout = setTimeout(function () {
28460 if (_t.hoverState == 'in') {
28463 }, this.delay.show);
28467 clearTimeout(this.timeout);
28469 this.hoverState = 'out';
28470 if (!this.delay || !this.delay.hide) {
28476 this.timeout = setTimeout(function () {
28477 //Roo.log("leave - timeout");
28479 if (_t.hoverState == 'out') {
28481 Roo.bootstrap.Tooltip.currentEl = false;
28486 show : function (msg)
28489 this.render(document.body);
28492 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28494 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28496 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28498 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28499 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28501 var placement = typeof this.placement == 'function' ?
28502 this.placement.call(this, this.el, on_el) :
28505 var autoToken = /\s?auto?\s?/i;
28506 var autoPlace = autoToken.test(placement);
28508 placement = placement.replace(autoToken, '') || 'top';
28512 //this.el.setXY([0,0]);
28514 //this.el.dom.style.display='block';
28516 //this.el.appendTo(on_el);
28518 var p = this.getPosition();
28519 var box = this.el.getBox();
28525 var align = this.alignment[placement];
28527 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28529 if(placement == 'top' || placement == 'bottom'){
28531 placement = 'right';
28534 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28535 placement = 'left';
28538 var scroll = Roo.select('body', true).first().getScroll();
28540 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28544 align = this.alignment[placement];
28546 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
28550 this.el.alignTo(this.bindEl, align[0],align[1]);
28551 //var arrow = this.el.select('.arrow',true).first();
28552 //arrow.set(align[2],
28554 this.el.addClass(placement);
28555 this.el.addClass("bs-tooltip-"+ placement);
28557 this.el.addClass('in fade show');
28559 this.hoverState = null;
28561 if (this.el.hasClass('fade')) {
28576 //this.el.setXY([0,0]);
28577 this.el.removeClass(['show', 'in']);
28593 * @class Roo.bootstrap.LocationPicker
28594 * @extends Roo.bootstrap.Component
28595 * Bootstrap LocationPicker class
28596 * @cfg {Number} latitude Position when init default 0
28597 * @cfg {Number} longitude Position when init default 0
28598 * @cfg {Number} zoom default 15
28599 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
28600 * @cfg {Boolean} mapTypeControl default false
28601 * @cfg {Boolean} disableDoubleClickZoom default false
28602 * @cfg {Boolean} scrollwheel default true
28603 * @cfg {Boolean} streetViewControl default false
28604 * @cfg {Number} radius default 0
28605 * @cfg {String} locationName
28606 * @cfg {Boolean} draggable default true
28607 * @cfg {Boolean} enableAutocomplete default false
28608 * @cfg {Boolean} enableReverseGeocode default true
28609 * @cfg {String} markerTitle
28612 * Create a new LocationPicker
28613 * @param {Object} config The config object
28617 Roo.bootstrap.LocationPicker = function(config){
28619 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
28624 * Fires when the picker initialized.
28625 * @param {Roo.bootstrap.LocationPicker} this
28626 * @param {Google Location} location
28630 * @event positionchanged
28631 * Fires when the picker position changed.
28632 * @param {Roo.bootstrap.LocationPicker} this
28633 * @param {Google Location} location
28635 positionchanged : true,
28638 * Fires when the map resize.
28639 * @param {Roo.bootstrap.LocationPicker} this
28644 * Fires when the map show.
28645 * @param {Roo.bootstrap.LocationPicker} this
28650 * Fires when the map hide.
28651 * @param {Roo.bootstrap.LocationPicker} this
28656 * Fires when click the map.
28657 * @param {Roo.bootstrap.LocationPicker} this
28658 * @param {Map event} e
28662 * @event mapRightClick
28663 * Fires when right click the map.
28664 * @param {Roo.bootstrap.LocationPicker} this
28665 * @param {Map event} e
28667 mapRightClick : true,
28669 * @event markerClick
28670 * Fires when click the marker.
28671 * @param {Roo.bootstrap.LocationPicker} this
28672 * @param {Map event} e
28674 markerClick : true,
28676 * @event markerRightClick
28677 * Fires when right click the marker.
28678 * @param {Roo.bootstrap.LocationPicker} this
28679 * @param {Map event} e
28681 markerRightClick : true,
28683 * @event OverlayViewDraw
28684 * Fires when OverlayView Draw
28685 * @param {Roo.bootstrap.LocationPicker} this
28687 OverlayViewDraw : true,
28689 * @event OverlayViewOnAdd
28690 * Fires when OverlayView Draw
28691 * @param {Roo.bootstrap.LocationPicker} this
28693 OverlayViewOnAdd : true,
28695 * @event OverlayViewOnRemove
28696 * Fires when OverlayView Draw
28697 * @param {Roo.bootstrap.LocationPicker} this
28699 OverlayViewOnRemove : true,
28701 * @event OverlayViewShow
28702 * Fires when OverlayView Draw
28703 * @param {Roo.bootstrap.LocationPicker} this
28704 * @param {Pixel} cpx
28706 OverlayViewShow : true,
28708 * @event OverlayViewHide
28709 * Fires when OverlayView Draw
28710 * @param {Roo.bootstrap.LocationPicker} this
28712 OverlayViewHide : true,
28714 * @event loadexception
28715 * Fires when load google lib failed.
28716 * @param {Roo.bootstrap.LocationPicker} this
28718 loadexception : true
28723 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
28725 gMapContext: false,
28731 mapTypeControl: false,
28732 disableDoubleClickZoom: false,
28734 streetViewControl: false,
28738 enableAutocomplete: false,
28739 enableReverseGeocode: true,
28742 getAutoCreate: function()
28747 cls: 'roo-location-picker'
28753 initEvents: function(ct, position)
28755 if(!this.el.getWidth() || this.isApplied()){
28759 this.el.setVisibilityMode(Roo.Element.DISPLAY);
28764 initial: function()
28766 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
28767 this.fireEvent('loadexception', this);
28771 if(!this.mapTypeId){
28772 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
28775 this.gMapContext = this.GMapContext();
28777 this.initOverlayView();
28779 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
28783 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
28784 _this.setPosition(_this.gMapContext.marker.position);
28787 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
28788 _this.fireEvent('mapClick', this, event);
28792 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
28793 _this.fireEvent('mapRightClick', this, event);
28797 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
28798 _this.fireEvent('markerClick', this, event);
28802 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
28803 _this.fireEvent('markerRightClick', this, event);
28807 this.setPosition(this.gMapContext.location);
28809 this.fireEvent('initial', this, this.gMapContext.location);
28812 initOverlayView: function()
28816 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
28820 _this.fireEvent('OverlayViewDraw', _this);
28825 _this.fireEvent('OverlayViewOnAdd', _this);
28828 onRemove: function()
28830 _this.fireEvent('OverlayViewOnRemove', _this);
28833 show: function(cpx)
28835 _this.fireEvent('OverlayViewShow', _this, cpx);
28840 _this.fireEvent('OverlayViewHide', _this);
28846 fromLatLngToContainerPixel: function(event)
28848 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
28851 isApplied: function()
28853 return this.getGmapContext() == false ? false : true;
28856 getGmapContext: function()
28858 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
28861 GMapContext: function()
28863 var position = new google.maps.LatLng(this.latitude, this.longitude);
28865 var _map = new google.maps.Map(this.el.dom, {
28868 mapTypeId: this.mapTypeId,
28869 mapTypeControl: this.mapTypeControl,
28870 disableDoubleClickZoom: this.disableDoubleClickZoom,
28871 scrollwheel: this.scrollwheel,
28872 streetViewControl: this.streetViewControl,
28873 locationName: this.locationName,
28874 draggable: this.draggable,
28875 enableAutocomplete: this.enableAutocomplete,
28876 enableReverseGeocode: this.enableReverseGeocode
28879 var _marker = new google.maps.Marker({
28880 position: position,
28882 title: this.markerTitle,
28883 draggable: this.draggable
28890 location: position,
28891 radius: this.radius,
28892 locationName: this.locationName,
28893 addressComponents: {
28894 formatted_address: null,
28895 addressLine1: null,
28896 addressLine2: null,
28898 streetNumber: null,
28902 stateOrProvince: null
28905 domContainer: this.el.dom,
28906 geodecoder: new google.maps.Geocoder()
28910 drawCircle: function(center, radius, options)
28912 if (this.gMapContext.circle != null) {
28913 this.gMapContext.circle.setMap(null);
28917 options = Roo.apply({}, options, {
28918 strokeColor: "#0000FF",
28919 strokeOpacity: .35,
28921 fillColor: "#0000FF",
28925 options.map = this.gMapContext.map;
28926 options.radius = radius;
28927 options.center = center;
28928 this.gMapContext.circle = new google.maps.Circle(options);
28929 return this.gMapContext.circle;
28935 setPosition: function(location)
28937 this.gMapContext.location = location;
28938 this.gMapContext.marker.setPosition(location);
28939 this.gMapContext.map.panTo(location);
28940 this.drawCircle(location, this.gMapContext.radius, {});
28944 if (this.gMapContext.settings.enableReverseGeocode) {
28945 this.gMapContext.geodecoder.geocode({
28946 latLng: this.gMapContext.location
28947 }, function(results, status) {
28949 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
28950 _this.gMapContext.locationName = results[0].formatted_address;
28951 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
28953 _this.fireEvent('positionchanged', this, location);
28960 this.fireEvent('positionchanged', this, location);
28965 google.maps.event.trigger(this.gMapContext.map, "resize");
28967 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
28969 this.fireEvent('resize', this);
28972 setPositionByLatLng: function(latitude, longitude)
28974 this.setPosition(new google.maps.LatLng(latitude, longitude));
28977 getCurrentPosition: function()
28980 latitude: this.gMapContext.location.lat(),
28981 longitude: this.gMapContext.location.lng()
28985 getAddressName: function()
28987 return this.gMapContext.locationName;
28990 getAddressComponents: function()
28992 return this.gMapContext.addressComponents;
28995 address_component_from_google_geocode: function(address_components)
28999 for (var i = 0; i < address_components.length; i++) {
29000 var component = address_components[i];
29001 if (component.types.indexOf("postal_code") >= 0) {
29002 result.postalCode = component.short_name;
29003 } else if (component.types.indexOf("street_number") >= 0) {
29004 result.streetNumber = component.short_name;
29005 } else if (component.types.indexOf("route") >= 0) {
29006 result.streetName = component.short_name;
29007 } else if (component.types.indexOf("neighborhood") >= 0) {
29008 result.city = component.short_name;
29009 } else if (component.types.indexOf("locality") >= 0) {
29010 result.city = component.short_name;
29011 } else if (component.types.indexOf("sublocality") >= 0) {
29012 result.district = component.short_name;
29013 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29014 result.stateOrProvince = component.short_name;
29015 } else if (component.types.indexOf("country") >= 0) {
29016 result.country = component.short_name;
29020 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29021 result.addressLine2 = "";
29025 setZoomLevel: function(zoom)
29027 this.gMapContext.map.setZoom(zoom);
29040 this.fireEvent('show', this);
29051 this.fireEvent('hide', this);
29056 Roo.apply(Roo.bootstrap.LocationPicker, {
29058 OverlayView : function(map, options)
29060 options = options || {};
29067 * @class Roo.bootstrap.Alert
29068 * @extends Roo.bootstrap.Component
29069 * Bootstrap Alert class - shows an alert area box
29071 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29072 Enter a valid email address
29075 * @cfg {String} title The title of alert
29076 * @cfg {String} html The content of alert
29077 * @cfg {String} weight ( success | info | warning | danger )
29078 * @cfg {String} faicon font-awesomeicon
29081 * Create a new alert
29082 * @param {Object} config The config object
29086 Roo.bootstrap.Alert = function(config){
29087 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29091 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
29098 getAutoCreate : function()
29107 cls : 'roo-alert-icon'
29112 cls : 'roo-alert-title',
29117 cls : 'roo-alert-text',
29124 cfg.cn[0].cls += ' fa ' + this.faicon;
29128 cfg.cls += ' alert-' + this.weight;
29134 initEvents: function()
29136 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29139 setTitle : function(str)
29141 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29144 setText : function(str)
29146 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29149 setWeight : function(weight)
29152 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29155 this.weight = weight;
29157 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29160 setIcon : function(icon)
29163 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29166 this.faicon = icon;
29168 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29189 * @class Roo.bootstrap.UploadCropbox
29190 * @extends Roo.bootstrap.Component
29191 * Bootstrap UploadCropbox class
29192 * @cfg {String} emptyText show when image has been loaded
29193 * @cfg {String} rotateNotify show when image too small to rotate
29194 * @cfg {Number} errorTimeout default 3000
29195 * @cfg {Number} minWidth default 300
29196 * @cfg {Number} minHeight default 300
29197 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29198 * @cfg {Boolean} isDocument (true|false) default false
29199 * @cfg {String} url action url
29200 * @cfg {String} paramName default 'imageUpload'
29201 * @cfg {String} method default POST
29202 * @cfg {Boolean} loadMask (true|false) default true
29203 * @cfg {Boolean} loadingText default 'Loading...'
29206 * Create a new UploadCropbox
29207 * @param {Object} config The config object
29210 Roo.bootstrap.UploadCropbox = function(config){
29211 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29215 * @event beforeselectfile
29216 * Fire before select file
29217 * @param {Roo.bootstrap.UploadCropbox} this
29219 "beforeselectfile" : true,
29222 * Fire after initEvent
29223 * @param {Roo.bootstrap.UploadCropbox} this
29228 * Fire after initEvent
29229 * @param {Roo.bootstrap.UploadCropbox} this
29230 * @param {String} data
29235 * Fire when preparing the file data
29236 * @param {Roo.bootstrap.UploadCropbox} this
29237 * @param {Object} file
29242 * Fire when get exception
29243 * @param {Roo.bootstrap.UploadCropbox} this
29244 * @param {XMLHttpRequest} xhr
29246 "exception" : true,
29248 * @event beforeloadcanvas
29249 * Fire before load the canvas
29250 * @param {Roo.bootstrap.UploadCropbox} this
29251 * @param {String} src
29253 "beforeloadcanvas" : true,
29256 * Fire when trash image
29257 * @param {Roo.bootstrap.UploadCropbox} this
29262 * Fire when download the image
29263 * @param {Roo.bootstrap.UploadCropbox} this
29267 * @event footerbuttonclick
29268 * Fire when footerbuttonclick
29269 * @param {Roo.bootstrap.UploadCropbox} this
29270 * @param {String} type
29272 "footerbuttonclick" : true,
29276 * @param {Roo.bootstrap.UploadCropbox} this
29281 * Fire when rotate the image
29282 * @param {Roo.bootstrap.UploadCropbox} this
29283 * @param {String} pos
29288 * Fire when inspect the file
29289 * @param {Roo.bootstrap.UploadCropbox} this
29290 * @param {Object} file
29295 * Fire when xhr upload the file
29296 * @param {Roo.bootstrap.UploadCropbox} this
29297 * @param {Object} data
29302 * Fire when arrange the file data
29303 * @param {Roo.bootstrap.UploadCropbox} this
29304 * @param {Object} formData
29309 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29312 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
29314 emptyText : 'Click to upload image',
29315 rotateNotify : 'Image is too small to rotate',
29316 errorTimeout : 3000,
29330 cropType : 'image/jpeg',
29332 canvasLoaded : false,
29333 isDocument : false,
29335 paramName : 'imageUpload',
29337 loadingText : 'Loading...',
29340 getAutoCreate : function()
29344 cls : 'roo-upload-cropbox',
29348 cls : 'roo-upload-cropbox-selector',
29353 cls : 'roo-upload-cropbox-body',
29354 style : 'cursor:pointer',
29358 cls : 'roo-upload-cropbox-preview'
29362 cls : 'roo-upload-cropbox-thumb'
29366 cls : 'roo-upload-cropbox-empty-notify',
29367 html : this.emptyText
29371 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29372 html : this.rotateNotify
29378 cls : 'roo-upload-cropbox-footer',
29381 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29391 onRender : function(ct, position)
29393 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29395 if (this.buttons.length) {
29397 Roo.each(this.buttons, function(bb) {
29399 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29401 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29407 this.maskEl = this.el;
29411 initEvents : function()
29413 this.urlAPI = (window.createObjectURL && window) ||
29414 (window.URL && URL.revokeObjectURL && URL) ||
29415 (window.webkitURL && webkitURL);
29417 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29418 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29420 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29421 this.selectorEl.hide();
29423 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29424 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29426 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29427 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29428 this.thumbEl.hide();
29430 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29431 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29433 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29434 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29435 this.errorEl.hide();
29437 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29438 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29439 this.footerEl.hide();
29441 this.setThumbBoxSize();
29447 this.fireEvent('initial', this);
29454 window.addEventListener("resize", function() { _this.resize(); } );
29456 this.bodyEl.on('click', this.beforeSelectFile, this);
29459 this.bodyEl.on('touchstart', this.onTouchStart, this);
29460 this.bodyEl.on('touchmove', this.onTouchMove, this);
29461 this.bodyEl.on('touchend', this.onTouchEnd, this);
29465 this.bodyEl.on('mousedown', this.onMouseDown, this);
29466 this.bodyEl.on('mousemove', this.onMouseMove, this);
29467 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29468 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29469 Roo.get(document).on('mouseup', this.onMouseUp, this);
29472 this.selectorEl.on('change', this.onFileSelected, this);
29478 this.baseScale = 1;
29480 this.baseRotate = 1;
29481 this.dragable = false;
29482 this.pinching = false;
29485 this.cropData = false;
29486 this.notifyEl.dom.innerHTML = this.emptyText;
29488 this.selectorEl.dom.value = '';
29492 resize : function()
29494 if(this.fireEvent('resize', this) != false){
29495 this.setThumbBoxPosition();
29496 this.setCanvasPosition();
29500 onFooterButtonClick : function(e, el, o, type)
29503 case 'rotate-left' :
29504 this.onRotateLeft(e);
29506 case 'rotate-right' :
29507 this.onRotateRight(e);
29510 this.beforeSelectFile(e);
29525 this.fireEvent('footerbuttonclick', this, type);
29528 beforeSelectFile : function(e)
29530 e.preventDefault();
29532 if(this.fireEvent('beforeselectfile', this) != false){
29533 this.selectorEl.dom.click();
29537 onFileSelected : function(e)
29539 e.preventDefault();
29541 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29545 var file = this.selectorEl.dom.files[0];
29547 if(this.fireEvent('inspect', this, file) != false){
29548 this.prepare(file);
29553 trash : function(e)
29555 this.fireEvent('trash', this);
29558 download : function(e)
29560 this.fireEvent('download', this);
29563 loadCanvas : function(src)
29565 if(this.fireEvent('beforeloadcanvas', this, src) != false){
29569 this.imageEl = document.createElement('img');
29573 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29575 this.imageEl.src = src;
29579 onLoadCanvas : function()
29581 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29582 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29584 this.bodyEl.un('click', this.beforeSelectFile, this);
29586 this.notifyEl.hide();
29587 this.thumbEl.show();
29588 this.footerEl.show();
29590 this.baseRotateLevel();
29592 if(this.isDocument){
29593 this.setThumbBoxSize();
29596 this.setThumbBoxPosition();
29598 this.baseScaleLevel();
29604 this.canvasLoaded = true;
29607 this.maskEl.unmask();
29612 setCanvasPosition : function()
29614 if(!this.canvasEl){
29618 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
29619 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
29621 this.previewEl.setLeft(pw);
29622 this.previewEl.setTop(ph);
29626 onMouseDown : function(e)
29630 this.dragable = true;
29631 this.pinching = false;
29633 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
29634 this.dragable = false;
29638 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29639 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29643 onMouseMove : function(e)
29647 if(!this.canvasLoaded){
29651 if (!this.dragable){
29655 var minX = Math.ceil(this.thumbEl.getLeft(true));
29656 var minY = Math.ceil(this.thumbEl.getTop(true));
29658 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
29659 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
29661 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29662 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29664 x = x - this.mouseX;
29665 y = y - this.mouseY;
29667 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
29668 var bgY = Math.ceil(y + this.previewEl.getTop(true));
29670 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
29671 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
29673 this.previewEl.setLeft(bgX);
29674 this.previewEl.setTop(bgY);
29676 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29677 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29680 onMouseUp : function(e)
29684 this.dragable = false;
29687 onMouseWheel : function(e)
29691 this.startScale = this.scale;
29693 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
29695 if(!this.zoomable()){
29696 this.scale = this.startScale;
29705 zoomable : function()
29707 var minScale = this.thumbEl.getWidth() / this.minWidth;
29709 if(this.minWidth < this.minHeight){
29710 minScale = this.thumbEl.getHeight() / this.minHeight;
29713 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
29714 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
29718 (this.rotate == 0 || this.rotate == 180) &&
29720 width > this.imageEl.OriginWidth ||
29721 height > this.imageEl.OriginHeight ||
29722 (width < this.minWidth && height < this.minHeight)
29730 (this.rotate == 90 || this.rotate == 270) &&
29732 width > this.imageEl.OriginWidth ||
29733 height > this.imageEl.OriginHeight ||
29734 (width < this.minHeight && height < this.minWidth)
29741 !this.isDocument &&
29742 (this.rotate == 0 || this.rotate == 180) &&
29744 width < this.minWidth ||
29745 width > this.imageEl.OriginWidth ||
29746 height < this.minHeight ||
29747 height > this.imageEl.OriginHeight
29754 !this.isDocument &&
29755 (this.rotate == 90 || this.rotate == 270) &&
29757 width < this.minHeight ||
29758 width > this.imageEl.OriginWidth ||
29759 height < this.minWidth ||
29760 height > this.imageEl.OriginHeight
29770 onRotateLeft : function(e)
29772 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29774 var minScale = this.thumbEl.getWidth() / this.minWidth;
29776 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29777 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29779 this.startScale = this.scale;
29781 while (this.getScaleLevel() < minScale){
29783 this.scale = this.scale + 1;
29785 if(!this.zoomable()){
29790 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29791 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29796 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29803 this.scale = this.startScale;
29805 this.onRotateFail();
29810 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29812 if(this.isDocument){
29813 this.setThumbBoxSize();
29814 this.setThumbBoxPosition();
29815 this.setCanvasPosition();
29820 this.fireEvent('rotate', this, 'left');
29824 onRotateRight : function(e)
29826 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29828 var minScale = this.thumbEl.getWidth() / this.minWidth;
29830 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29831 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29833 this.startScale = this.scale;
29835 while (this.getScaleLevel() < minScale){
29837 this.scale = this.scale + 1;
29839 if(!this.zoomable()){
29844 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29845 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29850 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29857 this.scale = this.startScale;
29859 this.onRotateFail();
29864 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29866 if(this.isDocument){
29867 this.setThumbBoxSize();
29868 this.setThumbBoxPosition();
29869 this.setCanvasPosition();
29874 this.fireEvent('rotate', this, 'right');
29877 onRotateFail : function()
29879 this.errorEl.show(true);
29883 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
29888 this.previewEl.dom.innerHTML = '';
29890 var canvasEl = document.createElement("canvas");
29892 var contextEl = canvasEl.getContext("2d");
29894 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29895 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29896 var center = this.imageEl.OriginWidth / 2;
29898 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
29899 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29900 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29901 center = this.imageEl.OriginHeight / 2;
29904 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
29906 contextEl.translate(center, center);
29907 contextEl.rotate(this.rotate * Math.PI / 180);
29909 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
29911 this.canvasEl = document.createElement("canvas");
29913 this.contextEl = this.canvasEl.getContext("2d");
29915 switch (this.rotate) {
29918 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29919 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29921 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29926 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29927 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29929 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29930 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);
29934 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29939 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29940 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29942 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29943 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);
29947 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);
29952 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29953 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29955 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29956 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29960 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);
29967 this.previewEl.appendChild(this.canvasEl);
29969 this.setCanvasPosition();
29974 if(!this.canvasLoaded){
29978 var imageCanvas = document.createElement("canvas");
29980 var imageContext = imageCanvas.getContext("2d");
29982 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29983 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29985 var center = imageCanvas.width / 2;
29987 imageContext.translate(center, center);
29989 imageContext.rotate(this.rotate * Math.PI / 180);
29991 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
29993 var canvas = document.createElement("canvas");
29995 var context = canvas.getContext("2d");
29997 canvas.width = this.minWidth;
29998 canvas.height = this.minHeight;
30000 switch (this.rotate) {
30003 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30004 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30006 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30007 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30009 var targetWidth = this.minWidth - 2 * x;
30010 var targetHeight = this.minHeight - 2 * y;
30014 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30015 scale = targetWidth / width;
30018 if(x > 0 && y == 0){
30019 scale = targetHeight / height;
30022 if(x > 0 && y > 0){
30023 scale = targetWidth / width;
30025 if(width < height){
30026 scale = targetHeight / height;
30030 context.scale(scale, scale);
30032 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30033 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30035 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30036 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30038 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30043 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30044 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30046 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30047 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30049 var targetWidth = this.minWidth - 2 * x;
30050 var targetHeight = this.minHeight - 2 * y;
30054 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30055 scale = targetWidth / width;
30058 if(x > 0 && y == 0){
30059 scale = targetHeight / height;
30062 if(x > 0 && y > 0){
30063 scale = targetWidth / width;
30065 if(width < height){
30066 scale = targetHeight / height;
30070 context.scale(scale, scale);
30072 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30073 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30075 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30076 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30078 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30080 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30085 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30086 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30088 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30089 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30091 var targetWidth = this.minWidth - 2 * x;
30092 var targetHeight = this.minHeight - 2 * y;
30096 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30097 scale = targetWidth / width;
30100 if(x > 0 && y == 0){
30101 scale = targetHeight / height;
30104 if(x > 0 && y > 0){
30105 scale = targetWidth / width;
30107 if(width < height){
30108 scale = targetHeight / height;
30112 context.scale(scale, scale);
30114 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30115 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30117 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30118 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30120 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30121 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30123 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30128 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30129 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30131 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30132 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30134 var targetWidth = this.minWidth - 2 * x;
30135 var targetHeight = this.minHeight - 2 * y;
30139 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30140 scale = targetWidth / width;
30143 if(x > 0 && y == 0){
30144 scale = targetHeight / height;
30147 if(x > 0 && y > 0){
30148 scale = targetWidth / width;
30150 if(width < height){
30151 scale = targetHeight / height;
30155 context.scale(scale, scale);
30157 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30158 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30160 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30161 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30163 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30165 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30172 this.cropData = canvas.toDataURL(this.cropType);
30174 if(this.fireEvent('crop', this, this.cropData) !== false){
30175 this.process(this.file, this.cropData);
30182 setThumbBoxSize : function()
30186 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30187 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30188 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30190 this.minWidth = width;
30191 this.minHeight = height;
30193 if(this.rotate == 90 || this.rotate == 270){
30194 this.minWidth = height;
30195 this.minHeight = width;
30200 width = Math.ceil(this.minWidth * height / this.minHeight);
30202 if(this.minWidth > this.minHeight){
30204 height = Math.ceil(this.minHeight * width / this.minWidth);
30207 this.thumbEl.setStyle({
30208 width : width + 'px',
30209 height : height + 'px'
30216 setThumbBoxPosition : function()
30218 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30219 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30221 this.thumbEl.setLeft(x);
30222 this.thumbEl.setTop(y);
30226 baseRotateLevel : function()
30228 this.baseRotate = 1;
30231 typeof(this.exif) != 'undefined' &&
30232 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30233 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30235 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30238 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30242 baseScaleLevel : function()
30246 if(this.isDocument){
30248 if(this.baseRotate == 6 || this.baseRotate == 8){
30250 height = this.thumbEl.getHeight();
30251 this.baseScale = height / this.imageEl.OriginWidth;
30253 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30254 width = this.thumbEl.getWidth();
30255 this.baseScale = width / this.imageEl.OriginHeight;
30261 height = this.thumbEl.getHeight();
30262 this.baseScale = height / this.imageEl.OriginHeight;
30264 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30265 width = this.thumbEl.getWidth();
30266 this.baseScale = width / this.imageEl.OriginWidth;
30272 if(this.baseRotate == 6 || this.baseRotate == 8){
30274 width = this.thumbEl.getHeight();
30275 this.baseScale = width / this.imageEl.OriginHeight;
30277 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30278 height = this.thumbEl.getWidth();
30279 this.baseScale = height / this.imageEl.OriginHeight;
30282 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30283 height = this.thumbEl.getWidth();
30284 this.baseScale = height / this.imageEl.OriginHeight;
30286 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30287 width = this.thumbEl.getHeight();
30288 this.baseScale = width / this.imageEl.OriginWidth;
30295 width = this.thumbEl.getWidth();
30296 this.baseScale = width / this.imageEl.OriginWidth;
30298 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30299 height = this.thumbEl.getHeight();
30300 this.baseScale = height / this.imageEl.OriginHeight;
30303 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30305 height = this.thumbEl.getHeight();
30306 this.baseScale = height / this.imageEl.OriginHeight;
30308 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30309 width = this.thumbEl.getWidth();
30310 this.baseScale = width / this.imageEl.OriginWidth;
30318 getScaleLevel : function()
30320 return this.baseScale * Math.pow(1.1, this.scale);
30323 onTouchStart : function(e)
30325 if(!this.canvasLoaded){
30326 this.beforeSelectFile(e);
30330 var touches = e.browserEvent.touches;
30336 if(touches.length == 1){
30337 this.onMouseDown(e);
30341 if(touches.length != 2){
30347 for(var i = 0, finger; finger = touches[i]; i++){
30348 coords.push(finger.pageX, finger.pageY);
30351 var x = Math.pow(coords[0] - coords[2], 2);
30352 var y = Math.pow(coords[1] - coords[3], 2);
30354 this.startDistance = Math.sqrt(x + y);
30356 this.startScale = this.scale;
30358 this.pinching = true;
30359 this.dragable = false;
30363 onTouchMove : function(e)
30365 if(!this.pinching && !this.dragable){
30369 var touches = e.browserEvent.touches;
30376 this.onMouseMove(e);
30382 for(var i = 0, finger; finger = touches[i]; i++){
30383 coords.push(finger.pageX, finger.pageY);
30386 var x = Math.pow(coords[0] - coords[2], 2);
30387 var y = Math.pow(coords[1] - coords[3], 2);
30389 this.endDistance = Math.sqrt(x + y);
30391 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30393 if(!this.zoomable()){
30394 this.scale = this.startScale;
30402 onTouchEnd : function(e)
30404 this.pinching = false;
30405 this.dragable = false;
30409 process : function(file, crop)
30412 this.maskEl.mask(this.loadingText);
30415 this.xhr = new XMLHttpRequest();
30417 file.xhr = this.xhr;
30419 this.xhr.open(this.method, this.url, true);
30422 "Accept": "application/json",
30423 "Cache-Control": "no-cache",
30424 "X-Requested-With": "XMLHttpRequest"
30427 for (var headerName in headers) {
30428 var headerValue = headers[headerName];
30430 this.xhr.setRequestHeader(headerName, headerValue);
30436 this.xhr.onload = function()
30438 _this.xhrOnLoad(_this.xhr);
30441 this.xhr.onerror = function()
30443 _this.xhrOnError(_this.xhr);
30446 var formData = new FormData();
30448 formData.append('returnHTML', 'NO');
30451 formData.append('crop', crop);
30454 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30455 formData.append(this.paramName, file, file.name);
30458 if(typeof(file.filename) != 'undefined'){
30459 formData.append('filename', file.filename);
30462 if(typeof(file.mimetype) != 'undefined'){
30463 formData.append('mimetype', file.mimetype);
30466 if(this.fireEvent('arrange', this, formData) != false){
30467 this.xhr.send(formData);
30471 xhrOnLoad : function(xhr)
30474 this.maskEl.unmask();
30477 if (xhr.readyState !== 4) {
30478 this.fireEvent('exception', this, xhr);
30482 var response = Roo.decode(xhr.responseText);
30484 if(!response.success){
30485 this.fireEvent('exception', this, xhr);
30489 var response = Roo.decode(xhr.responseText);
30491 this.fireEvent('upload', this, response);
30495 xhrOnError : function()
30498 this.maskEl.unmask();
30501 Roo.log('xhr on error');
30503 var response = Roo.decode(xhr.responseText);
30509 prepare : function(file)
30512 this.maskEl.mask(this.loadingText);
30518 if(typeof(file) === 'string'){
30519 this.loadCanvas(file);
30523 if(!file || !this.urlAPI){
30528 this.cropType = file.type;
30532 if(this.fireEvent('prepare', this, this.file) != false){
30534 var reader = new FileReader();
30536 reader.onload = function (e) {
30537 if (e.target.error) {
30538 Roo.log(e.target.error);
30542 var buffer = e.target.result,
30543 dataView = new DataView(buffer),
30545 maxOffset = dataView.byteLength - 4,
30549 if (dataView.getUint16(0) === 0xffd8) {
30550 while (offset < maxOffset) {
30551 markerBytes = dataView.getUint16(offset);
30553 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30554 markerLength = dataView.getUint16(offset + 2) + 2;
30555 if (offset + markerLength > dataView.byteLength) {
30556 Roo.log('Invalid meta data: Invalid segment size.');
30560 if(markerBytes == 0xffe1){
30561 _this.parseExifData(
30568 offset += markerLength;
30578 var url = _this.urlAPI.createObjectURL(_this.file);
30580 _this.loadCanvas(url);
30585 reader.readAsArrayBuffer(this.file);
30591 parseExifData : function(dataView, offset, length)
30593 var tiffOffset = offset + 10,
30597 if (dataView.getUint32(offset + 4) !== 0x45786966) {
30598 // No Exif data, might be XMP data instead
30602 // Check for the ASCII code for "Exif" (0x45786966):
30603 if (dataView.getUint32(offset + 4) !== 0x45786966) {
30604 // No Exif data, might be XMP data instead
30607 if (tiffOffset + 8 > dataView.byteLength) {
30608 Roo.log('Invalid Exif data: Invalid segment size.');
30611 // Check for the two null bytes:
30612 if (dataView.getUint16(offset + 8) !== 0x0000) {
30613 Roo.log('Invalid Exif data: Missing byte alignment offset.');
30616 // Check the byte alignment:
30617 switch (dataView.getUint16(tiffOffset)) {
30619 littleEndian = true;
30622 littleEndian = false;
30625 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
30628 // Check for the TIFF tag marker (0x002A):
30629 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
30630 Roo.log('Invalid Exif data: Missing TIFF marker.');
30633 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
30634 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
30636 this.parseExifTags(
30639 tiffOffset + dirOffset,
30644 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
30649 if (dirOffset + 6 > dataView.byteLength) {
30650 Roo.log('Invalid Exif data: Invalid directory offset.');
30653 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
30654 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
30655 if (dirEndOffset + 4 > dataView.byteLength) {
30656 Roo.log('Invalid Exif data: Invalid directory size.');
30659 for (i = 0; i < tagsNumber; i += 1) {
30663 dirOffset + 2 + 12 * i, // tag offset
30667 // Return the offset to the next directory:
30668 return dataView.getUint32(dirEndOffset, littleEndian);
30671 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
30673 var tag = dataView.getUint16(offset, littleEndian);
30675 this.exif[tag] = this.getExifValue(
30679 dataView.getUint16(offset + 2, littleEndian), // tag type
30680 dataView.getUint32(offset + 4, littleEndian), // tag length
30685 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
30687 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
30696 Roo.log('Invalid Exif data: Invalid tag type.');
30700 tagSize = tagType.size * length;
30701 // Determine if the value is contained in the dataOffset bytes,
30702 // or if the value at the dataOffset is a pointer to the actual data:
30703 dataOffset = tagSize > 4 ?
30704 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
30705 if (dataOffset + tagSize > dataView.byteLength) {
30706 Roo.log('Invalid Exif data: Invalid data offset.');
30709 if (length === 1) {
30710 return tagType.getValue(dataView, dataOffset, littleEndian);
30713 for (i = 0; i < length; i += 1) {
30714 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
30717 if (tagType.ascii) {
30719 // Concatenate the chars:
30720 for (i = 0; i < values.length; i += 1) {
30722 // Ignore the terminating NULL byte(s):
30723 if (c === '\u0000') {
30735 Roo.apply(Roo.bootstrap.UploadCropbox, {
30737 'Orientation': 0x0112
30741 1: 0, //'top-left',
30743 3: 180, //'bottom-right',
30744 // 4: 'bottom-left',
30746 6: 90, //'right-top',
30747 // 7: 'right-bottom',
30748 8: 270 //'left-bottom'
30752 // byte, 8-bit unsigned int:
30754 getValue: function (dataView, dataOffset) {
30755 return dataView.getUint8(dataOffset);
30759 // ascii, 8-bit byte:
30761 getValue: function (dataView, dataOffset) {
30762 return String.fromCharCode(dataView.getUint8(dataOffset));
30767 // short, 16 bit int:
30769 getValue: function (dataView, dataOffset, littleEndian) {
30770 return dataView.getUint16(dataOffset, littleEndian);
30774 // long, 32 bit int:
30776 getValue: function (dataView, dataOffset, littleEndian) {
30777 return dataView.getUint32(dataOffset, littleEndian);
30781 // rational = two long values, first is numerator, second is denominator:
30783 getValue: function (dataView, dataOffset, littleEndian) {
30784 return dataView.getUint32(dataOffset, littleEndian) /
30785 dataView.getUint32(dataOffset + 4, littleEndian);
30789 // slong, 32 bit signed int:
30791 getValue: function (dataView, dataOffset, littleEndian) {
30792 return dataView.getInt32(dataOffset, littleEndian);
30796 // srational, two slongs, first is numerator, second is denominator:
30798 getValue: function (dataView, dataOffset, littleEndian) {
30799 return dataView.getInt32(dataOffset, littleEndian) /
30800 dataView.getInt32(dataOffset + 4, littleEndian);
30810 cls : 'btn-group roo-upload-cropbox-rotate-left',
30811 action : 'rotate-left',
30815 cls : 'btn btn-default',
30816 html : '<i class="fa fa-undo"></i>'
30822 cls : 'btn-group roo-upload-cropbox-picture',
30823 action : 'picture',
30827 cls : 'btn btn-default',
30828 html : '<i class="fa fa-picture-o"></i>'
30834 cls : 'btn-group roo-upload-cropbox-rotate-right',
30835 action : 'rotate-right',
30839 cls : 'btn btn-default',
30840 html : '<i class="fa fa-repeat"></i>'
30848 cls : 'btn-group roo-upload-cropbox-rotate-left',
30849 action : 'rotate-left',
30853 cls : 'btn btn-default',
30854 html : '<i class="fa fa-undo"></i>'
30860 cls : 'btn-group roo-upload-cropbox-download',
30861 action : 'download',
30865 cls : 'btn btn-default',
30866 html : '<i class="fa fa-download"></i>'
30872 cls : 'btn-group roo-upload-cropbox-crop',
30877 cls : 'btn btn-default',
30878 html : '<i class="fa fa-crop"></i>'
30884 cls : 'btn-group roo-upload-cropbox-trash',
30889 cls : 'btn btn-default',
30890 html : '<i class="fa fa-trash"></i>'
30896 cls : 'btn-group roo-upload-cropbox-rotate-right',
30897 action : 'rotate-right',
30901 cls : 'btn btn-default',
30902 html : '<i class="fa fa-repeat"></i>'
30910 cls : 'btn-group roo-upload-cropbox-rotate-left',
30911 action : 'rotate-left',
30915 cls : 'btn btn-default',
30916 html : '<i class="fa fa-undo"></i>'
30922 cls : 'btn-group roo-upload-cropbox-rotate-right',
30923 action : 'rotate-right',
30927 cls : 'btn btn-default',
30928 html : '<i class="fa fa-repeat"></i>'
30941 * @class Roo.bootstrap.DocumentManager
30942 * @extends Roo.bootstrap.Component
30943 * Bootstrap DocumentManager class
30944 * @cfg {String} paramName default 'imageUpload'
30945 * @cfg {String} toolTipName default 'filename'
30946 * @cfg {String} method default POST
30947 * @cfg {String} url action url
30948 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
30949 * @cfg {Boolean} multiple multiple upload default true
30950 * @cfg {Number} thumbSize default 300
30951 * @cfg {String} fieldLabel
30952 * @cfg {Number} labelWidth default 4
30953 * @cfg {String} labelAlign (left|top) default left
30954 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
30955 * @cfg {Number} labellg set the width of label (1-12)
30956 * @cfg {Number} labelmd set the width of label (1-12)
30957 * @cfg {Number} labelsm set the width of label (1-12)
30958 * @cfg {Number} labelxs set the width of label (1-12)
30961 * Create a new DocumentManager
30962 * @param {Object} config The config object
30965 Roo.bootstrap.DocumentManager = function(config){
30966 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
30969 this.delegates = [];
30974 * Fire when initial the DocumentManager
30975 * @param {Roo.bootstrap.DocumentManager} this
30980 * inspect selected file
30981 * @param {Roo.bootstrap.DocumentManager} this
30982 * @param {File} file
30987 * Fire when xhr load exception
30988 * @param {Roo.bootstrap.DocumentManager} this
30989 * @param {XMLHttpRequest} xhr
30991 "exception" : true,
30993 * @event afterupload
30994 * Fire when xhr load exception
30995 * @param {Roo.bootstrap.DocumentManager} this
30996 * @param {XMLHttpRequest} xhr
30998 "afterupload" : true,
31001 * prepare the form data
31002 * @param {Roo.bootstrap.DocumentManager} this
31003 * @param {Object} formData
31008 * Fire when remove the file
31009 * @param {Roo.bootstrap.DocumentManager} this
31010 * @param {Object} file
31015 * Fire after refresh the file
31016 * @param {Roo.bootstrap.DocumentManager} this
31021 * Fire after click the image
31022 * @param {Roo.bootstrap.DocumentManager} this
31023 * @param {Object} file
31028 * Fire when upload a image and editable set to true
31029 * @param {Roo.bootstrap.DocumentManager} this
31030 * @param {Object} file
31034 * @event beforeselectfile
31035 * Fire before select file
31036 * @param {Roo.bootstrap.DocumentManager} this
31038 "beforeselectfile" : true,
31041 * Fire before process file
31042 * @param {Roo.bootstrap.DocumentManager} this
31043 * @param {Object} file
31047 * @event previewrendered
31048 * Fire when preview rendered
31049 * @param {Roo.bootstrap.DocumentManager} this
31050 * @param {Object} file
31052 "previewrendered" : true,
31055 "previewResize" : true
31060 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
31069 paramName : 'imageUpload',
31070 toolTipName : 'filename',
31073 labelAlign : 'left',
31083 getAutoCreate : function()
31085 var managerWidget = {
31087 cls : 'roo-document-manager',
31091 cls : 'roo-document-manager-selector',
31096 cls : 'roo-document-manager-uploader',
31100 cls : 'roo-document-manager-upload-btn',
31101 html : '<i class="fa fa-plus"></i>'
31112 cls : 'column col-md-12',
31117 if(this.fieldLabel.length){
31122 cls : 'column col-md-12',
31123 html : this.fieldLabel
31127 cls : 'column col-md-12',
31132 if(this.labelAlign == 'left'){
31137 html : this.fieldLabel
31146 if(this.labelWidth > 12){
31147 content[0].style = "width: " + this.labelWidth + 'px';
31150 if(this.labelWidth < 13 && this.labelmd == 0){
31151 this.labelmd = this.labelWidth;
31154 if(this.labellg > 0){
31155 content[0].cls += ' col-lg-' + this.labellg;
31156 content[1].cls += ' col-lg-' + (12 - this.labellg);
31159 if(this.labelmd > 0){
31160 content[0].cls += ' col-md-' + this.labelmd;
31161 content[1].cls += ' col-md-' + (12 - this.labelmd);
31164 if(this.labelsm > 0){
31165 content[0].cls += ' col-sm-' + this.labelsm;
31166 content[1].cls += ' col-sm-' + (12 - this.labelsm);
31169 if(this.labelxs > 0){
31170 content[0].cls += ' col-xs-' + this.labelxs;
31171 content[1].cls += ' col-xs-' + (12 - this.labelxs);
31179 cls : 'row clearfix',
31187 initEvents : function()
31189 this.managerEl = this.el.select('.roo-document-manager', true).first();
31190 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31192 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31193 this.selectorEl.hide();
31196 this.selectorEl.attr('multiple', 'multiple');
31199 this.selectorEl.on('change', this.onFileSelected, this);
31201 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31202 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31204 this.uploader.on('click', this.onUploaderClick, this);
31206 this.renderProgressDialog();
31210 window.addEventListener("resize", function() { _this.refresh(); } );
31212 this.fireEvent('initial', this);
31215 renderProgressDialog : function()
31219 this.progressDialog = new Roo.bootstrap.Modal({
31220 cls : 'roo-document-manager-progress-dialog',
31221 allow_close : false,
31232 btnclick : function() {
31233 _this.uploadCancel();
31239 this.progressDialog.render(Roo.get(document.body));
31241 this.progress = new Roo.bootstrap.Progress({
31242 cls : 'roo-document-manager-progress',
31247 this.progress.render(this.progressDialog.getChildContainer());
31249 this.progressBar = new Roo.bootstrap.ProgressBar({
31250 cls : 'roo-document-manager-progress-bar',
31253 aria_valuemax : 12,
31257 this.progressBar.render(this.progress.getChildContainer());
31260 onUploaderClick : function(e)
31262 e.preventDefault();
31264 if(this.fireEvent('beforeselectfile', this) != false){
31265 this.selectorEl.dom.click();
31270 onFileSelected : function(e)
31272 e.preventDefault();
31274 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31278 Roo.each(this.selectorEl.dom.files, function(file){
31279 if(this.fireEvent('inspect', this, file) != false){
31280 this.files.push(file);
31290 this.selectorEl.dom.value = '';
31292 if(!this.files || !this.files.length){
31296 if(this.boxes > 0 && this.files.length > this.boxes){
31297 this.files = this.files.slice(0, this.boxes);
31300 this.uploader.show();
31302 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31303 this.uploader.hide();
31312 Roo.each(this.files, function(file){
31314 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31315 var f = this.renderPreview(file);
31320 if(file.type.indexOf('image') != -1){
31321 this.delegates.push(
31323 _this.process(file);
31324 }).createDelegate(this)
31332 _this.process(file);
31333 }).createDelegate(this)
31338 this.files = files;
31340 this.delegates = this.delegates.concat(docs);
31342 if(!this.delegates.length){
31347 this.progressBar.aria_valuemax = this.delegates.length;
31354 arrange : function()
31356 if(!this.delegates.length){
31357 this.progressDialog.hide();
31362 var delegate = this.delegates.shift();
31364 this.progressDialog.show();
31366 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31368 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31373 refresh : function()
31375 this.uploader.show();
31377 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31378 this.uploader.hide();
31381 Roo.isTouch ? this.closable(false) : this.closable(true);
31383 this.fireEvent('refresh', this);
31386 onRemove : function(e, el, o)
31388 e.preventDefault();
31390 this.fireEvent('remove', this, o);
31394 remove : function(o)
31398 Roo.each(this.files, function(file){
31399 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31408 this.files = files;
31415 Roo.each(this.files, function(file){
31420 file.target.remove();
31429 onClick : function(e, el, o)
31431 e.preventDefault();
31433 this.fireEvent('click', this, o);
31437 closable : function(closable)
31439 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31441 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31453 xhrOnLoad : function(xhr)
31455 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31459 if (xhr.readyState !== 4) {
31461 this.fireEvent('exception', this, xhr);
31465 var response = Roo.decode(xhr.responseText);
31467 if(!response.success){
31469 this.fireEvent('exception', this, xhr);
31473 var file = this.renderPreview(response.data);
31475 this.files.push(file);
31479 this.fireEvent('afterupload', this, xhr);
31483 xhrOnError : function(xhr)
31485 Roo.log('xhr on error');
31487 var response = Roo.decode(xhr.responseText);
31494 process : function(file)
31496 if(this.fireEvent('process', this, file) !== false){
31497 if(this.editable && file.type.indexOf('image') != -1){
31498 this.fireEvent('edit', this, file);
31502 this.uploadStart(file, false);
31509 uploadStart : function(file, crop)
31511 this.xhr = new XMLHttpRequest();
31513 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31518 file.xhr = this.xhr;
31520 this.managerEl.createChild({
31522 cls : 'roo-document-manager-loading',
31526 tooltip : file.name,
31527 cls : 'roo-document-manager-thumb',
31528 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31534 this.xhr.open(this.method, this.url, true);
31537 "Accept": "application/json",
31538 "Cache-Control": "no-cache",
31539 "X-Requested-With": "XMLHttpRequest"
31542 for (var headerName in headers) {
31543 var headerValue = headers[headerName];
31545 this.xhr.setRequestHeader(headerName, headerValue);
31551 this.xhr.onload = function()
31553 _this.xhrOnLoad(_this.xhr);
31556 this.xhr.onerror = function()
31558 _this.xhrOnError(_this.xhr);
31561 var formData = new FormData();
31563 formData.append('returnHTML', 'NO');
31566 formData.append('crop', crop);
31569 formData.append(this.paramName, file, file.name);
31576 if(this.fireEvent('prepare', this, formData, options) != false){
31578 if(options.manually){
31582 this.xhr.send(formData);
31586 this.uploadCancel();
31589 uploadCancel : function()
31595 this.delegates = [];
31597 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31604 renderPreview : function(file)
31606 if(typeof(file.target) != 'undefined' && file.target){
31610 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
31612 var previewEl = this.managerEl.createChild({
31614 cls : 'roo-document-manager-preview',
31618 tooltip : file[this.toolTipName],
31619 cls : 'roo-document-manager-thumb',
31620 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
31625 html : '<i class="fa fa-times-circle"></i>'
31630 var close = previewEl.select('button.close', true).first();
31632 close.on('click', this.onRemove, this, file);
31634 file.target = previewEl;
31636 var image = previewEl.select('img', true).first();
31640 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
31642 image.on('click', this.onClick, this, file);
31644 this.fireEvent('previewrendered', this, file);
31650 onPreviewLoad : function(file, image)
31652 if(typeof(file.target) == 'undefined' || !file.target){
31656 var width = image.dom.naturalWidth || image.dom.width;
31657 var height = image.dom.naturalHeight || image.dom.height;
31659 if(!this.previewResize) {
31663 if(width > height){
31664 file.target.addClass('wide');
31668 file.target.addClass('tall');
31673 uploadFromSource : function(file, crop)
31675 this.xhr = new XMLHttpRequest();
31677 this.managerEl.createChild({
31679 cls : 'roo-document-manager-loading',
31683 tooltip : file.name,
31684 cls : 'roo-document-manager-thumb',
31685 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31691 this.xhr.open(this.method, this.url, true);
31694 "Accept": "application/json",
31695 "Cache-Control": "no-cache",
31696 "X-Requested-With": "XMLHttpRequest"
31699 for (var headerName in headers) {
31700 var headerValue = headers[headerName];
31702 this.xhr.setRequestHeader(headerName, headerValue);
31708 this.xhr.onload = function()
31710 _this.xhrOnLoad(_this.xhr);
31713 this.xhr.onerror = function()
31715 _this.xhrOnError(_this.xhr);
31718 var formData = new FormData();
31720 formData.append('returnHTML', 'NO');
31722 formData.append('crop', crop);
31724 if(typeof(file.filename) != 'undefined'){
31725 formData.append('filename', file.filename);
31728 if(typeof(file.mimetype) != 'undefined'){
31729 formData.append('mimetype', file.mimetype);
31734 if(this.fireEvent('prepare', this, formData) != false){
31735 this.xhr.send(formData);
31745 * @class Roo.bootstrap.DocumentViewer
31746 * @extends Roo.bootstrap.Component
31747 * Bootstrap DocumentViewer class
31748 * @cfg {Boolean} showDownload (true|false) show download button (default true)
31749 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
31752 * Create a new DocumentViewer
31753 * @param {Object} config The config object
31756 Roo.bootstrap.DocumentViewer = function(config){
31757 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
31762 * Fire after initEvent
31763 * @param {Roo.bootstrap.DocumentViewer} this
31769 * @param {Roo.bootstrap.DocumentViewer} this
31774 * Fire after download button
31775 * @param {Roo.bootstrap.DocumentViewer} this
31780 * Fire after trash button
31781 * @param {Roo.bootstrap.DocumentViewer} this
31788 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
31790 showDownload : true,
31794 getAutoCreate : function()
31798 cls : 'roo-document-viewer',
31802 cls : 'roo-document-viewer-body',
31806 cls : 'roo-document-viewer-thumb',
31810 cls : 'roo-document-viewer-image'
31818 cls : 'roo-document-viewer-footer',
31821 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
31825 cls : 'btn-group roo-document-viewer-download',
31829 cls : 'btn btn-default',
31830 html : '<i class="fa fa-download"></i>'
31836 cls : 'btn-group roo-document-viewer-trash',
31840 cls : 'btn btn-default',
31841 html : '<i class="fa fa-trash"></i>'
31854 initEvents : function()
31856 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
31857 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
31859 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
31860 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
31862 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
31863 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
31865 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
31866 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
31868 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
31869 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
31871 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
31872 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
31874 this.bodyEl.on('click', this.onClick, this);
31875 this.downloadBtn.on('click', this.onDownload, this);
31876 this.trashBtn.on('click', this.onTrash, this);
31878 this.downloadBtn.hide();
31879 this.trashBtn.hide();
31881 if(this.showDownload){
31882 this.downloadBtn.show();
31885 if(this.showTrash){
31886 this.trashBtn.show();
31889 if(!this.showDownload && !this.showTrash) {
31890 this.footerEl.hide();
31895 initial : function()
31897 this.fireEvent('initial', this);
31901 onClick : function(e)
31903 e.preventDefault();
31905 this.fireEvent('click', this);
31908 onDownload : function(e)
31910 e.preventDefault();
31912 this.fireEvent('download', this);
31915 onTrash : function(e)
31917 e.preventDefault();
31919 this.fireEvent('trash', this);
31931 * @class Roo.bootstrap.NavProgressBar
31932 * @extends Roo.bootstrap.Component
31933 * Bootstrap NavProgressBar class
31936 * Create a new nav progress bar
31937 * @param {Object} config The config object
31940 Roo.bootstrap.NavProgressBar = function(config){
31941 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
31943 this.bullets = this.bullets || [];
31945 // Roo.bootstrap.NavProgressBar.register(this);
31949 * Fires when the active item changes
31950 * @param {Roo.bootstrap.NavProgressBar} this
31951 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
31952 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
31959 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
31964 getAutoCreate : function()
31966 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
31970 cls : 'roo-navigation-bar-group',
31974 cls : 'roo-navigation-top-bar'
31978 cls : 'roo-navigation-bullets-bar',
31982 cls : 'roo-navigation-bar'
31989 cls : 'roo-navigation-bottom-bar'
31999 initEvents: function()
32004 onRender : function(ct, position)
32006 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32008 if(this.bullets.length){
32009 Roo.each(this.bullets, function(b){
32018 addItem : function(cfg)
32020 var item = new Roo.bootstrap.NavProgressItem(cfg);
32022 item.parentId = this.id;
32023 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32026 var top = new Roo.bootstrap.Element({
32028 cls : 'roo-navigation-bar-text'
32031 var bottom = new Roo.bootstrap.Element({
32033 cls : 'roo-navigation-bar-text'
32036 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32037 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32039 var topText = new Roo.bootstrap.Element({
32041 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32044 var bottomText = new Roo.bootstrap.Element({
32046 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32049 topText.onRender(top.el, null);
32050 bottomText.onRender(bottom.el, null);
32053 item.bottomEl = bottom;
32056 this.barItems.push(item);
32061 getActive : function()
32063 var active = false;
32065 Roo.each(this.barItems, function(v){
32067 if (!v.isActive()) {
32079 setActiveItem : function(item)
32083 Roo.each(this.barItems, function(v){
32084 if (v.rid == item.rid) {
32088 if (v.isActive()) {
32089 v.setActive(false);
32094 item.setActive(true);
32096 this.fireEvent('changed', this, item, prev);
32099 getBarItem: function(rid)
32103 Roo.each(this.barItems, function(e) {
32104 if (e.rid != rid) {
32115 indexOfItem : function(item)
32119 Roo.each(this.barItems, function(v, i){
32121 if (v.rid != item.rid) {
32132 setActiveNext : function()
32134 var i = this.indexOfItem(this.getActive());
32136 if (i > this.barItems.length) {
32140 this.setActiveItem(this.barItems[i+1]);
32143 setActivePrev : function()
32145 var i = this.indexOfItem(this.getActive());
32151 this.setActiveItem(this.barItems[i-1]);
32154 format : function()
32156 if(!this.barItems.length){
32160 var width = 100 / this.barItems.length;
32162 Roo.each(this.barItems, function(i){
32163 i.el.setStyle('width', width + '%');
32164 i.topEl.el.setStyle('width', width + '%');
32165 i.bottomEl.el.setStyle('width', width + '%');
32174 * Nav Progress Item
32179 * @class Roo.bootstrap.NavProgressItem
32180 * @extends Roo.bootstrap.Component
32181 * Bootstrap NavProgressItem class
32182 * @cfg {String} rid the reference id
32183 * @cfg {Boolean} active (true|false) Is item active default false
32184 * @cfg {Boolean} disabled (true|false) Is item active default false
32185 * @cfg {String} html
32186 * @cfg {String} position (top|bottom) text position default bottom
32187 * @cfg {String} icon show icon instead of number
32190 * Create a new NavProgressItem
32191 * @param {Object} config The config object
32193 Roo.bootstrap.NavProgressItem = function(config){
32194 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32199 * The raw click event for the entire grid.
32200 * @param {Roo.bootstrap.NavProgressItem} this
32201 * @param {Roo.EventObject} e
32208 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
32214 position : 'bottom',
32217 getAutoCreate : function()
32219 var iconCls = 'roo-navigation-bar-item-icon';
32221 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32225 cls: 'roo-navigation-bar-item',
32235 cfg.cls += ' active';
32238 cfg.cls += ' disabled';
32244 disable : function()
32246 this.setDisabled(true);
32249 enable : function()
32251 this.setDisabled(false);
32254 initEvents: function()
32256 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32258 this.iconEl.on('click', this.onClick, this);
32261 onClick : function(e)
32263 e.preventDefault();
32269 if(this.fireEvent('click', this, e) === false){
32273 this.parent().setActiveItem(this);
32276 isActive: function ()
32278 return this.active;
32281 setActive : function(state)
32283 if(this.active == state){
32287 this.active = state;
32290 this.el.addClass('active');
32294 this.el.removeClass('active');
32299 setDisabled : function(state)
32301 if(this.disabled == state){
32305 this.disabled = state;
32308 this.el.addClass('disabled');
32312 this.el.removeClass('disabled');
32315 tooltipEl : function()
32317 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32330 * @class Roo.bootstrap.FieldLabel
32331 * @extends Roo.bootstrap.Component
32332 * Bootstrap FieldLabel class
32333 * @cfg {String} html contents of the element
32334 * @cfg {String} tag tag of the element default label
32335 * @cfg {String} cls class of the element
32336 * @cfg {String} target label target
32337 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32338 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32339 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32340 * @cfg {String} iconTooltip default "This field is required"
32341 * @cfg {String} indicatorpos (left|right) default left
32344 * Create a new FieldLabel
32345 * @param {Object} config The config object
32348 Roo.bootstrap.FieldLabel = function(config){
32349 Roo.bootstrap.Element.superclass.constructor.call(this, config);
32354 * Fires after the field has been marked as invalid.
32355 * @param {Roo.form.FieldLabel} this
32356 * @param {String} msg The validation message
32361 * Fires after the field has been validated with no errors.
32362 * @param {Roo.form.FieldLabel} this
32368 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
32375 invalidClass : 'has-warning',
32376 validClass : 'has-success',
32377 iconTooltip : 'This field is required',
32378 indicatorpos : 'left',
32380 getAutoCreate : function(){
32383 if (!this.allowBlank) {
32389 cls : 'roo-bootstrap-field-label ' + this.cls,
32394 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32395 tooltip : this.iconTooltip
32404 if(this.indicatorpos == 'right'){
32407 cls : 'roo-bootstrap-field-label ' + this.cls,
32416 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32417 tooltip : this.iconTooltip
32426 initEvents: function()
32428 Roo.bootstrap.Element.superclass.initEvents.call(this);
32430 this.indicator = this.indicatorEl();
32432 if(this.indicator){
32433 this.indicator.removeClass('visible');
32434 this.indicator.addClass('invisible');
32437 Roo.bootstrap.FieldLabel.register(this);
32440 indicatorEl : function()
32442 var indicator = this.el.select('i.roo-required-indicator',true).first();
32453 * Mark this field as valid
32455 markValid : function()
32457 if(this.indicator){
32458 this.indicator.removeClass('visible');
32459 this.indicator.addClass('invisible');
32461 if (Roo.bootstrap.version == 3) {
32462 this.el.removeClass(this.invalidClass);
32463 this.el.addClass(this.validClass);
32465 this.el.removeClass('is-invalid');
32466 this.el.addClass('is-valid');
32470 this.fireEvent('valid', this);
32474 * Mark this field as invalid
32475 * @param {String} msg The validation message
32477 markInvalid : function(msg)
32479 if(this.indicator){
32480 this.indicator.removeClass('invisible');
32481 this.indicator.addClass('visible');
32483 if (Roo.bootstrap.version == 3) {
32484 this.el.removeClass(this.validClass);
32485 this.el.addClass(this.invalidClass);
32487 this.el.removeClass('is-valid');
32488 this.el.addClass('is-invalid');
32492 this.fireEvent('invalid', this, msg);
32498 Roo.apply(Roo.bootstrap.FieldLabel, {
32503 * register a FieldLabel Group
32504 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32506 register : function(label)
32508 if(this.groups.hasOwnProperty(label.target)){
32512 this.groups[label.target] = label;
32516 * fetch a FieldLabel Group based on the target
32517 * @param {string} target
32518 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32520 get: function(target) {
32521 if (typeof(this.groups[target]) == 'undefined') {
32525 return this.groups[target] ;
32534 * page DateSplitField.
32540 * @class Roo.bootstrap.DateSplitField
32541 * @extends Roo.bootstrap.Component
32542 * Bootstrap DateSplitField class
32543 * @cfg {string} fieldLabel - the label associated
32544 * @cfg {Number} labelWidth set the width of label (0-12)
32545 * @cfg {String} labelAlign (top|left)
32546 * @cfg {Boolean} dayAllowBlank (true|false) default false
32547 * @cfg {Boolean} monthAllowBlank (true|false) default false
32548 * @cfg {Boolean} yearAllowBlank (true|false) default false
32549 * @cfg {string} dayPlaceholder
32550 * @cfg {string} monthPlaceholder
32551 * @cfg {string} yearPlaceholder
32552 * @cfg {string} dayFormat default 'd'
32553 * @cfg {string} monthFormat default 'm'
32554 * @cfg {string} yearFormat default 'Y'
32555 * @cfg {Number} labellg set the width of label (1-12)
32556 * @cfg {Number} labelmd set the width of label (1-12)
32557 * @cfg {Number} labelsm set the width of label (1-12)
32558 * @cfg {Number} labelxs set the width of label (1-12)
32562 * Create a new DateSplitField
32563 * @param {Object} config The config object
32566 Roo.bootstrap.DateSplitField = function(config){
32567 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32573 * getting the data of years
32574 * @param {Roo.bootstrap.DateSplitField} this
32575 * @param {Object} years
32580 * getting the data of days
32581 * @param {Roo.bootstrap.DateSplitField} this
32582 * @param {Object} days
32587 * Fires after the field has been marked as invalid.
32588 * @param {Roo.form.Field} this
32589 * @param {String} msg The validation message
32594 * Fires after the field has been validated with no errors.
32595 * @param {Roo.form.Field} this
32601 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
32604 labelAlign : 'top',
32606 dayAllowBlank : false,
32607 monthAllowBlank : false,
32608 yearAllowBlank : false,
32609 dayPlaceholder : '',
32610 monthPlaceholder : '',
32611 yearPlaceholder : '',
32615 isFormField : true,
32621 getAutoCreate : function()
32625 cls : 'row roo-date-split-field-group',
32630 cls : 'form-hidden-field roo-date-split-field-group-value',
32636 var labelCls = 'col-md-12';
32637 var contentCls = 'col-md-4';
32639 if(this.fieldLabel){
32643 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
32647 html : this.fieldLabel
32652 if(this.labelAlign == 'left'){
32654 if(this.labelWidth > 12){
32655 label.style = "width: " + this.labelWidth + 'px';
32658 if(this.labelWidth < 13 && this.labelmd == 0){
32659 this.labelmd = this.labelWidth;
32662 if(this.labellg > 0){
32663 labelCls = ' col-lg-' + this.labellg;
32664 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
32667 if(this.labelmd > 0){
32668 labelCls = ' col-md-' + this.labelmd;
32669 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
32672 if(this.labelsm > 0){
32673 labelCls = ' col-sm-' + this.labelsm;
32674 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
32677 if(this.labelxs > 0){
32678 labelCls = ' col-xs-' + this.labelxs;
32679 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
32683 label.cls += ' ' + labelCls;
32685 cfg.cn.push(label);
32688 Roo.each(['day', 'month', 'year'], function(t){
32691 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
32698 inputEl: function ()
32700 return this.el.select('.roo-date-split-field-group-value', true).first();
32703 onRender : function(ct, position)
32707 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32709 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
32711 this.dayField = new Roo.bootstrap.ComboBox({
32712 allowBlank : this.dayAllowBlank,
32713 alwaysQuery : true,
32714 displayField : 'value',
32717 forceSelection : true,
32719 placeholder : this.dayPlaceholder,
32720 selectOnFocus : true,
32721 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32722 triggerAction : 'all',
32724 valueField : 'value',
32725 store : new Roo.data.SimpleStore({
32726 data : (function() {
32728 _this.fireEvent('days', _this, days);
32731 fields : [ 'value' ]
32734 select : function (_self, record, index)
32736 _this.setValue(_this.getValue());
32741 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
32743 this.monthField = new Roo.bootstrap.MonthField({
32744 after : '<i class=\"fa fa-calendar\"></i>',
32745 allowBlank : this.monthAllowBlank,
32746 placeholder : this.monthPlaceholder,
32749 render : function (_self)
32751 this.el.select('span.input-group-addon', true).first().on('click', function(e){
32752 e.preventDefault();
32756 select : function (_self, oldvalue, newvalue)
32758 _this.setValue(_this.getValue());
32763 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
32765 this.yearField = new Roo.bootstrap.ComboBox({
32766 allowBlank : this.yearAllowBlank,
32767 alwaysQuery : true,
32768 displayField : 'value',
32771 forceSelection : true,
32773 placeholder : this.yearPlaceholder,
32774 selectOnFocus : true,
32775 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32776 triggerAction : 'all',
32778 valueField : 'value',
32779 store : new Roo.data.SimpleStore({
32780 data : (function() {
32782 _this.fireEvent('years', _this, years);
32785 fields : [ 'value' ]
32788 select : function (_self, record, index)
32790 _this.setValue(_this.getValue());
32795 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
32798 setValue : function(v, format)
32800 this.inputEl.dom.value = v;
32802 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
32804 var d = Date.parseDate(v, f);
32811 this.setDay(d.format(this.dayFormat));
32812 this.setMonth(d.format(this.monthFormat));
32813 this.setYear(d.format(this.yearFormat));
32820 setDay : function(v)
32822 this.dayField.setValue(v);
32823 this.inputEl.dom.value = this.getValue();
32828 setMonth : function(v)
32830 this.monthField.setValue(v, true);
32831 this.inputEl.dom.value = this.getValue();
32836 setYear : function(v)
32838 this.yearField.setValue(v);
32839 this.inputEl.dom.value = this.getValue();
32844 getDay : function()
32846 return this.dayField.getValue();
32849 getMonth : function()
32851 return this.monthField.getValue();
32854 getYear : function()
32856 return this.yearField.getValue();
32859 getValue : function()
32861 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
32863 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
32873 this.inputEl.dom.value = '';
32878 validate : function()
32880 var d = this.dayField.validate();
32881 var m = this.monthField.validate();
32882 var y = this.yearField.validate();
32887 (!this.dayAllowBlank && !d) ||
32888 (!this.monthAllowBlank && !m) ||
32889 (!this.yearAllowBlank && !y)
32894 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
32903 this.markInvalid();
32908 markValid : function()
32911 var label = this.el.select('label', true).first();
32912 var icon = this.el.select('i.fa-star', true).first();
32918 this.fireEvent('valid', this);
32922 * Mark this field as invalid
32923 * @param {String} msg The validation message
32925 markInvalid : function(msg)
32928 var label = this.el.select('label', true).first();
32929 var icon = this.el.select('i.fa-star', true).first();
32931 if(label && !icon){
32932 this.el.select('.roo-date-split-field-label', true).createChild({
32934 cls : 'text-danger fa fa-lg fa-star',
32935 tooltip : 'This field is required',
32936 style : 'margin-right:5px;'
32940 this.fireEvent('invalid', this, msg);
32943 clearInvalid : function()
32945 var label = this.el.select('label', true).first();
32946 var icon = this.el.select('i.fa-star', true).first();
32952 this.fireEvent('valid', this);
32955 getName: function()
32965 * http://masonry.desandro.com
32967 * The idea is to render all the bricks based on vertical width...
32969 * The original code extends 'outlayer' - we might need to use that....
32975 * @class Roo.bootstrap.LayoutMasonry
32976 * @extends Roo.bootstrap.Component
32977 * Bootstrap Layout Masonry class
32980 * Create a new Element
32981 * @param {Object} config The config object
32984 Roo.bootstrap.LayoutMasonry = function(config){
32986 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
32990 Roo.bootstrap.LayoutMasonry.register(this);
32996 * Fire after layout the items
32997 * @param {Roo.bootstrap.LayoutMasonry} this
32998 * @param {Roo.EventObject} e
33005 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
33008 * @cfg {Boolean} isLayoutInstant = no animation?
33010 isLayoutInstant : false, // needed?
33013 * @cfg {Number} boxWidth width of the columns
33018 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
33023 * @cfg {Number} padWidth padding below box..
33028 * @cfg {Number} gutter gutter width..
33033 * @cfg {Number} maxCols maximum number of columns
33039 * @cfg {Boolean} isAutoInitial defalut true
33041 isAutoInitial : true,
33046 * @cfg {Boolean} isHorizontal defalut false
33048 isHorizontal : false,
33050 currentSize : null,
33056 bricks: null, //CompositeElement
33060 _isLayoutInited : false,
33062 // isAlternative : false, // only use for vertical layout...
33065 * @cfg {Number} alternativePadWidth padding below box..
33067 alternativePadWidth : 50,
33069 selectedBrick : [],
33071 getAutoCreate : function(){
33073 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33077 cls: 'blog-masonary-wrapper ' + this.cls,
33079 cls : 'mas-boxes masonary'
33086 getChildContainer: function( )
33088 if (this.boxesEl) {
33089 return this.boxesEl;
33092 this.boxesEl = this.el.select('.mas-boxes').first();
33094 return this.boxesEl;
33098 initEvents : function()
33102 if(this.isAutoInitial){
33103 Roo.log('hook children rendered');
33104 this.on('childrenrendered', function() {
33105 Roo.log('children rendered');
33111 initial : function()
33113 this.selectedBrick = [];
33115 this.currentSize = this.el.getBox(true);
33117 Roo.EventManager.onWindowResize(this.resize, this);
33119 if(!this.isAutoInitial){
33127 //this.layout.defer(500,this);
33131 resize : function()
33133 var cs = this.el.getBox(true);
33136 this.currentSize.width == cs.width &&
33137 this.currentSize.x == cs.x &&
33138 this.currentSize.height == cs.height &&
33139 this.currentSize.y == cs.y
33141 Roo.log("no change in with or X or Y");
33145 this.currentSize = cs;
33151 layout : function()
33153 this._resetLayout();
33155 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33157 this.layoutItems( isInstant );
33159 this._isLayoutInited = true;
33161 this.fireEvent('layout', this);
33165 _resetLayout : function()
33167 if(this.isHorizontal){
33168 this.horizontalMeasureColumns();
33172 this.verticalMeasureColumns();
33176 verticalMeasureColumns : function()
33178 this.getContainerWidth();
33180 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33181 // this.colWidth = Math.floor(this.containerWidth * 0.8);
33185 var boxWidth = this.boxWidth + this.padWidth;
33187 if(this.containerWidth < this.boxWidth){
33188 boxWidth = this.containerWidth
33191 var containerWidth = this.containerWidth;
33193 var cols = Math.floor(containerWidth / boxWidth);
33195 this.cols = Math.max( cols, 1 );
33197 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33199 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33201 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33203 this.colWidth = boxWidth + avail - this.padWidth;
33205 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33206 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
33209 horizontalMeasureColumns : function()
33211 this.getContainerWidth();
33213 var boxWidth = this.boxWidth;
33215 if(this.containerWidth < boxWidth){
33216 boxWidth = this.containerWidth;
33219 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33221 this.el.setHeight(boxWidth);
33225 getContainerWidth : function()
33227 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
33230 layoutItems : function( isInstant )
33232 Roo.log(this.bricks);
33234 var items = Roo.apply([], this.bricks);
33236 if(this.isHorizontal){
33237 this._horizontalLayoutItems( items , isInstant );
33241 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33242 // this._verticalAlternativeLayoutItems( items , isInstant );
33246 this._verticalLayoutItems( items , isInstant );
33250 _verticalLayoutItems : function ( items , isInstant)
33252 if ( !items || !items.length ) {
33257 ['xs', 'xs', 'xs', 'tall'],
33258 ['xs', 'xs', 'tall'],
33259 ['xs', 'xs', 'sm'],
33260 ['xs', 'xs', 'xs'],
33266 ['sm', 'xs', 'xs'],
33270 ['tall', 'xs', 'xs', 'xs'],
33271 ['tall', 'xs', 'xs'],
33283 Roo.each(items, function(item, k){
33285 switch (item.size) {
33286 // these layouts take up a full box,
33297 boxes.push([item]);
33320 var filterPattern = function(box, length)
33328 var pattern = box.slice(0, length);
33332 Roo.each(pattern, function(i){
33333 format.push(i.size);
33336 Roo.each(standard, function(s){
33338 if(String(s) != String(format)){
33347 if(!match && length == 1){
33352 filterPattern(box, length - 1);
33356 queue.push(pattern);
33358 box = box.slice(length, box.length);
33360 filterPattern(box, 4);
33366 Roo.each(boxes, function(box, k){
33372 if(box.length == 1){
33377 filterPattern(box, 4);
33381 this._processVerticalLayoutQueue( queue, isInstant );
33385 // _verticalAlternativeLayoutItems : function( items , isInstant )
33387 // if ( !items || !items.length ) {
33391 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
33395 _horizontalLayoutItems : function ( items , isInstant)
33397 if ( !items || !items.length || items.length < 3) {
33403 var eItems = items.slice(0, 3);
33405 items = items.slice(3, items.length);
33408 ['xs', 'xs', 'xs', 'wide'],
33409 ['xs', 'xs', 'wide'],
33410 ['xs', 'xs', 'sm'],
33411 ['xs', 'xs', 'xs'],
33417 ['sm', 'xs', 'xs'],
33421 ['wide', 'xs', 'xs', 'xs'],
33422 ['wide', 'xs', 'xs'],
33435 Roo.each(items, function(item, k){
33437 switch (item.size) {
33448 boxes.push([item]);
33472 var filterPattern = function(box, length)
33480 var pattern = box.slice(0, length);
33484 Roo.each(pattern, function(i){
33485 format.push(i.size);
33488 Roo.each(standard, function(s){
33490 if(String(s) != String(format)){
33499 if(!match && length == 1){
33504 filterPattern(box, length - 1);
33508 queue.push(pattern);
33510 box = box.slice(length, box.length);
33512 filterPattern(box, 4);
33518 Roo.each(boxes, function(box, k){
33524 if(box.length == 1){
33529 filterPattern(box, 4);
33536 var pos = this.el.getBox(true);
33540 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33542 var hit_end = false;
33544 Roo.each(queue, function(box){
33548 Roo.each(box, function(b){
33550 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33560 Roo.each(box, function(b){
33562 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33565 mx = Math.max(mx, b.x);
33569 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33573 Roo.each(box, function(b){
33575 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33589 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
33592 /** Sets position of item in DOM
33593 * @param {Element} item
33594 * @param {Number} x - horizontal position
33595 * @param {Number} y - vertical position
33596 * @param {Boolean} isInstant - disables transitions
33598 _processVerticalLayoutQueue : function( queue, isInstant )
33600 var pos = this.el.getBox(true);
33605 for (var i = 0; i < this.cols; i++){
33609 Roo.each(queue, function(box, k){
33611 var col = k % this.cols;
33613 Roo.each(box, function(b,kk){
33615 b.el.position('absolute');
33617 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33618 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33620 if(b.size == 'md-left' || b.size == 'md-right'){
33621 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33622 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33625 b.el.setWidth(width);
33626 b.el.setHeight(height);
33628 b.el.select('iframe',true).setSize(width,height);
33632 for (var i = 0; i < this.cols; i++){
33634 if(maxY[i] < maxY[col]){
33639 col = Math.min(col, i);
33643 x = pos.x + col * (this.colWidth + this.padWidth);
33647 var positions = [];
33649 switch (box.length){
33651 positions = this.getVerticalOneBoxColPositions(x, y, box);
33654 positions = this.getVerticalTwoBoxColPositions(x, y, box);
33657 positions = this.getVerticalThreeBoxColPositions(x, y, box);
33660 positions = this.getVerticalFourBoxColPositions(x, y, box);
33666 Roo.each(box, function(b,kk){
33668 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33670 var sz = b.el.getSize();
33672 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
33680 for (var i = 0; i < this.cols; i++){
33681 mY = Math.max(mY, maxY[i]);
33684 this.el.setHeight(mY - pos.y);
33688 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
33690 // var pos = this.el.getBox(true);
33693 // var maxX = pos.right;
33695 // var maxHeight = 0;
33697 // Roo.each(items, function(item, k){
33701 // item.el.position('absolute');
33703 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
33705 // item.el.setWidth(width);
33707 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
33709 // item.el.setHeight(height);
33712 // item.el.setXY([x, y], isInstant ? false : true);
33714 // item.el.setXY([maxX - width, y], isInstant ? false : true);
33717 // y = y + height + this.alternativePadWidth;
33719 // maxHeight = maxHeight + height + this.alternativePadWidth;
33723 // this.el.setHeight(maxHeight);
33727 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
33729 var pos = this.el.getBox(true);
33734 var maxX = pos.right;
33736 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
33738 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33740 Roo.each(queue, function(box, k){
33742 Roo.each(box, function(b, kk){
33744 b.el.position('absolute');
33746 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33747 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33749 if(b.size == 'md-left' || b.size == 'md-right'){
33750 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33751 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33754 b.el.setWidth(width);
33755 b.el.setHeight(height);
33763 var positions = [];
33765 switch (box.length){
33767 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
33770 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
33773 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
33776 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
33782 Roo.each(box, function(b,kk){
33784 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33786 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
33794 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
33796 Roo.each(eItems, function(b,k){
33798 b.size = (k == 0) ? 'sm' : 'xs';
33799 b.x = (k == 0) ? 2 : 1;
33800 b.y = (k == 0) ? 2 : 1;
33802 b.el.position('absolute');
33804 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33806 b.el.setWidth(width);
33808 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33810 b.el.setHeight(height);
33814 var positions = [];
33817 x : maxX - this.unitWidth * 2 - this.gutter,
33822 x : maxX - this.unitWidth,
33823 y : minY + (this.unitWidth + this.gutter) * 2
33827 x : maxX - this.unitWidth * 3 - this.gutter * 2,
33831 Roo.each(eItems, function(b,k){
33833 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
33839 getVerticalOneBoxColPositions : function(x, y, box)
33843 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
33845 if(box[0].size == 'md-left'){
33849 if(box[0].size == 'md-right'){
33854 x : x + (this.unitWidth + this.gutter) * rand,
33861 getVerticalTwoBoxColPositions : function(x, y, box)
33865 if(box[0].size == 'xs'){
33869 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
33873 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
33887 x : x + (this.unitWidth + this.gutter) * 2,
33888 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
33895 getVerticalThreeBoxColPositions : function(x, y, box)
33899 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
33907 x : x + (this.unitWidth + this.gutter) * 1,
33912 x : x + (this.unitWidth + this.gutter) * 2,
33920 if(box[0].size == 'xs' && box[1].size == 'xs'){
33929 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
33933 x : x + (this.unitWidth + this.gutter) * 1,
33947 x : x + (this.unitWidth + this.gutter) * 2,
33952 x : x + (this.unitWidth + this.gutter) * 2,
33953 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
33960 getVerticalFourBoxColPositions : function(x, y, box)
33964 if(box[0].size == 'xs'){
33973 y : y + (this.unitHeight + this.gutter) * 1
33978 y : y + (this.unitHeight + this.gutter) * 2
33982 x : x + (this.unitWidth + this.gutter) * 1,
33996 x : x + (this.unitWidth + this.gutter) * 2,
34001 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34002 y : y + (this.unitHeight + this.gutter) * 1
34006 x : x + (this.unitWidth + this.gutter) * 2,
34007 y : y + (this.unitWidth + this.gutter) * 2
34014 getHorizontalOneBoxColPositions : function(maxX, minY, box)
34018 if(box[0].size == 'md-left'){
34020 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34027 if(box[0].size == 'md-right'){
34029 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34030 y : minY + (this.unitWidth + this.gutter) * 1
34036 var rand = Math.floor(Math.random() * (4 - box[0].y));
34039 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34040 y : minY + (this.unitWidth + this.gutter) * rand
34047 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34051 if(box[0].size == 'xs'){
34054 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34059 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34060 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34068 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34073 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34074 y : minY + (this.unitWidth + this.gutter) * 2
34081 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34085 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34088 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34093 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34094 y : minY + (this.unitWidth + this.gutter) * 1
34098 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34099 y : minY + (this.unitWidth + this.gutter) * 2
34106 if(box[0].size == 'xs' && box[1].size == 'xs'){
34109 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34114 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34119 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34120 y : minY + (this.unitWidth + this.gutter) * 1
34128 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34133 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34134 y : minY + (this.unitWidth + this.gutter) * 2
34138 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34139 y : minY + (this.unitWidth + this.gutter) * 2
34146 getHorizontalFourBoxColPositions : function(maxX, minY, box)
34150 if(box[0].size == 'xs'){
34153 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34158 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34163 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),
34168 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34169 y : minY + (this.unitWidth + this.gutter) * 1
34177 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34182 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34183 y : minY + (this.unitWidth + this.gutter) * 2
34187 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34188 y : minY + (this.unitWidth + this.gutter) * 2
34192 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),
34193 y : minY + (this.unitWidth + this.gutter) * 2
34201 * remove a Masonry Brick
34202 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34204 removeBrick : function(brick_id)
34210 for (var i = 0; i<this.bricks.length; i++) {
34211 if (this.bricks[i].id == brick_id) {
34212 this.bricks.splice(i,1);
34213 this.el.dom.removeChild(Roo.get(brick_id).dom);
34220 * adds a Masonry Brick
34221 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34223 addBrick : function(cfg)
34225 var cn = new Roo.bootstrap.MasonryBrick(cfg);
34226 //this.register(cn);
34227 cn.parentId = this.id;
34228 cn.render(this.el);
34233 * register a Masonry Brick
34234 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34237 register : function(brick)
34239 this.bricks.push(brick);
34240 brick.masonryId = this.id;
34244 * clear all the Masonry Brick
34246 clearAll : function()
34249 //this.getChildContainer().dom.innerHTML = "";
34250 this.el.dom.innerHTML = '';
34253 getSelected : function()
34255 if (!this.selectedBrick) {
34259 return this.selectedBrick;
34263 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34267 * register a Masonry Layout
34268 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34271 register : function(layout)
34273 this.groups[layout.id] = layout;
34276 * fetch a Masonry Layout based on the masonry layout ID
34277 * @param {string} the masonry layout to add
34278 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34281 get: function(layout_id) {
34282 if (typeof(this.groups[layout_id]) == 'undefined') {
34285 return this.groups[layout_id] ;
34297 * http://masonry.desandro.com
34299 * The idea is to render all the bricks based on vertical width...
34301 * The original code extends 'outlayer' - we might need to use that....
34307 * @class Roo.bootstrap.LayoutMasonryAuto
34308 * @extends Roo.bootstrap.Component
34309 * Bootstrap Layout Masonry class
34312 * Create a new Element
34313 * @param {Object} config The config object
34316 Roo.bootstrap.LayoutMasonryAuto = function(config){
34317 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34320 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
34323 * @cfg {Boolean} isFitWidth - resize the width..
34325 isFitWidth : false, // options..
34327 * @cfg {Boolean} isOriginLeft = left align?
34329 isOriginLeft : true,
34331 * @cfg {Boolean} isOriginTop = top align?
34333 isOriginTop : false,
34335 * @cfg {Boolean} isLayoutInstant = no animation?
34337 isLayoutInstant : false, // needed?
34339 * @cfg {Boolean} isResizingContainer = not sure if this is used..
34341 isResizingContainer : true,
34343 * @cfg {Number} columnWidth width of the columns
34349 * @cfg {Number} maxCols maximum number of columns
34354 * @cfg {Number} padHeight padding below box..
34360 * @cfg {Boolean} isAutoInitial defalut true
34363 isAutoInitial : true,
34369 initialColumnWidth : 0,
34370 currentSize : null,
34372 colYs : null, // array.
34379 bricks: null, //CompositeElement
34380 cols : 0, // array?
34381 // element : null, // wrapped now this.el
34382 _isLayoutInited : null,
34385 getAutoCreate : function(){
34389 cls: 'blog-masonary-wrapper ' + this.cls,
34391 cls : 'mas-boxes masonary'
34398 getChildContainer: function( )
34400 if (this.boxesEl) {
34401 return this.boxesEl;
34404 this.boxesEl = this.el.select('.mas-boxes').first();
34406 return this.boxesEl;
34410 initEvents : function()
34414 if(this.isAutoInitial){
34415 Roo.log('hook children rendered');
34416 this.on('childrenrendered', function() {
34417 Roo.log('children rendered');
34424 initial : function()
34426 this.reloadItems();
34428 this.currentSize = this.el.getBox(true);
34430 /// was window resize... - let's see if this works..
34431 Roo.EventManager.onWindowResize(this.resize, this);
34433 if(!this.isAutoInitial){
34438 this.layout.defer(500,this);
34441 reloadItems: function()
34443 this.bricks = this.el.select('.masonry-brick', true);
34445 this.bricks.each(function(b) {
34446 //Roo.log(b.getSize());
34447 if (!b.attr('originalwidth')) {
34448 b.attr('originalwidth', b.getSize().width);
34453 Roo.log(this.bricks.elements.length);
34456 resize : function()
34459 var cs = this.el.getBox(true);
34461 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34462 Roo.log("no change in with or X");
34465 this.currentSize = cs;
34469 layout : function()
34472 this._resetLayout();
34473 //this._manageStamps();
34475 // don't animate first layout
34476 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34477 this.layoutItems( isInstant );
34479 // flag for initalized
34480 this._isLayoutInited = true;
34483 layoutItems : function( isInstant )
34485 //var items = this._getItemsForLayout( this.items );
34486 // original code supports filtering layout items.. we just ignore it..
34488 this._layoutItems( this.bricks , isInstant );
34490 this._postLayout();
34492 _layoutItems : function ( items , isInstant)
34494 //this.fireEvent( 'layout', this, items );
34497 if ( !items || !items.elements.length ) {
34498 // no items, emit event with empty array
34503 items.each(function(item) {
34504 Roo.log("layout item");
34506 // get x/y object from method
34507 var position = this._getItemLayoutPosition( item );
34509 position.item = item;
34510 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34511 queue.push( position );
34514 this._processLayoutQueue( queue );
34516 /** Sets position of item in DOM
34517 * @param {Element} item
34518 * @param {Number} x - horizontal position
34519 * @param {Number} y - vertical position
34520 * @param {Boolean} isInstant - disables transitions
34522 _processLayoutQueue : function( queue )
34524 for ( var i=0, len = queue.length; i < len; i++ ) {
34525 var obj = queue[i];
34526 obj.item.position('absolute');
34527 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34533 * Any logic you want to do after each layout,
34534 * i.e. size the container
34536 _postLayout : function()
34538 this.resizeContainer();
34541 resizeContainer : function()
34543 if ( !this.isResizingContainer ) {
34546 var size = this._getContainerSize();
34548 this.el.setSize(size.width,size.height);
34549 this.boxesEl.setSize(size.width,size.height);
34555 _resetLayout : function()
34557 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34558 this.colWidth = this.el.getWidth();
34559 //this.gutter = this.el.getWidth();
34561 this.measureColumns();
34567 this.colYs.push( 0 );
34573 measureColumns : function()
34575 this.getContainerWidth();
34576 // if columnWidth is 0, default to outerWidth of first item
34577 if ( !this.columnWidth ) {
34578 var firstItem = this.bricks.first();
34579 Roo.log(firstItem);
34580 this.columnWidth = this.containerWidth;
34581 if (firstItem && firstItem.attr('originalwidth') ) {
34582 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34584 // columnWidth fall back to item of first element
34585 Roo.log("set column width?");
34586 this.initialColumnWidth = this.columnWidth ;
34588 // if first elem has no width, default to size of container
34593 if (this.initialColumnWidth) {
34594 this.columnWidth = this.initialColumnWidth;
34599 // column width is fixed at the top - however if container width get's smaller we should
34602 // this bit calcs how man columns..
34604 var columnWidth = this.columnWidth += this.gutter;
34606 // calculate columns
34607 var containerWidth = this.containerWidth + this.gutter;
34609 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
34610 // fix rounding errors, typically with gutters
34611 var excess = columnWidth - containerWidth % columnWidth;
34614 // if overshoot is less than a pixel, round up, otherwise floor it
34615 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
34616 cols = Math[ mathMethod ]( cols );
34617 this.cols = Math.max( cols, 1 );
34618 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34620 // padding positioning..
34621 var totalColWidth = this.cols * this.columnWidth;
34622 var padavail = this.containerWidth - totalColWidth;
34623 // so for 2 columns - we need 3 'pads'
34625 var padNeeded = (1+this.cols) * this.padWidth;
34627 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
34629 this.columnWidth += padExtra
34630 //this.padWidth = Math.floor(padavail / ( this.cols));
34632 // adjust colum width so that padding is fixed??
34634 // we have 3 columns ... total = width * 3
34635 // we have X left over... that should be used by
34637 //if (this.expandC) {
34645 getContainerWidth : function()
34647 /* // container is parent if fit width
34648 var container = this.isFitWidth ? this.element.parentNode : this.element;
34649 // check that this.size and size are there
34650 // IE8 triggers resize on body size change, so they might not be
34652 var size = getSize( container ); //FIXME
34653 this.containerWidth = size && size.innerWidth; //FIXME
34656 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34660 _getItemLayoutPosition : function( item ) // what is item?
34662 // we resize the item to our columnWidth..
34664 item.setWidth(this.columnWidth);
34665 item.autoBoxAdjust = false;
34667 var sz = item.getSize();
34669 // how many columns does this brick span
34670 var remainder = this.containerWidth % this.columnWidth;
34672 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
34673 // round if off by 1 pixel, otherwise use ceil
34674 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
34675 colSpan = Math.min( colSpan, this.cols );
34677 // normally this should be '1' as we dont' currently allow multi width columns..
34679 var colGroup = this._getColGroup( colSpan );
34680 // get the minimum Y value from the columns
34681 var minimumY = Math.min.apply( Math, colGroup );
34682 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
34684 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
34686 // position the brick
34688 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
34689 y: this.currentSize.y + minimumY + this.padHeight
34693 // apply setHeight to necessary columns
34694 var setHeight = minimumY + sz.height + this.padHeight;
34695 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
34697 var setSpan = this.cols + 1 - colGroup.length;
34698 for ( var i = 0; i < setSpan; i++ ) {
34699 this.colYs[ shortColIndex + i ] = setHeight ;
34706 * @param {Number} colSpan - number of columns the element spans
34707 * @returns {Array} colGroup
34709 _getColGroup : function( colSpan )
34711 if ( colSpan < 2 ) {
34712 // if brick spans only one column, use all the column Ys
34717 // how many different places could this brick fit horizontally
34718 var groupCount = this.cols + 1 - colSpan;
34719 // for each group potential horizontal position
34720 for ( var i = 0; i < groupCount; i++ ) {
34721 // make an array of colY values for that one group
34722 var groupColYs = this.colYs.slice( i, i + colSpan );
34723 // and get the max value of the array
34724 colGroup[i] = Math.max.apply( Math, groupColYs );
34729 _manageStamp : function( stamp )
34731 var stampSize = stamp.getSize();
34732 var offset = stamp.getBox();
34733 // get the columns that this stamp affects
34734 var firstX = this.isOriginLeft ? offset.x : offset.right;
34735 var lastX = firstX + stampSize.width;
34736 var firstCol = Math.floor( firstX / this.columnWidth );
34737 firstCol = Math.max( 0, firstCol );
34739 var lastCol = Math.floor( lastX / this.columnWidth );
34740 // lastCol should not go over if multiple of columnWidth #425
34741 lastCol -= lastX % this.columnWidth ? 0 : 1;
34742 lastCol = Math.min( this.cols - 1, lastCol );
34744 // set colYs to bottom of the stamp
34745 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
34748 for ( var i = firstCol; i <= lastCol; i++ ) {
34749 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
34754 _getContainerSize : function()
34756 this.maxY = Math.max.apply( Math, this.colYs );
34761 if ( this.isFitWidth ) {
34762 size.width = this._getContainerFitWidth();
34768 _getContainerFitWidth : function()
34770 var unusedCols = 0;
34771 // count unused columns
34774 if ( this.colYs[i] !== 0 ) {
34779 // fit container to columns that have been used
34780 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
34783 needsResizeLayout : function()
34785 var previousWidth = this.containerWidth;
34786 this.getContainerWidth();
34787 return previousWidth !== this.containerWidth;
34802 * @class Roo.bootstrap.MasonryBrick
34803 * @extends Roo.bootstrap.Component
34804 * Bootstrap MasonryBrick class
34807 * Create a new MasonryBrick
34808 * @param {Object} config The config object
34811 Roo.bootstrap.MasonryBrick = function(config){
34813 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
34815 Roo.bootstrap.MasonryBrick.register(this);
34821 * When a MasonryBrick is clcik
34822 * @param {Roo.bootstrap.MasonryBrick} this
34823 * @param {Roo.EventObject} e
34829 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
34832 * @cfg {String} title
34836 * @cfg {String} html
34840 * @cfg {String} bgimage
34844 * @cfg {String} videourl
34848 * @cfg {String} cls
34852 * @cfg {String} href
34856 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
34861 * @cfg {String} placetitle (center|bottom)
34866 * @cfg {Boolean} isFitContainer defalut true
34868 isFitContainer : true,
34871 * @cfg {Boolean} preventDefault defalut false
34873 preventDefault : false,
34876 * @cfg {Boolean} inverse defalut false
34878 maskInverse : false,
34880 getAutoCreate : function()
34882 if(!this.isFitContainer){
34883 return this.getSplitAutoCreate();
34886 var cls = 'masonry-brick masonry-brick-full';
34888 if(this.href.length){
34889 cls += ' masonry-brick-link';
34892 if(this.bgimage.length){
34893 cls += ' masonry-brick-image';
34896 if(this.maskInverse){
34897 cls += ' mask-inverse';
34900 if(!this.html.length && !this.maskInverse && !this.videourl.length){
34901 cls += ' enable-mask';
34905 cls += ' masonry-' + this.size + '-brick';
34908 if(this.placetitle.length){
34910 switch (this.placetitle) {
34912 cls += ' masonry-center-title';
34915 cls += ' masonry-bottom-title';
34922 if(!this.html.length && !this.bgimage.length){
34923 cls += ' masonry-center-title';
34926 if(!this.html.length && this.bgimage.length){
34927 cls += ' masonry-bottom-title';
34932 cls += ' ' + this.cls;
34936 tag: (this.href.length) ? 'a' : 'div',
34941 cls: 'masonry-brick-mask'
34945 cls: 'masonry-brick-paragraph',
34951 if(this.href.length){
34952 cfg.href = this.href;
34955 var cn = cfg.cn[1].cn;
34957 if(this.title.length){
34960 cls: 'masonry-brick-title',
34965 if(this.html.length){
34968 cls: 'masonry-brick-text',
34973 if (!this.title.length && !this.html.length) {
34974 cfg.cn[1].cls += ' hide';
34977 if(this.bgimage.length){
34980 cls: 'masonry-brick-image-view',
34985 if(this.videourl.length){
34986 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
34987 // youtube support only?
34990 cls: 'masonry-brick-image-view',
34993 allowfullscreen : true
35001 getSplitAutoCreate : function()
35003 var cls = 'masonry-brick masonry-brick-split';
35005 if(this.href.length){
35006 cls += ' masonry-brick-link';
35009 if(this.bgimage.length){
35010 cls += ' masonry-brick-image';
35014 cls += ' masonry-' + this.size + '-brick';
35017 switch (this.placetitle) {
35019 cls += ' masonry-center-title';
35022 cls += ' masonry-bottom-title';
35025 if(!this.bgimage.length){
35026 cls += ' masonry-center-title';
35029 if(this.bgimage.length){
35030 cls += ' masonry-bottom-title';
35036 cls += ' ' + this.cls;
35040 tag: (this.href.length) ? 'a' : 'div',
35045 cls: 'masonry-brick-split-head',
35049 cls: 'masonry-brick-paragraph',
35056 cls: 'masonry-brick-split-body',
35062 if(this.href.length){
35063 cfg.href = this.href;
35066 if(this.title.length){
35067 cfg.cn[0].cn[0].cn.push({
35069 cls: 'masonry-brick-title',
35074 if(this.html.length){
35075 cfg.cn[1].cn.push({
35077 cls: 'masonry-brick-text',
35082 if(this.bgimage.length){
35083 cfg.cn[0].cn.push({
35085 cls: 'masonry-brick-image-view',
35090 if(this.videourl.length){
35091 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35092 // youtube support only?
35093 cfg.cn[0].cn.cn.push({
35095 cls: 'masonry-brick-image-view',
35098 allowfullscreen : true
35105 initEvents: function()
35107 switch (this.size) {
35140 this.el.on('touchstart', this.onTouchStart, this);
35141 this.el.on('touchmove', this.onTouchMove, this);
35142 this.el.on('touchend', this.onTouchEnd, this);
35143 this.el.on('contextmenu', this.onContextMenu, this);
35145 this.el.on('mouseenter' ,this.enter, this);
35146 this.el.on('mouseleave', this.leave, this);
35147 this.el.on('click', this.onClick, this);
35150 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35151 this.parent().bricks.push(this);
35156 onClick: function(e, el)
35158 var time = this.endTimer - this.startTimer;
35159 // Roo.log(e.preventDefault());
35162 e.preventDefault();
35167 if(!this.preventDefault){
35171 e.preventDefault();
35173 if (this.activeClass != '') {
35174 this.selectBrick();
35177 this.fireEvent('click', this, e);
35180 enter: function(e, el)
35182 e.preventDefault();
35184 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35188 if(this.bgimage.length && this.html.length){
35189 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35193 leave: function(e, el)
35195 e.preventDefault();
35197 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35201 if(this.bgimage.length && this.html.length){
35202 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35206 onTouchStart: function(e, el)
35208 // e.preventDefault();
35210 this.touchmoved = false;
35212 if(!this.isFitContainer){
35216 if(!this.bgimage.length || !this.html.length){
35220 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35222 this.timer = new Date().getTime();
35226 onTouchMove: function(e, el)
35228 this.touchmoved = true;
35231 onContextMenu : function(e,el)
35233 e.preventDefault();
35234 e.stopPropagation();
35238 onTouchEnd: function(e, el)
35240 // e.preventDefault();
35242 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35249 if(!this.bgimage.length || !this.html.length){
35251 if(this.href.length){
35252 window.location.href = this.href;
35258 if(!this.isFitContainer){
35262 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35264 window.location.href = this.href;
35267 //selection on single brick only
35268 selectBrick : function() {
35270 if (!this.parentId) {
35274 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35275 var index = m.selectedBrick.indexOf(this.id);
35278 m.selectedBrick.splice(index,1);
35279 this.el.removeClass(this.activeClass);
35283 for(var i = 0; i < m.selectedBrick.length; i++) {
35284 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35285 b.el.removeClass(b.activeClass);
35288 m.selectedBrick = [];
35290 m.selectedBrick.push(this.id);
35291 this.el.addClass(this.activeClass);
35295 isSelected : function(){
35296 return this.el.hasClass(this.activeClass);
35301 Roo.apply(Roo.bootstrap.MasonryBrick, {
35304 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35306 * register a Masonry Brick
35307 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35310 register : function(brick)
35312 //this.groups[brick.id] = brick;
35313 this.groups.add(brick.id, brick);
35316 * fetch a masonry brick based on the masonry brick ID
35317 * @param {string} the masonry brick to add
35318 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35321 get: function(brick_id)
35323 // if (typeof(this.groups[brick_id]) == 'undefined') {
35326 // return this.groups[brick_id] ;
35328 if(this.groups.key(brick_id)) {
35329 return this.groups.key(brick_id);
35347 * @class Roo.bootstrap.Brick
35348 * @extends Roo.bootstrap.Component
35349 * Bootstrap Brick class
35352 * Create a new Brick
35353 * @param {Object} config The config object
35356 Roo.bootstrap.Brick = function(config){
35357 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35363 * When a Brick is click
35364 * @param {Roo.bootstrap.Brick} this
35365 * @param {Roo.EventObject} e
35371 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
35374 * @cfg {String} title
35378 * @cfg {String} html
35382 * @cfg {String} bgimage
35386 * @cfg {String} cls
35390 * @cfg {String} href
35394 * @cfg {String} video
35398 * @cfg {Boolean} square
35402 getAutoCreate : function()
35404 var cls = 'roo-brick';
35406 if(this.href.length){
35407 cls += ' roo-brick-link';
35410 if(this.bgimage.length){
35411 cls += ' roo-brick-image';
35414 if(!this.html.length && !this.bgimage.length){
35415 cls += ' roo-brick-center-title';
35418 if(!this.html.length && this.bgimage.length){
35419 cls += ' roo-brick-bottom-title';
35423 cls += ' ' + this.cls;
35427 tag: (this.href.length) ? 'a' : 'div',
35432 cls: 'roo-brick-paragraph',
35438 if(this.href.length){
35439 cfg.href = this.href;
35442 var cn = cfg.cn[0].cn;
35444 if(this.title.length){
35447 cls: 'roo-brick-title',
35452 if(this.html.length){
35455 cls: 'roo-brick-text',
35462 if(this.bgimage.length){
35465 cls: 'roo-brick-image-view',
35473 initEvents: function()
35475 if(this.title.length || this.html.length){
35476 this.el.on('mouseenter' ,this.enter, this);
35477 this.el.on('mouseleave', this.leave, this);
35480 Roo.EventManager.onWindowResize(this.resize, this);
35482 if(this.bgimage.length){
35483 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35484 this.imageEl.on('load', this.onImageLoad, this);
35491 onImageLoad : function()
35496 resize : function()
35498 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35500 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35502 if(this.bgimage.length){
35503 var image = this.el.select('.roo-brick-image-view', true).first();
35505 image.setWidth(paragraph.getWidth());
35508 image.setHeight(paragraph.getWidth());
35511 this.el.setHeight(image.getHeight());
35512 paragraph.setHeight(image.getHeight());
35518 enter: function(e, el)
35520 e.preventDefault();
35522 if(this.bgimage.length){
35523 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35524 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35528 leave: function(e, el)
35530 e.preventDefault();
35532 if(this.bgimage.length){
35533 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35534 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35549 * @class Roo.bootstrap.NumberField
35550 * @extends Roo.bootstrap.Input
35551 * Bootstrap NumberField class
35557 * Create a new NumberField
35558 * @param {Object} config The config object
35561 Roo.bootstrap.NumberField = function(config){
35562 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35565 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35568 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35570 allowDecimals : true,
35572 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35574 decimalSeparator : ".",
35576 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35578 decimalPrecision : 2,
35580 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35582 allowNegative : true,
35585 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35589 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
35591 minValue : Number.NEGATIVE_INFINITY,
35593 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
35595 maxValue : Number.MAX_VALUE,
35597 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
35599 minText : "The minimum value for this field is {0}",
35601 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
35603 maxText : "The maximum value for this field is {0}",
35605 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
35606 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
35608 nanText : "{0} is not a valid number",
35610 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
35612 thousandsDelimiter : false,
35614 * @cfg {String} valueAlign alignment of value
35616 valueAlign : "left",
35618 getAutoCreate : function()
35620 var hiddenInput = {
35624 cls: 'hidden-number-input'
35628 hiddenInput.name = this.name;
35633 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
35635 this.name = hiddenInput.name;
35637 if(cfg.cn.length > 0) {
35638 cfg.cn.push(hiddenInput);
35645 initEvents : function()
35647 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
35649 var allowed = "0123456789";
35651 if(this.allowDecimals){
35652 allowed += this.decimalSeparator;
35655 if(this.allowNegative){
35659 if(this.thousandsDelimiter) {
35663 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
35665 var keyPress = function(e){
35667 var k = e.getKey();
35669 var c = e.getCharCode();
35672 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
35673 allowed.indexOf(String.fromCharCode(c)) === -1
35679 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
35683 if(allowed.indexOf(String.fromCharCode(c)) === -1){
35688 this.el.on("keypress", keyPress, this);
35691 validateValue : function(value)
35694 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
35698 var num = this.parseValue(value);
35701 this.markInvalid(String.format(this.nanText, value));
35705 if(num < this.minValue){
35706 this.markInvalid(String.format(this.minText, this.minValue));
35710 if(num > this.maxValue){
35711 this.markInvalid(String.format(this.maxText, this.maxValue));
35718 getValue : function()
35720 var v = this.hiddenEl().getValue();
35722 return this.fixPrecision(this.parseValue(v));
35725 parseValue : function(value)
35727 if(this.thousandsDelimiter) {
35729 r = new RegExp(",", "g");
35730 value = value.replace(r, "");
35733 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
35734 return isNaN(value) ? '' : value;
35737 fixPrecision : function(value)
35739 if(this.thousandsDelimiter) {
35741 r = new RegExp(",", "g");
35742 value = value.replace(r, "");
35745 var nan = isNaN(value);
35747 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
35748 return nan ? '' : value;
35750 return parseFloat(value).toFixed(this.decimalPrecision);
35753 setValue : function(v)
35755 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
35761 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
35763 this.inputEl().dom.value = (v == '') ? '' :
35764 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
35766 if(!this.allowZero && v === '0') {
35767 this.hiddenEl().dom.value = '';
35768 this.inputEl().dom.value = '';
35775 decimalPrecisionFcn : function(v)
35777 return Math.floor(v);
35780 beforeBlur : function()
35782 var v = this.parseValue(this.getRawValue());
35784 if(v || v === 0 || v === ''){
35789 hiddenEl : function()
35791 return this.el.select('input.hidden-number-input',true).first();
35803 * @class Roo.bootstrap.DocumentSlider
35804 * @extends Roo.bootstrap.Component
35805 * Bootstrap DocumentSlider class
35808 * Create a new DocumentViewer
35809 * @param {Object} config The config object
35812 Roo.bootstrap.DocumentSlider = function(config){
35813 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
35820 * Fire after initEvent
35821 * @param {Roo.bootstrap.DocumentSlider} this
35826 * Fire after update
35827 * @param {Roo.bootstrap.DocumentSlider} this
35833 * @param {Roo.bootstrap.DocumentSlider} this
35839 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
35845 getAutoCreate : function()
35849 cls : 'roo-document-slider',
35853 cls : 'roo-document-slider-header',
35857 cls : 'roo-document-slider-header-title'
35863 cls : 'roo-document-slider-body',
35867 cls : 'roo-document-slider-prev',
35871 cls : 'fa fa-chevron-left'
35877 cls : 'roo-document-slider-thumb',
35881 cls : 'roo-document-slider-image'
35887 cls : 'roo-document-slider-next',
35891 cls : 'fa fa-chevron-right'
35903 initEvents : function()
35905 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
35906 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
35908 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
35909 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
35911 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
35912 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
35914 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
35915 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
35917 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
35918 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
35920 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
35921 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35923 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
35924 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35926 this.thumbEl.on('click', this.onClick, this);
35928 this.prevIndicator.on('click', this.prev, this);
35930 this.nextIndicator.on('click', this.next, this);
35934 initial : function()
35936 if(this.files.length){
35937 this.indicator = 1;
35941 this.fireEvent('initial', this);
35944 update : function()
35946 this.imageEl.attr('src', this.files[this.indicator - 1]);
35948 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
35950 this.prevIndicator.show();
35952 if(this.indicator == 1){
35953 this.prevIndicator.hide();
35956 this.nextIndicator.show();
35958 if(this.indicator == this.files.length){
35959 this.nextIndicator.hide();
35962 this.thumbEl.scrollTo('top');
35964 this.fireEvent('update', this);
35967 onClick : function(e)
35969 e.preventDefault();
35971 this.fireEvent('click', this);
35976 e.preventDefault();
35978 this.indicator = Math.max(1, this.indicator - 1);
35985 e.preventDefault();
35987 this.indicator = Math.min(this.files.length, this.indicator + 1);
36001 * @class Roo.bootstrap.RadioSet
36002 * @extends Roo.bootstrap.Input
36003 * Bootstrap RadioSet class
36004 * @cfg {String} indicatorpos (left|right) default left
36005 * @cfg {Boolean} inline (true|false) inline the element (default true)
36006 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36008 * Create a new RadioSet
36009 * @param {Object} config The config object
36012 Roo.bootstrap.RadioSet = function(config){
36014 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36018 Roo.bootstrap.RadioSet.register(this);
36023 * Fires when the element is checked or unchecked.
36024 * @param {Roo.bootstrap.RadioSet} this This radio
36025 * @param {Roo.bootstrap.Radio} item The checked item
36030 * Fires when the element is click.
36031 * @param {Roo.bootstrap.RadioSet} this This radio set
36032 * @param {Roo.bootstrap.Radio} item The checked item
36033 * @param {Roo.EventObject} e The event object
36040 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
36048 indicatorpos : 'left',
36050 getAutoCreate : function()
36054 cls : 'roo-radio-set-label',
36058 html : this.fieldLabel
36062 if (Roo.bootstrap.version == 3) {
36065 if(this.indicatorpos == 'left'){
36068 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36069 tooltip : 'This field is required'
36074 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36075 tooltip : 'This field is required'
36081 cls : 'roo-radio-set-items'
36084 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36086 if (align === 'left' && this.fieldLabel.length) {
36089 cls : "roo-radio-set-right",
36095 if(this.labelWidth > 12){
36096 label.style = "width: " + this.labelWidth + 'px';
36099 if(this.labelWidth < 13 && this.labelmd == 0){
36100 this.labelmd = this.labelWidth;
36103 if(this.labellg > 0){
36104 label.cls += ' col-lg-' + this.labellg;
36105 items.cls += ' col-lg-' + (12 - this.labellg);
36108 if(this.labelmd > 0){
36109 label.cls += ' col-md-' + this.labelmd;
36110 items.cls += ' col-md-' + (12 - this.labelmd);
36113 if(this.labelsm > 0){
36114 label.cls += ' col-sm-' + this.labelsm;
36115 items.cls += ' col-sm-' + (12 - this.labelsm);
36118 if(this.labelxs > 0){
36119 label.cls += ' col-xs-' + this.labelxs;
36120 items.cls += ' col-xs-' + (12 - this.labelxs);
36126 cls : 'roo-radio-set',
36130 cls : 'roo-radio-set-input',
36133 value : this.value ? this.value : ''
36140 if(this.weight.length){
36141 cfg.cls += ' roo-radio-' + this.weight;
36145 cfg.cls += ' roo-radio-set-inline';
36149 ['xs','sm','md','lg'].map(function(size){
36150 if (settings[size]) {
36151 cfg.cls += ' col-' + size + '-' + settings[size];
36159 initEvents : function()
36161 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36162 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36164 if(!this.fieldLabel.length){
36165 this.labelEl.hide();
36168 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36169 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36171 this.indicator = this.indicatorEl();
36173 if(this.indicator){
36174 this.indicator.addClass('invisible');
36177 this.originalValue = this.getValue();
36181 inputEl: function ()
36183 return this.el.select('.roo-radio-set-input', true).first();
36186 getChildContainer : function()
36188 return this.itemsEl;
36191 register : function(item)
36193 this.radioes.push(item);
36197 validate : function()
36199 if(this.getVisibilityEl().hasClass('hidden')){
36205 Roo.each(this.radioes, function(i){
36214 if(this.allowBlank) {
36218 if(this.disabled || valid){
36223 this.markInvalid();
36228 markValid : function()
36230 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36231 this.indicatorEl().removeClass('visible');
36232 this.indicatorEl().addClass('invisible');
36236 if (Roo.bootstrap.version == 3) {
36237 this.el.removeClass([this.invalidClass, this.validClass]);
36238 this.el.addClass(this.validClass);
36240 this.el.removeClass(['is-invalid','is-valid']);
36241 this.el.addClass(['is-valid']);
36243 this.fireEvent('valid', this);
36246 markInvalid : function(msg)
36248 if(this.allowBlank || this.disabled){
36252 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36253 this.indicatorEl().removeClass('invisible');
36254 this.indicatorEl().addClass('visible');
36256 if (Roo.bootstrap.version == 3) {
36257 this.el.removeClass([this.invalidClass, this.validClass]);
36258 this.el.addClass(this.invalidClass);
36260 this.el.removeClass(['is-invalid','is-valid']);
36261 this.el.addClass(['is-invalid']);
36264 this.fireEvent('invalid', this, msg);
36268 setValue : function(v, suppressEvent)
36270 if(this.value === v){
36277 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36280 Roo.each(this.radioes, function(i){
36282 i.el.removeClass('checked');
36285 Roo.each(this.radioes, function(i){
36287 if(i.value === v || i.value.toString() === v.toString()){
36289 i.el.addClass('checked');
36291 if(suppressEvent !== true){
36292 this.fireEvent('check', this, i);
36303 clearInvalid : function(){
36305 if(!this.el || this.preventMark){
36309 this.el.removeClass([this.invalidClass]);
36311 this.fireEvent('valid', this);
36316 Roo.apply(Roo.bootstrap.RadioSet, {
36320 register : function(set)
36322 this.groups[set.name] = set;
36325 get: function(name)
36327 if (typeof(this.groups[name]) == 'undefined') {
36331 return this.groups[name] ;
36337 * Ext JS Library 1.1.1
36338 * Copyright(c) 2006-2007, Ext JS, LLC.
36340 * Originally Released Under LGPL - original licence link has changed is not relivant.
36343 * <script type="text/javascript">
36348 * @class Roo.bootstrap.SplitBar
36349 * @extends Roo.util.Observable
36350 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36354 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36355 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36356 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36357 split.minSize = 100;
36358 split.maxSize = 600;
36359 split.animate = true;
36360 split.on('moved', splitterMoved);
36363 * Create a new SplitBar
36364 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
36365 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
36366 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36367 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
36368 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36369 position of the SplitBar).
36371 Roo.bootstrap.SplitBar = function(cfg){
36376 // dragElement : elm
36377 // resizingElement: el,
36379 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36380 // placement : Roo.bootstrap.SplitBar.LEFT ,
36381 // existingProxy ???
36384 this.el = Roo.get(cfg.dragElement, true);
36385 this.el.dom.unselectable = "on";
36387 this.resizingEl = Roo.get(cfg.resizingElement, true);
36391 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36392 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36395 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36398 * The minimum size of the resizing element. (Defaults to 0)
36404 * The maximum size of the resizing element. (Defaults to 2000)
36407 this.maxSize = 2000;
36410 * Whether to animate the transition to the new size
36413 this.animate = false;
36416 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36419 this.useShim = false;
36424 if(!cfg.existingProxy){
36426 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36428 this.proxy = Roo.get(cfg.existingProxy).dom;
36431 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36434 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36437 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36440 this.dragSpecs = {};
36443 * @private The adapter to use to positon and resize elements
36445 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36446 this.adapter.init(this);
36448 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36450 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36451 this.el.addClass("roo-splitbar-h");
36454 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36455 this.el.addClass("roo-splitbar-v");
36461 * Fires when the splitter is moved (alias for {@link #event-moved})
36462 * @param {Roo.bootstrap.SplitBar} this
36463 * @param {Number} newSize the new width or height
36468 * Fires when the splitter is moved
36469 * @param {Roo.bootstrap.SplitBar} this
36470 * @param {Number} newSize the new width or height
36474 * @event beforeresize
36475 * Fires before the splitter is dragged
36476 * @param {Roo.bootstrap.SplitBar} this
36478 "beforeresize" : true,
36480 "beforeapply" : true
36483 Roo.util.Observable.call(this);
36486 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36487 onStartProxyDrag : function(x, y){
36488 this.fireEvent("beforeresize", this);
36490 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
36492 o.enableDisplayMode("block");
36493 // all splitbars share the same overlay
36494 Roo.bootstrap.SplitBar.prototype.overlay = o;
36496 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36497 this.overlay.show();
36498 Roo.get(this.proxy).setDisplayed("block");
36499 var size = this.adapter.getElementSize(this);
36500 this.activeMinSize = this.getMinimumSize();;
36501 this.activeMaxSize = this.getMaximumSize();;
36502 var c1 = size - this.activeMinSize;
36503 var c2 = Math.max(this.activeMaxSize - size, 0);
36504 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36505 this.dd.resetConstraints();
36506 this.dd.setXConstraint(
36507 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
36508 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36510 this.dd.setYConstraint(0, 0);
36512 this.dd.resetConstraints();
36513 this.dd.setXConstraint(0, 0);
36514 this.dd.setYConstraint(
36515 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
36516 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36519 this.dragSpecs.startSize = size;
36520 this.dragSpecs.startPoint = [x, y];
36521 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36525 * @private Called after the drag operation by the DDProxy
36527 onEndProxyDrag : function(e){
36528 Roo.get(this.proxy).setDisplayed(false);
36529 var endPoint = Roo.lib.Event.getXY(e);
36531 this.overlay.hide();
36534 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36535 newSize = this.dragSpecs.startSize +
36536 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36537 endPoint[0] - this.dragSpecs.startPoint[0] :
36538 this.dragSpecs.startPoint[0] - endPoint[0]
36541 newSize = this.dragSpecs.startSize +
36542 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36543 endPoint[1] - this.dragSpecs.startPoint[1] :
36544 this.dragSpecs.startPoint[1] - endPoint[1]
36547 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36548 if(newSize != this.dragSpecs.startSize){
36549 if(this.fireEvent('beforeapply', this, newSize) !== false){
36550 this.adapter.setElementSize(this, newSize);
36551 this.fireEvent("moved", this, newSize);
36552 this.fireEvent("resize", this, newSize);
36558 * Get the adapter this SplitBar uses
36559 * @return The adapter object
36561 getAdapter : function(){
36562 return this.adapter;
36566 * Set the adapter this SplitBar uses
36567 * @param {Object} adapter A SplitBar adapter object
36569 setAdapter : function(adapter){
36570 this.adapter = adapter;
36571 this.adapter.init(this);
36575 * Gets the minimum size for the resizing element
36576 * @return {Number} The minimum size
36578 getMinimumSize : function(){
36579 return this.minSize;
36583 * Sets the minimum size for the resizing element
36584 * @param {Number} minSize The minimum size
36586 setMinimumSize : function(minSize){
36587 this.minSize = minSize;
36591 * Gets the maximum size for the resizing element
36592 * @return {Number} The maximum size
36594 getMaximumSize : function(){
36595 return this.maxSize;
36599 * Sets the maximum size for the resizing element
36600 * @param {Number} maxSize The maximum size
36602 setMaximumSize : function(maxSize){
36603 this.maxSize = maxSize;
36607 * Sets the initialize size for the resizing element
36608 * @param {Number} size The initial size
36610 setCurrentSize : function(size){
36611 var oldAnimate = this.animate;
36612 this.animate = false;
36613 this.adapter.setElementSize(this, size);
36614 this.animate = oldAnimate;
36618 * Destroy this splitbar.
36619 * @param {Boolean} removeEl True to remove the element
36621 destroy : function(removeEl){
36623 this.shim.remove();
36626 this.proxy.parentNode.removeChild(this.proxy);
36634 * @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.
36636 Roo.bootstrap.SplitBar.createProxy = function(dir){
36637 var proxy = new Roo.Element(document.createElement("div"));
36638 proxy.unselectable();
36639 var cls = 'roo-splitbar-proxy';
36640 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
36641 document.body.appendChild(proxy.dom);
36646 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
36647 * Default Adapter. It assumes the splitter and resizing element are not positioned
36648 * elements and only gets/sets the width of the element. Generally used for table based layouts.
36650 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
36653 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
36654 // do nothing for now
36655 init : function(s){
36659 * Called before drag operations to get the current size of the resizing element.
36660 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36662 getElementSize : function(s){
36663 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36664 return s.resizingEl.getWidth();
36666 return s.resizingEl.getHeight();
36671 * Called after drag operations to set the size of the resizing element.
36672 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36673 * @param {Number} newSize The new size to set
36674 * @param {Function} onComplete A function to be invoked when resizing is complete
36676 setElementSize : function(s, newSize, onComplete){
36677 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36679 s.resizingEl.setWidth(newSize);
36681 onComplete(s, newSize);
36684 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
36689 s.resizingEl.setHeight(newSize);
36691 onComplete(s, newSize);
36694 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
36701 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
36702 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
36703 * Adapter that moves the splitter element to align with the resized sizing element.
36704 * Used with an absolute positioned SplitBar.
36705 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
36706 * document.body, make sure you assign an id to the body element.
36708 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
36709 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36710 this.container = Roo.get(container);
36713 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
36714 init : function(s){
36715 this.basic.init(s);
36718 getElementSize : function(s){
36719 return this.basic.getElementSize(s);
36722 setElementSize : function(s, newSize, onComplete){
36723 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
36726 moveSplitter : function(s){
36727 var yes = Roo.bootstrap.SplitBar;
36728 switch(s.placement){
36730 s.el.setX(s.resizingEl.getRight());
36733 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
36736 s.el.setY(s.resizingEl.getBottom());
36739 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
36746 * Orientation constant - Create a vertical SplitBar
36750 Roo.bootstrap.SplitBar.VERTICAL = 1;
36753 * Orientation constant - Create a horizontal SplitBar
36757 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
36760 * Placement constant - The resizing element is to the left of the splitter element
36764 Roo.bootstrap.SplitBar.LEFT = 1;
36767 * Placement constant - The resizing element is to the right of the splitter element
36771 Roo.bootstrap.SplitBar.RIGHT = 2;
36774 * Placement constant - The resizing element is positioned above the splitter element
36778 Roo.bootstrap.SplitBar.TOP = 3;
36781 * Placement constant - The resizing element is positioned under splitter element
36785 Roo.bootstrap.SplitBar.BOTTOM = 4;
36786 Roo.namespace("Roo.bootstrap.layout");/*
36788 * Ext JS Library 1.1.1
36789 * Copyright(c) 2006-2007, Ext JS, LLC.
36791 * Originally Released Under LGPL - original licence link has changed is not relivant.
36794 * <script type="text/javascript">
36798 * @class Roo.bootstrap.layout.Manager
36799 * @extends Roo.bootstrap.Component
36800 * Base class for layout managers.
36802 Roo.bootstrap.layout.Manager = function(config)
36804 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
36810 /** false to disable window resize monitoring @type Boolean */
36811 this.monitorWindowResize = true;
36816 * Fires when a layout is performed.
36817 * @param {Roo.LayoutManager} this
36821 * @event regionresized
36822 * Fires when the user resizes a region.
36823 * @param {Roo.LayoutRegion} region The resized region
36824 * @param {Number} newSize The new size (width for east/west, height for north/south)
36826 "regionresized" : true,
36828 * @event regioncollapsed
36829 * Fires when a region is collapsed.
36830 * @param {Roo.LayoutRegion} region The collapsed region
36832 "regioncollapsed" : true,
36834 * @event regionexpanded
36835 * Fires when a region is expanded.
36836 * @param {Roo.LayoutRegion} region The expanded region
36838 "regionexpanded" : true
36840 this.updating = false;
36843 this.el = Roo.get(config.el);
36849 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
36854 monitorWindowResize : true,
36860 onRender : function(ct, position)
36863 this.el = Roo.get(ct);
36866 //this.fireEvent('render',this);
36870 initEvents: function()
36874 // ie scrollbar fix
36875 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
36876 document.body.scroll = "no";
36877 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
36878 this.el.position('relative');
36880 this.id = this.el.id;
36881 this.el.addClass("roo-layout-container");
36882 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
36883 if(this.el.dom != document.body ) {
36884 this.el.on('resize', this.layout,this);
36885 this.el.on('show', this.layout,this);
36891 * Returns true if this layout is currently being updated
36892 * @return {Boolean}
36894 isUpdating : function(){
36895 return this.updating;
36899 * Suspend the LayoutManager from doing auto-layouts while
36900 * making multiple add or remove calls
36902 beginUpdate : function(){
36903 this.updating = true;
36907 * Restore auto-layouts and optionally disable the manager from performing a layout
36908 * @param {Boolean} noLayout true to disable a layout update
36910 endUpdate : function(noLayout){
36911 this.updating = false;
36917 layout: function(){
36921 onRegionResized : function(region, newSize){
36922 this.fireEvent("regionresized", region, newSize);
36926 onRegionCollapsed : function(region){
36927 this.fireEvent("regioncollapsed", region);
36930 onRegionExpanded : function(region){
36931 this.fireEvent("regionexpanded", region);
36935 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
36936 * performs box-model adjustments.
36937 * @return {Object} The size as an object {width: (the width), height: (the height)}
36939 getViewSize : function()
36942 if(this.el.dom != document.body){
36943 size = this.el.getSize();
36945 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
36947 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
36948 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
36953 * Returns the Element this layout is bound to.
36954 * @return {Roo.Element}
36956 getEl : function(){
36961 * Returns the specified region.
36962 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
36963 * @return {Roo.LayoutRegion}
36965 getRegion : function(target){
36966 return this.regions[target.toLowerCase()];
36969 onWindowResize : function(){
36970 if(this.monitorWindowResize){
36977 * Ext JS Library 1.1.1
36978 * Copyright(c) 2006-2007, Ext JS, LLC.
36980 * Originally Released Under LGPL - original licence link has changed is not relivant.
36983 * <script type="text/javascript">
36986 * @class Roo.bootstrap.layout.Border
36987 * @extends Roo.bootstrap.layout.Manager
36988 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
36989 * please see: examples/bootstrap/nested.html<br><br>
36991 <b>The container the layout is rendered into can be either the body element or any other element.
36992 If it is not the body element, the container needs to either be an absolute positioned element,
36993 or you will need to add "position:relative" to the css of the container. You will also need to specify
36994 the container size if it is not the body element.</b>
36997 * Create a new Border
36998 * @param {Object} config Configuration options
37000 Roo.bootstrap.layout.Border = function(config){
37001 config = config || {};
37002 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37006 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37007 if(config[region]){
37008 config[region].region = region;
37009 this.addRegion(config[region]);
37015 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
37017 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37019 parent : false, // this might point to a 'nest' or a ???
37022 * Creates and adds a new region if it doesn't already exist.
37023 * @param {String} target The target region key (north, south, east, west or center).
37024 * @param {Object} config The regions config object
37025 * @return {BorderLayoutRegion} The new region
37027 addRegion : function(config)
37029 if(!this.regions[config.region]){
37030 var r = this.factory(config);
37031 this.bindRegion(r);
37033 return this.regions[config.region];
37037 bindRegion : function(r){
37038 this.regions[r.config.region] = r;
37040 r.on("visibilitychange", this.layout, this);
37041 r.on("paneladded", this.layout, this);
37042 r.on("panelremoved", this.layout, this);
37043 r.on("invalidated", this.layout, this);
37044 r.on("resized", this.onRegionResized, this);
37045 r.on("collapsed", this.onRegionCollapsed, this);
37046 r.on("expanded", this.onRegionExpanded, this);
37050 * Performs a layout update.
37052 layout : function()
37054 if(this.updating) {
37058 // render all the rebions if they have not been done alreayd?
37059 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37060 if(this.regions[region] && !this.regions[region].bodyEl){
37061 this.regions[region].onRender(this.el)
37065 var size = this.getViewSize();
37066 var w = size.width;
37067 var h = size.height;
37072 //var x = 0, y = 0;
37074 var rs = this.regions;
37075 var north = rs["north"];
37076 var south = rs["south"];
37077 var west = rs["west"];
37078 var east = rs["east"];
37079 var center = rs["center"];
37080 //if(this.hideOnLayout){ // not supported anymore
37081 //c.el.setStyle("display", "none");
37083 if(north && north.isVisible()){
37084 var b = north.getBox();
37085 var m = north.getMargins();
37086 b.width = w - (m.left+m.right);
37089 centerY = b.height + b.y + m.bottom;
37090 centerH -= centerY;
37091 north.updateBox(this.safeBox(b));
37093 if(south && south.isVisible()){
37094 var b = south.getBox();
37095 var m = south.getMargins();
37096 b.width = w - (m.left+m.right);
37098 var totalHeight = (b.height + m.top + m.bottom);
37099 b.y = h - totalHeight + m.top;
37100 centerH -= totalHeight;
37101 south.updateBox(this.safeBox(b));
37103 if(west && west.isVisible()){
37104 var b = west.getBox();
37105 var m = west.getMargins();
37106 b.height = centerH - (m.top+m.bottom);
37108 b.y = centerY + m.top;
37109 var totalWidth = (b.width + m.left + m.right);
37110 centerX += totalWidth;
37111 centerW -= totalWidth;
37112 west.updateBox(this.safeBox(b));
37114 if(east && east.isVisible()){
37115 var b = east.getBox();
37116 var m = east.getMargins();
37117 b.height = centerH - (m.top+m.bottom);
37118 var totalWidth = (b.width + m.left + m.right);
37119 b.x = w - totalWidth + m.left;
37120 b.y = centerY + m.top;
37121 centerW -= totalWidth;
37122 east.updateBox(this.safeBox(b));
37125 var m = center.getMargins();
37127 x: centerX + m.left,
37128 y: centerY + m.top,
37129 width: centerW - (m.left+m.right),
37130 height: centerH - (m.top+m.bottom)
37132 //if(this.hideOnLayout){
37133 //center.el.setStyle("display", "block");
37135 center.updateBox(this.safeBox(centerBox));
37138 this.fireEvent("layout", this);
37142 safeBox : function(box){
37143 box.width = Math.max(0, box.width);
37144 box.height = Math.max(0, box.height);
37149 * Adds a ContentPanel (or subclass) to this layout.
37150 * @param {String} target The target region key (north, south, east, west or center).
37151 * @param {Roo.ContentPanel} panel The panel to add
37152 * @return {Roo.ContentPanel} The added panel
37154 add : function(target, panel){
37156 target = target.toLowerCase();
37157 return this.regions[target].add(panel);
37161 * Remove a ContentPanel (or subclass) to this layout.
37162 * @param {String} target The target region key (north, south, east, west or center).
37163 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37164 * @return {Roo.ContentPanel} The removed panel
37166 remove : function(target, panel){
37167 target = target.toLowerCase();
37168 return this.regions[target].remove(panel);
37172 * Searches all regions for a panel with the specified id
37173 * @param {String} panelId
37174 * @return {Roo.ContentPanel} The panel or null if it wasn't found
37176 findPanel : function(panelId){
37177 var rs = this.regions;
37178 for(var target in rs){
37179 if(typeof rs[target] != "function"){
37180 var p = rs[target].getPanel(panelId);
37190 * Searches all regions for a panel with the specified id and activates (shows) it.
37191 * @param {String/ContentPanel} panelId The panels id or the panel itself
37192 * @return {Roo.ContentPanel} The shown panel or null
37194 showPanel : function(panelId) {
37195 var rs = this.regions;
37196 for(var target in rs){
37197 var r = rs[target];
37198 if(typeof r != "function"){
37199 if(r.hasPanel(panelId)){
37200 return r.showPanel(panelId);
37208 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37209 * @param {Roo.state.Provider} provider (optional) An alternate state provider
37212 restoreState : function(provider){
37214 provider = Roo.state.Manager;
37216 var sm = new Roo.LayoutStateManager();
37217 sm.init(this, provider);
37223 * Adds a xtype elements to the layout.
37227 xtype : 'ContentPanel',
37234 xtype : 'NestedLayoutPanel',
37240 items : [ ... list of content panels or nested layout panels.. ]
37244 * @param {Object} cfg Xtype definition of item to add.
37246 addxtype : function(cfg)
37248 // basically accepts a pannel...
37249 // can accept a layout region..!?!?
37250 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37253 // theory? children can only be panels??
37255 //if (!cfg.xtype.match(/Panel$/)) {
37260 if (typeof(cfg.region) == 'undefined') {
37261 Roo.log("Failed to add Panel, region was not set");
37265 var region = cfg.region;
37271 xitems = cfg.items;
37276 if ( region == 'center') {
37277 Roo.log("Center: " + cfg.title);
37283 case 'Content': // ContentPanel (el, cfg)
37284 case 'Scroll': // ContentPanel (el, cfg)
37286 cfg.autoCreate = cfg.autoCreate || true;
37287 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37289 // var el = this.el.createChild();
37290 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37293 this.add(region, ret);
37297 case 'TreePanel': // our new panel!
37298 cfg.el = this.el.createChild();
37299 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37300 this.add(region, ret);
37305 // create a new Layout (which is a Border Layout...
37307 var clayout = cfg.layout;
37308 clayout.el = this.el.createChild();
37309 clayout.items = clayout.items || [];
37313 // replace this exitems with the clayout ones..
37314 xitems = clayout.items;
37316 // force background off if it's in center...
37317 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37318 cfg.background = false;
37320 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
37323 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37324 //console.log('adding nested layout panel ' + cfg.toSource());
37325 this.add(region, ret);
37326 nb = {}; /// find first...
37331 // needs grid and region
37333 //var el = this.getRegion(region).el.createChild();
37335 *var el = this.el.createChild();
37336 // create the grid first...
37337 cfg.grid.container = el;
37338 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37341 if (region == 'center' && this.active ) {
37342 cfg.background = false;
37345 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37347 this.add(region, ret);
37349 if (cfg.background) {
37350 // render grid on panel activation (if panel background)
37351 ret.on('activate', function(gp) {
37352 if (!gp.grid.rendered) {
37353 // gp.grid.render(el);
37357 // cfg.grid.render(el);
37363 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37364 // it was the old xcomponent building that caused this before.
37365 // espeically if border is the top element in the tree.
37375 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37377 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37378 this.add(region, ret);
37382 throw "Can not add '" + cfg.xtype + "' to Border";
37388 this.beginUpdate();
37392 Roo.each(xitems, function(i) {
37393 region = nb && i.region ? i.region : false;
37395 var add = ret.addxtype(i);
37398 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37399 if (!i.background) {
37400 abn[region] = nb[region] ;
37407 // make the last non-background panel active..
37408 //if (nb) { Roo.log(abn); }
37411 for(var r in abn) {
37412 region = this.getRegion(r);
37414 // tried using nb[r], but it does not work..
37416 region.showPanel(abn[r]);
37427 factory : function(cfg)
37430 var validRegions = Roo.bootstrap.layout.Border.regions;
37432 var target = cfg.region;
37435 var r = Roo.bootstrap.layout;
37439 return new r.North(cfg);
37441 return new r.South(cfg);
37443 return new r.East(cfg);
37445 return new r.West(cfg);
37447 return new r.Center(cfg);
37449 throw 'Layout region "'+target+'" not supported.';
37456 * Ext JS Library 1.1.1
37457 * Copyright(c) 2006-2007, Ext JS, LLC.
37459 * Originally Released Under LGPL - original licence link has changed is not relivant.
37462 * <script type="text/javascript">
37466 * @class Roo.bootstrap.layout.Basic
37467 * @extends Roo.util.Observable
37468 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37469 * and does not have a titlebar, tabs or any other features. All it does is size and position
37470 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37471 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
37472 * @cfg {string} region the region that it inhabits..
37473 * @cfg {bool} skipConfig skip config?
37477 Roo.bootstrap.layout.Basic = function(config){
37479 this.mgr = config.mgr;
37481 this.position = config.region;
37483 var skipConfig = config.skipConfig;
37487 * @scope Roo.BasicLayoutRegion
37491 * @event beforeremove
37492 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37493 * @param {Roo.LayoutRegion} this
37494 * @param {Roo.ContentPanel} panel The panel
37495 * @param {Object} e The cancel event object
37497 "beforeremove" : true,
37499 * @event invalidated
37500 * Fires when the layout for this region is changed.
37501 * @param {Roo.LayoutRegion} this
37503 "invalidated" : true,
37505 * @event visibilitychange
37506 * Fires when this region is shown or hidden
37507 * @param {Roo.LayoutRegion} this
37508 * @param {Boolean} visibility true or false
37510 "visibilitychange" : true,
37512 * @event paneladded
37513 * Fires when a panel is added.
37514 * @param {Roo.LayoutRegion} this
37515 * @param {Roo.ContentPanel} panel The panel
37517 "paneladded" : true,
37519 * @event panelremoved
37520 * Fires when a panel is removed.
37521 * @param {Roo.LayoutRegion} this
37522 * @param {Roo.ContentPanel} panel The panel
37524 "panelremoved" : true,
37526 * @event beforecollapse
37527 * Fires when this region before collapse.
37528 * @param {Roo.LayoutRegion} this
37530 "beforecollapse" : true,
37533 * Fires when this region is collapsed.
37534 * @param {Roo.LayoutRegion} this
37536 "collapsed" : true,
37539 * Fires when this region is expanded.
37540 * @param {Roo.LayoutRegion} this
37545 * Fires when this region is slid into view.
37546 * @param {Roo.LayoutRegion} this
37548 "slideshow" : true,
37551 * Fires when this region slides out of view.
37552 * @param {Roo.LayoutRegion} this
37554 "slidehide" : true,
37556 * @event panelactivated
37557 * Fires when a panel is activated.
37558 * @param {Roo.LayoutRegion} this
37559 * @param {Roo.ContentPanel} panel The activated panel
37561 "panelactivated" : true,
37564 * Fires when the user resizes this region.
37565 * @param {Roo.LayoutRegion} this
37566 * @param {Number} newSize The new size (width for east/west, height for north/south)
37570 /** A collection of panels in this region. @type Roo.util.MixedCollection */
37571 this.panels = new Roo.util.MixedCollection();
37572 this.panels.getKey = this.getPanelId.createDelegate(this);
37574 this.activePanel = null;
37575 // ensure listeners are added...
37577 if (config.listeners || config.events) {
37578 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37579 listeners : config.listeners || {},
37580 events : config.events || {}
37584 if(skipConfig !== true){
37585 this.applyConfig(config);
37589 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
37591 getPanelId : function(p){
37595 applyConfig : function(config){
37596 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37597 this.config = config;
37602 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
37603 * the width, for horizontal (north, south) the height.
37604 * @param {Number} newSize The new width or height
37606 resizeTo : function(newSize){
37607 var el = this.el ? this.el :
37608 (this.activePanel ? this.activePanel.getEl() : null);
37610 switch(this.position){
37613 el.setWidth(newSize);
37614 this.fireEvent("resized", this, newSize);
37618 el.setHeight(newSize);
37619 this.fireEvent("resized", this, newSize);
37625 getBox : function(){
37626 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
37629 getMargins : function(){
37630 return this.margins;
37633 updateBox : function(box){
37635 var el = this.activePanel.getEl();
37636 el.dom.style.left = box.x + "px";
37637 el.dom.style.top = box.y + "px";
37638 this.activePanel.setSize(box.width, box.height);
37642 * Returns the container element for this region.
37643 * @return {Roo.Element}
37645 getEl : function(){
37646 return this.activePanel;
37650 * Returns true if this region is currently visible.
37651 * @return {Boolean}
37653 isVisible : function(){
37654 return this.activePanel ? true : false;
37657 setActivePanel : function(panel){
37658 panel = this.getPanel(panel);
37659 if(this.activePanel && this.activePanel != panel){
37660 this.activePanel.setActiveState(false);
37661 this.activePanel.getEl().setLeftTop(-10000,-10000);
37663 this.activePanel = panel;
37664 panel.setActiveState(true);
37666 panel.setSize(this.box.width, this.box.height);
37668 this.fireEvent("panelactivated", this, panel);
37669 this.fireEvent("invalidated");
37673 * Show the specified panel.
37674 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
37675 * @return {Roo.ContentPanel} The shown panel or null
37677 showPanel : function(panel){
37678 panel = this.getPanel(panel);
37680 this.setActivePanel(panel);
37686 * Get the active panel for this region.
37687 * @return {Roo.ContentPanel} The active panel or null
37689 getActivePanel : function(){
37690 return this.activePanel;
37694 * Add the passed ContentPanel(s)
37695 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
37696 * @return {Roo.ContentPanel} The panel added (if only one was added)
37698 add : function(panel){
37699 if(arguments.length > 1){
37700 for(var i = 0, len = arguments.length; i < len; i++) {
37701 this.add(arguments[i]);
37705 if(this.hasPanel(panel)){
37706 this.showPanel(panel);
37709 var el = panel.getEl();
37710 if(el.dom.parentNode != this.mgr.el.dom){
37711 this.mgr.el.dom.appendChild(el.dom);
37713 if(panel.setRegion){
37714 panel.setRegion(this);
37716 this.panels.add(panel);
37717 el.setStyle("position", "absolute");
37718 if(!panel.background){
37719 this.setActivePanel(panel);
37720 if(this.config.initialSize && this.panels.getCount()==1){
37721 this.resizeTo(this.config.initialSize);
37724 this.fireEvent("paneladded", this, panel);
37729 * Returns true if the panel is in this region.
37730 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37731 * @return {Boolean}
37733 hasPanel : function(panel){
37734 if(typeof panel == "object"){ // must be panel obj
37735 panel = panel.getId();
37737 return this.getPanel(panel) ? true : false;
37741 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
37742 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37743 * @param {Boolean} preservePanel Overrides the config preservePanel option
37744 * @return {Roo.ContentPanel} The panel that was removed
37746 remove : function(panel, preservePanel){
37747 panel = this.getPanel(panel);
37752 this.fireEvent("beforeremove", this, panel, e);
37753 if(e.cancel === true){
37756 var panelId = panel.getId();
37757 this.panels.removeKey(panelId);
37762 * Returns the panel specified or null if it's not in this region.
37763 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37764 * @return {Roo.ContentPanel}
37766 getPanel : function(id){
37767 if(typeof id == "object"){ // must be panel obj
37770 return this.panels.get(id);
37774 * Returns this regions position (north/south/east/west/center).
37777 getPosition: function(){
37778 return this.position;
37782 * Ext JS Library 1.1.1
37783 * Copyright(c) 2006-2007, Ext JS, LLC.
37785 * Originally Released Under LGPL - original licence link has changed is not relivant.
37788 * <script type="text/javascript">
37792 * @class Roo.bootstrap.layout.Region
37793 * @extends Roo.bootstrap.layout.Basic
37794 * This class represents a region in a layout manager.
37796 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
37797 * @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})
37798 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
37799 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
37800 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
37801 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
37802 * @cfg {String} title The title for the region (overrides panel titles)
37803 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
37804 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
37805 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
37806 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
37807 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
37808 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
37809 * the space available, similar to FireFox 1.5 tabs (defaults to false)
37810 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
37811 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
37812 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
37814 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
37815 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
37816 * @cfg {Boolean} disableTabTips True to disable tab tooltips
37817 * @cfg {Number} width For East/West panels
37818 * @cfg {Number} height For North/South panels
37819 * @cfg {Boolean} split To show the splitter
37820 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
37822 * @cfg {string} cls Extra CSS classes to add to region
37824 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
37825 * @cfg {string} region the region that it inhabits..
37828 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
37829 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
37831 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
37832 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
37833 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
37835 Roo.bootstrap.layout.Region = function(config)
37837 this.applyConfig(config);
37839 var mgr = config.mgr;
37840 var pos = config.region;
37841 config.skipConfig = true;
37842 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
37845 this.onRender(mgr.el);
37848 this.visible = true;
37849 this.collapsed = false;
37850 this.unrendered_panels = [];
37853 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
37855 position: '', // set by wrapper (eg. north/south etc..)
37856 unrendered_panels : null, // unrendered panels.
37858 tabPosition : false,
37860 mgr: false, // points to 'Border'
37863 createBody : function(){
37864 /** This region's body element
37865 * @type Roo.Element */
37866 this.bodyEl = this.el.createChild({
37868 cls: "roo-layout-panel-body tab-content" // bootstrap added...
37872 onRender: function(ctr, pos)
37874 var dh = Roo.DomHelper;
37875 /** This region's container element
37876 * @type Roo.Element */
37877 this.el = dh.append(ctr.dom, {
37879 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
37881 /** This region's title element
37882 * @type Roo.Element */
37884 this.titleEl = dh.append(this.el.dom, {
37886 unselectable: "on",
37887 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
37889 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
37890 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
37894 this.titleEl.enableDisplayMode();
37895 /** This region's title text element
37896 * @type HTMLElement */
37897 this.titleTextEl = this.titleEl.dom.firstChild;
37898 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
37900 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
37901 this.closeBtn.enableDisplayMode();
37902 this.closeBtn.on("click", this.closeClicked, this);
37903 this.closeBtn.hide();
37905 this.createBody(this.config);
37906 if(this.config.hideWhenEmpty){
37908 this.on("paneladded", this.validateVisibility, this);
37909 this.on("panelremoved", this.validateVisibility, this);
37911 if(this.autoScroll){
37912 this.bodyEl.setStyle("overflow", "auto");
37914 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
37916 //if(c.titlebar !== false){
37917 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
37918 this.titleEl.hide();
37920 this.titleEl.show();
37921 if(this.config.title){
37922 this.titleTextEl.innerHTML = this.config.title;
37926 if(this.config.collapsed){
37927 this.collapse(true);
37929 if(this.config.hidden){
37933 if (this.unrendered_panels && this.unrendered_panels.length) {
37934 for (var i =0;i< this.unrendered_panels.length; i++) {
37935 this.add(this.unrendered_panels[i]);
37937 this.unrendered_panels = null;
37943 applyConfig : function(c)
37946 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
37947 var dh = Roo.DomHelper;
37948 if(c.titlebar !== false){
37949 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
37950 this.collapseBtn.on("click", this.collapse, this);
37951 this.collapseBtn.enableDisplayMode();
37953 if(c.showPin === true || this.showPin){
37954 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
37955 this.stickBtn.enableDisplayMode();
37956 this.stickBtn.on("click", this.expand, this);
37957 this.stickBtn.hide();
37962 /** This region's collapsed element
37963 * @type Roo.Element */
37966 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
37967 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
37970 if(c.floatable !== false){
37971 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
37972 this.collapsedEl.on("click", this.collapseClick, this);
37975 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
37976 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
37977 id: "message", unselectable: "on", style:{"float":"left"}});
37978 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
37980 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
37981 this.expandBtn.on("click", this.expand, this);
37985 if(this.collapseBtn){
37986 this.collapseBtn.setVisible(c.collapsible == true);
37989 this.cmargins = c.cmargins || this.cmargins ||
37990 (this.position == "west" || this.position == "east" ?
37991 {top: 0, left: 2, right:2, bottom: 0} :
37992 {top: 2, left: 0, right:0, bottom: 2});
37994 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37997 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
37999 this.autoScroll = c.autoScroll || false;
38004 this.duration = c.duration || .30;
38005 this.slideDuration = c.slideDuration || .45;
38010 * Returns true if this region is currently visible.
38011 * @return {Boolean}
38013 isVisible : function(){
38014 return this.visible;
38018 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38019 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
38021 //setCollapsedTitle : function(title){
38022 // title = title || " ";
38023 // if(this.collapsedTitleTextEl){
38024 // this.collapsedTitleTextEl.innerHTML = title;
38028 getBox : function(){
38030 // if(!this.collapsed){
38031 b = this.el.getBox(false, true);
38033 // b = this.collapsedEl.getBox(false, true);
38038 getMargins : function(){
38039 return this.margins;
38040 //return this.collapsed ? this.cmargins : this.margins;
38043 highlight : function(){
38044 this.el.addClass("x-layout-panel-dragover");
38047 unhighlight : function(){
38048 this.el.removeClass("x-layout-panel-dragover");
38051 updateBox : function(box)
38053 if (!this.bodyEl) {
38054 return; // not rendered yet..
38058 if(!this.collapsed){
38059 this.el.dom.style.left = box.x + "px";
38060 this.el.dom.style.top = box.y + "px";
38061 this.updateBody(box.width, box.height);
38063 this.collapsedEl.dom.style.left = box.x + "px";
38064 this.collapsedEl.dom.style.top = box.y + "px";
38065 this.collapsedEl.setSize(box.width, box.height);
38068 this.tabs.autoSizeTabs();
38072 updateBody : function(w, h)
38075 this.el.setWidth(w);
38076 w -= this.el.getBorderWidth("rl");
38077 if(this.config.adjustments){
38078 w += this.config.adjustments[0];
38081 if(h !== null && h > 0){
38082 this.el.setHeight(h);
38083 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38084 h -= this.el.getBorderWidth("tb");
38085 if(this.config.adjustments){
38086 h += this.config.adjustments[1];
38088 this.bodyEl.setHeight(h);
38090 h = this.tabs.syncHeight(h);
38093 if(this.panelSize){
38094 w = w !== null ? w : this.panelSize.width;
38095 h = h !== null ? h : this.panelSize.height;
38097 if(this.activePanel){
38098 var el = this.activePanel.getEl();
38099 w = w !== null ? w : el.getWidth();
38100 h = h !== null ? h : el.getHeight();
38101 this.panelSize = {width: w, height: h};
38102 this.activePanel.setSize(w, h);
38104 if(Roo.isIE && this.tabs){
38105 this.tabs.el.repaint();
38110 * Returns the container element for this region.
38111 * @return {Roo.Element}
38113 getEl : function(){
38118 * Hides this region.
38121 //if(!this.collapsed){
38122 this.el.dom.style.left = "-2000px";
38125 // this.collapsedEl.dom.style.left = "-2000px";
38126 // this.collapsedEl.hide();
38128 this.visible = false;
38129 this.fireEvent("visibilitychange", this, false);
38133 * Shows this region if it was previously hidden.
38136 //if(!this.collapsed){
38139 // this.collapsedEl.show();
38141 this.visible = true;
38142 this.fireEvent("visibilitychange", this, true);
38145 closeClicked : function(){
38146 if(this.activePanel){
38147 this.remove(this.activePanel);
38151 collapseClick : function(e){
38153 e.stopPropagation();
38156 e.stopPropagation();
38162 * Collapses this region.
38163 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38166 collapse : function(skipAnim, skipCheck = false){
38167 if(this.collapsed) {
38171 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38173 this.collapsed = true;
38175 this.split.el.hide();
38177 if(this.config.animate && skipAnim !== true){
38178 this.fireEvent("invalidated", this);
38179 this.animateCollapse();
38181 this.el.setLocation(-20000,-20000);
38183 this.collapsedEl.show();
38184 this.fireEvent("collapsed", this);
38185 this.fireEvent("invalidated", this);
38191 animateCollapse : function(){
38196 * Expands this region if it was previously collapsed.
38197 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38198 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38201 expand : function(e, skipAnim){
38203 e.stopPropagation();
38205 if(!this.collapsed || this.el.hasActiveFx()) {
38209 this.afterSlideIn();
38212 this.collapsed = false;
38213 if(this.config.animate && skipAnim !== true){
38214 this.animateExpand();
38218 this.split.el.show();
38220 this.collapsedEl.setLocation(-2000,-2000);
38221 this.collapsedEl.hide();
38222 this.fireEvent("invalidated", this);
38223 this.fireEvent("expanded", this);
38227 animateExpand : function(){
38231 initTabs : function()
38233 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38235 var ts = new Roo.bootstrap.panel.Tabs({
38236 el: this.bodyEl.dom,
38238 tabPosition: this.tabPosition ? this.tabPosition : 'top',
38239 disableTooltips: this.config.disableTabTips,
38240 toolbar : this.config.toolbar
38243 if(this.config.hideTabs){
38244 ts.stripWrap.setDisplayed(false);
38247 ts.resizeTabs = this.config.resizeTabs === true;
38248 ts.minTabWidth = this.config.minTabWidth || 40;
38249 ts.maxTabWidth = this.config.maxTabWidth || 250;
38250 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38251 ts.monitorResize = false;
38252 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38253 ts.bodyEl.addClass('roo-layout-tabs-body');
38254 this.panels.each(this.initPanelAsTab, this);
38257 initPanelAsTab : function(panel){
38258 var ti = this.tabs.addTab(
38262 this.config.closeOnTab && panel.isClosable(),
38265 if(panel.tabTip !== undefined){
38266 ti.setTooltip(panel.tabTip);
38268 ti.on("activate", function(){
38269 this.setActivePanel(panel);
38272 if(this.config.closeOnTab){
38273 ti.on("beforeclose", function(t, e){
38275 this.remove(panel);
38279 panel.tabItem = ti;
38284 updatePanelTitle : function(panel, title)
38286 if(this.activePanel == panel){
38287 this.updateTitle(title);
38290 var ti = this.tabs.getTab(panel.getEl().id);
38292 if(panel.tabTip !== undefined){
38293 ti.setTooltip(panel.tabTip);
38298 updateTitle : function(title){
38299 if(this.titleTextEl && !this.config.title){
38300 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
38304 setActivePanel : function(panel)
38306 panel = this.getPanel(panel);
38307 if(this.activePanel && this.activePanel != panel){
38308 if(this.activePanel.setActiveState(false) === false){
38312 this.activePanel = panel;
38313 panel.setActiveState(true);
38314 if(this.panelSize){
38315 panel.setSize(this.panelSize.width, this.panelSize.height);
38318 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38320 this.updateTitle(panel.getTitle());
38322 this.fireEvent("invalidated", this);
38324 this.fireEvent("panelactivated", this, panel);
38328 * Shows the specified panel.
38329 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38330 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38332 showPanel : function(panel)
38334 panel = this.getPanel(panel);
38337 var tab = this.tabs.getTab(panel.getEl().id);
38338 if(tab.isHidden()){
38339 this.tabs.unhideTab(tab.id);
38343 this.setActivePanel(panel);
38350 * Get the active panel for this region.
38351 * @return {Roo.ContentPanel} The active panel or null
38353 getActivePanel : function(){
38354 return this.activePanel;
38357 validateVisibility : function(){
38358 if(this.panels.getCount() < 1){
38359 this.updateTitle(" ");
38360 this.closeBtn.hide();
38363 if(!this.isVisible()){
38370 * Adds the passed ContentPanel(s) to this region.
38371 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38372 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38374 add : function(panel)
38376 if(arguments.length > 1){
38377 for(var i = 0, len = arguments.length; i < len; i++) {
38378 this.add(arguments[i]);
38383 // if we have not been rendered yet, then we can not really do much of this..
38384 if (!this.bodyEl) {
38385 this.unrendered_panels.push(panel);
38392 if(this.hasPanel(panel)){
38393 this.showPanel(panel);
38396 panel.setRegion(this);
38397 this.panels.add(panel);
38398 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38399 // sinle panel - no tab...?? would it not be better to render it with the tabs,
38400 // and hide them... ???
38401 this.bodyEl.dom.appendChild(panel.getEl().dom);
38402 if(panel.background !== true){
38403 this.setActivePanel(panel);
38405 this.fireEvent("paneladded", this, panel);
38412 this.initPanelAsTab(panel);
38416 if(panel.background !== true){
38417 this.tabs.activate(panel.getEl().id);
38419 this.fireEvent("paneladded", this, panel);
38424 * Hides the tab for the specified panel.
38425 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38427 hidePanel : function(panel){
38428 if(this.tabs && (panel = this.getPanel(panel))){
38429 this.tabs.hideTab(panel.getEl().id);
38434 * Unhides the tab for a previously hidden panel.
38435 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38437 unhidePanel : function(panel){
38438 if(this.tabs && (panel = this.getPanel(panel))){
38439 this.tabs.unhideTab(panel.getEl().id);
38443 clearPanels : function(){
38444 while(this.panels.getCount() > 0){
38445 this.remove(this.panels.first());
38450 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38451 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38452 * @param {Boolean} preservePanel Overrides the config preservePanel option
38453 * @return {Roo.ContentPanel} The panel that was removed
38455 remove : function(panel, preservePanel)
38457 panel = this.getPanel(panel);
38462 this.fireEvent("beforeremove", this, panel, e);
38463 if(e.cancel === true){
38466 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38467 var panelId = panel.getId();
38468 this.panels.removeKey(panelId);
38470 document.body.appendChild(panel.getEl().dom);
38473 this.tabs.removeTab(panel.getEl().id);
38474 }else if (!preservePanel){
38475 this.bodyEl.dom.removeChild(panel.getEl().dom);
38477 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38478 var p = this.panels.first();
38479 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38480 tempEl.appendChild(p.getEl().dom);
38481 this.bodyEl.update("");
38482 this.bodyEl.dom.appendChild(p.getEl().dom);
38484 this.updateTitle(p.getTitle());
38486 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38487 this.setActivePanel(p);
38489 panel.setRegion(null);
38490 if(this.activePanel == panel){
38491 this.activePanel = null;
38493 if(this.config.autoDestroy !== false && preservePanel !== true){
38494 try{panel.destroy();}catch(e){}
38496 this.fireEvent("panelremoved", this, panel);
38501 * Returns the TabPanel component used by this region
38502 * @return {Roo.TabPanel}
38504 getTabs : function(){
38508 createTool : function(parentEl, className){
38509 var btn = Roo.DomHelper.append(parentEl, {
38511 cls: "x-layout-tools-button",
38514 cls: "roo-layout-tools-button-inner " + className,
38518 btn.addClassOnOver("roo-layout-tools-button-over");
38523 * Ext JS Library 1.1.1
38524 * Copyright(c) 2006-2007, Ext JS, LLC.
38526 * Originally Released Under LGPL - original licence link has changed is not relivant.
38529 * <script type="text/javascript">
38535 * @class Roo.SplitLayoutRegion
38536 * @extends Roo.LayoutRegion
38537 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38539 Roo.bootstrap.layout.Split = function(config){
38540 this.cursor = config.cursor;
38541 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38544 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38546 splitTip : "Drag to resize.",
38547 collapsibleSplitTip : "Drag to resize. Double click to hide.",
38548 useSplitTips : false,
38550 applyConfig : function(config){
38551 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38554 onRender : function(ctr,pos) {
38556 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38557 if(!this.config.split){
38562 var splitEl = Roo.DomHelper.append(ctr.dom, {
38564 id: this.el.id + "-split",
38565 cls: "roo-layout-split roo-layout-split-"+this.position,
38568 /** The SplitBar for this region
38569 * @type Roo.SplitBar */
38570 // does not exist yet...
38571 Roo.log([this.position, this.orientation]);
38573 this.split = new Roo.bootstrap.SplitBar({
38574 dragElement : splitEl,
38575 resizingElement: this.el,
38576 orientation : this.orientation
38579 this.split.on("moved", this.onSplitMove, this);
38580 this.split.useShim = this.config.useShim === true;
38581 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38582 if(this.useSplitTips){
38583 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38585 //if(config.collapsible){
38586 // this.split.el.on("dblclick", this.collapse, this);
38589 if(typeof this.config.minSize != "undefined"){
38590 this.split.minSize = this.config.minSize;
38592 if(typeof this.config.maxSize != "undefined"){
38593 this.split.maxSize = this.config.maxSize;
38595 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
38596 this.hideSplitter();
38601 getHMaxSize : function(){
38602 var cmax = this.config.maxSize || 10000;
38603 var center = this.mgr.getRegion("center");
38604 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
38607 getVMaxSize : function(){
38608 var cmax = this.config.maxSize || 10000;
38609 var center = this.mgr.getRegion("center");
38610 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
38613 onSplitMove : function(split, newSize){
38614 this.fireEvent("resized", this, newSize);
38618 * Returns the {@link Roo.SplitBar} for this region.
38619 * @return {Roo.SplitBar}
38621 getSplitBar : function(){
38626 this.hideSplitter();
38627 Roo.bootstrap.layout.Split.superclass.hide.call(this);
38630 hideSplitter : function(){
38632 this.split.el.setLocation(-2000,-2000);
38633 this.split.el.hide();
38639 this.split.el.show();
38641 Roo.bootstrap.layout.Split.superclass.show.call(this);
38644 beforeSlide: function(){
38645 if(Roo.isGecko){// firefox overflow auto bug workaround
38646 this.bodyEl.clip();
38648 this.tabs.bodyEl.clip();
38650 if(this.activePanel){
38651 this.activePanel.getEl().clip();
38653 if(this.activePanel.beforeSlide){
38654 this.activePanel.beforeSlide();
38660 afterSlide : function(){
38661 if(Roo.isGecko){// firefox overflow auto bug workaround
38662 this.bodyEl.unclip();
38664 this.tabs.bodyEl.unclip();
38666 if(this.activePanel){
38667 this.activePanel.getEl().unclip();
38668 if(this.activePanel.afterSlide){
38669 this.activePanel.afterSlide();
38675 initAutoHide : function(){
38676 if(this.autoHide !== false){
38677 if(!this.autoHideHd){
38678 var st = new Roo.util.DelayedTask(this.slideIn, this);
38679 this.autoHideHd = {
38680 "mouseout": function(e){
38681 if(!e.within(this.el, true)){
38685 "mouseover" : function(e){
38691 this.el.on(this.autoHideHd);
38695 clearAutoHide : function(){
38696 if(this.autoHide !== false){
38697 this.el.un("mouseout", this.autoHideHd.mouseout);
38698 this.el.un("mouseover", this.autoHideHd.mouseover);
38702 clearMonitor : function(){
38703 Roo.get(document).un("click", this.slideInIf, this);
38706 // these names are backwards but not changed for compat
38707 slideOut : function(){
38708 if(this.isSlid || this.el.hasActiveFx()){
38711 this.isSlid = true;
38712 if(this.collapseBtn){
38713 this.collapseBtn.hide();
38715 this.closeBtnState = this.closeBtn.getStyle('display');
38716 this.closeBtn.hide();
38718 this.stickBtn.show();
38721 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
38722 this.beforeSlide();
38723 this.el.setStyle("z-index", 10001);
38724 this.el.slideIn(this.getSlideAnchor(), {
38725 callback: function(){
38727 this.initAutoHide();
38728 Roo.get(document).on("click", this.slideInIf, this);
38729 this.fireEvent("slideshow", this);
38736 afterSlideIn : function(){
38737 this.clearAutoHide();
38738 this.isSlid = false;
38739 this.clearMonitor();
38740 this.el.setStyle("z-index", "");
38741 if(this.collapseBtn){
38742 this.collapseBtn.show();
38744 this.closeBtn.setStyle('display', this.closeBtnState);
38746 this.stickBtn.hide();
38748 this.fireEvent("slidehide", this);
38751 slideIn : function(cb){
38752 if(!this.isSlid || this.el.hasActiveFx()){
38756 this.isSlid = false;
38757 this.beforeSlide();
38758 this.el.slideOut(this.getSlideAnchor(), {
38759 callback: function(){
38760 this.el.setLeftTop(-10000, -10000);
38762 this.afterSlideIn();
38770 slideInIf : function(e){
38771 if(!e.within(this.el)){
38776 animateCollapse : function(){
38777 this.beforeSlide();
38778 this.el.setStyle("z-index", 20000);
38779 var anchor = this.getSlideAnchor();
38780 this.el.slideOut(anchor, {
38781 callback : function(){
38782 this.el.setStyle("z-index", "");
38783 this.collapsedEl.slideIn(anchor, {duration:.3});
38785 this.el.setLocation(-10000,-10000);
38787 this.fireEvent("collapsed", this);
38794 animateExpand : function(){
38795 this.beforeSlide();
38796 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
38797 this.el.setStyle("z-index", 20000);
38798 this.collapsedEl.hide({
38801 this.el.slideIn(this.getSlideAnchor(), {
38802 callback : function(){
38803 this.el.setStyle("z-index", "");
38806 this.split.el.show();
38808 this.fireEvent("invalidated", this);
38809 this.fireEvent("expanded", this);
38837 getAnchor : function(){
38838 return this.anchors[this.position];
38841 getCollapseAnchor : function(){
38842 return this.canchors[this.position];
38845 getSlideAnchor : function(){
38846 return this.sanchors[this.position];
38849 getAlignAdj : function(){
38850 var cm = this.cmargins;
38851 switch(this.position){
38867 getExpandAdj : function(){
38868 var c = this.collapsedEl, cm = this.cmargins;
38869 switch(this.position){
38871 return [-(cm.right+c.getWidth()+cm.left), 0];
38874 return [cm.right+c.getWidth()+cm.left, 0];
38877 return [0, -(cm.top+cm.bottom+c.getHeight())];
38880 return [0, cm.top+cm.bottom+c.getHeight()];
38886 * Ext JS Library 1.1.1
38887 * Copyright(c) 2006-2007, Ext JS, LLC.
38889 * Originally Released Under LGPL - original licence link has changed is not relivant.
38892 * <script type="text/javascript">
38895 * These classes are private internal classes
38897 Roo.bootstrap.layout.Center = function(config){
38898 config.region = "center";
38899 Roo.bootstrap.layout.Region.call(this, config);
38900 this.visible = true;
38901 this.minWidth = config.minWidth || 20;
38902 this.minHeight = config.minHeight || 20;
38905 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
38907 // center panel can't be hidden
38911 // center panel can't be hidden
38914 getMinWidth: function(){
38915 return this.minWidth;
38918 getMinHeight: function(){
38919 return this.minHeight;
38933 Roo.bootstrap.layout.North = function(config)
38935 config.region = 'north';
38936 config.cursor = 'n-resize';
38938 Roo.bootstrap.layout.Split.call(this, config);
38942 this.split.placement = Roo.bootstrap.SplitBar.TOP;
38943 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
38944 this.split.el.addClass("roo-layout-split-v");
38946 var size = config.initialSize || config.height;
38947 if(typeof size != "undefined"){
38948 this.el.setHeight(size);
38951 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
38953 orientation: Roo.bootstrap.SplitBar.VERTICAL,
38957 getBox : function(){
38958 if(this.collapsed){
38959 return this.collapsedEl.getBox();
38961 var box = this.el.getBox();
38963 box.height += this.split.el.getHeight();
38968 updateBox : function(box){
38969 if(this.split && !this.collapsed){
38970 box.height -= this.split.el.getHeight();
38971 this.split.el.setLeft(box.x);
38972 this.split.el.setTop(box.y+box.height);
38973 this.split.el.setWidth(box.width);
38975 if(this.collapsed){
38976 this.updateBody(box.width, null);
38978 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38986 Roo.bootstrap.layout.South = function(config){
38987 config.region = 'south';
38988 config.cursor = 's-resize';
38989 Roo.bootstrap.layout.Split.call(this, config);
38991 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
38992 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
38993 this.split.el.addClass("roo-layout-split-v");
38995 var size = config.initialSize || config.height;
38996 if(typeof size != "undefined"){
38997 this.el.setHeight(size);
39001 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39002 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39003 getBox : function(){
39004 if(this.collapsed){
39005 return this.collapsedEl.getBox();
39007 var box = this.el.getBox();
39009 var sh = this.split.el.getHeight();
39016 updateBox : function(box){
39017 if(this.split && !this.collapsed){
39018 var sh = this.split.el.getHeight();
39021 this.split.el.setLeft(box.x);
39022 this.split.el.setTop(box.y-sh);
39023 this.split.el.setWidth(box.width);
39025 if(this.collapsed){
39026 this.updateBody(box.width, null);
39028 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39032 Roo.bootstrap.layout.East = function(config){
39033 config.region = "east";
39034 config.cursor = "e-resize";
39035 Roo.bootstrap.layout.Split.call(this, config);
39037 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39038 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39039 this.split.el.addClass("roo-layout-split-h");
39041 var size = config.initialSize || config.width;
39042 if(typeof size != "undefined"){
39043 this.el.setWidth(size);
39046 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39047 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39048 getBox : function(){
39049 if(this.collapsed){
39050 return this.collapsedEl.getBox();
39052 var box = this.el.getBox();
39054 var sw = this.split.el.getWidth();
39061 updateBox : function(box){
39062 if(this.split && !this.collapsed){
39063 var sw = this.split.el.getWidth();
39065 this.split.el.setLeft(box.x);
39066 this.split.el.setTop(box.y);
39067 this.split.el.setHeight(box.height);
39070 if(this.collapsed){
39071 this.updateBody(null, box.height);
39073 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39077 Roo.bootstrap.layout.West = function(config){
39078 config.region = "west";
39079 config.cursor = "w-resize";
39081 Roo.bootstrap.layout.Split.call(this, config);
39083 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39084 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39085 this.split.el.addClass("roo-layout-split-h");
39089 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39090 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39092 onRender: function(ctr, pos)
39094 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39095 var size = this.config.initialSize || this.config.width;
39096 if(typeof size != "undefined"){
39097 this.el.setWidth(size);
39101 getBox : function(){
39102 if(this.collapsed){
39103 return this.collapsedEl.getBox();
39105 var box = this.el.getBox();
39107 box.width += this.split.el.getWidth();
39112 updateBox : function(box){
39113 if(this.split && !this.collapsed){
39114 var sw = this.split.el.getWidth();
39116 this.split.el.setLeft(box.x+box.width);
39117 this.split.el.setTop(box.y);
39118 this.split.el.setHeight(box.height);
39120 if(this.collapsed){
39121 this.updateBody(null, box.height);
39123 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39125 });Roo.namespace("Roo.bootstrap.panel");/*
39127 * Ext JS Library 1.1.1
39128 * Copyright(c) 2006-2007, Ext JS, LLC.
39130 * Originally Released Under LGPL - original licence link has changed is not relivant.
39133 * <script type="text/javascript">
39136 * @class Roo.ContentPanel
39137 * @extends Roo.util.Observable
39138 * A basic ContentPanel element.
39139 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
39140 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
39141 * @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
39142 * @cfg {Boolean} closable True if the panel can be closed/removed
39143 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
39144 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39145 * @cfg {Toolbar} toolbar A toolbar for this panel
39146 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
39147 * @cfg {String} title The title for this panel
39148 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39149 * @cfg {String} url Calls {@link #setUrl} with this value
39150 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39151 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
39152 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
39153 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
39154 * @cfg {Boolean} badges render the badges
39157 * Create a new ContentPanel.
39158 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39159 * @param {String/Object} config A string to set only the title or a config object
39160 * @param {String} content (optional) Set the HTML content for this panel
39161 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39163 Roo.bootstrap.panel.Content = function( config){
39165 this.tpl = config.tpl || false;
39167 var el = config.el;
39168 var content = config.content;
39170 if(config.autoCreate){ // xtype is available if this is called from factory
39173 this.el = Roo.get(el);
39174 if(!this.el && config && config.autoCreate){
39175 if(typeof config.autoCreate == "object"){
39176 if(!config.autoCreate.id){
39177 config.autoCreate.id = config.id||el;
39179 this.el = Roo.DomHelper.append(document.body,
39180 config.autoCreate, true);
39182 var elcfg = { tag: "div",
39183 cls: "roo-layout-inactive-content",
39187 elcfg.html = config.html;
39191 this.el = Roo.DomHelper.append(document.body, elcfg , true);
39194 this.closable = false;
39195 this.loaded = false;
39196 this.active = false;
39199 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39201 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39203 this.wrapEl = this.el; //this.el.wrap();
39205 if (config.toolbar.items) {
39206 ti = config.toolbar.items ;
39207 delete config.toolbar.items ;
39211 this.toolbar.render(this.wrapEl, 'before');
39212 for(var i =0;i < ti.length;i++) {
39213 // Roo.log(['add child', items[i]]);
39214 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39216 this.toolbar.items = nitems;
39217 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39218 delete config.toolbar;
39222 // xtype created footer. - not sure if will work as we normally have to render first..
39223 if (this.footer && !this.footer.el && this.footer.xtype) {
39224 if (!this.wrapEl) {
39225 this.wrapEl = this.el.wrap();
39228 this.footer.container = this.wrapEl.createChild();
39230 this.footer = Roo.factory(this.footer, Roo);
39235 if(typeof config == "string"){
39236 this.title = config;
39238 Roo.apply(this, config);
39242 this.resizeEl = Roo.get(this.resizeEl, true);
39244 this.resizeEl = this.el;
39246 // handle view.xtype
39254 * Fires when this panel is activated.
39255 * @param {Roo.ContentPanel} this
39259 * @event deactivate
39260 * Fires when this panel is activated.
39261 * @param {Roo.ContentPanel} this
39263 "deactivate" : true,
39267 * Fires when this panel is resized if fitToFrame is true.
39268 * @param {Roo.ContentPanel} this
39269 * @param {Number} width The width after any component adjustments
39270 * @param {Number} height The height after any component adjustments
39276 * Fires when this tab is created
39277 * @param {Roo.ContentPanel} this
39288 if(this.autoScroll){
39289 this.resizeEl.setStyle("overflow", "auto");
39291 // fix randome scrolling
39292 //this.el.on('scroll', function() {
39293 // Roo.log('fix random scolling');
39294 // this.scrollTo('top',0);
39297 content = content || this.content;
39299 this.setContent(content);
39301 if(config && config.url){
39302 this.setUrl(this.url, this.params, this.loadOnce);
39307 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39309 if (this.view && typeof(this.view.xtype) != 'undefined') {
39310 this.view.el = this.el.appendChild(document.createElement("div"));
39311 this.view = Roo.factory(this.view);
39312 this.view.render && this.view.render(false, '');
39316 this.fireEvent('render', this);
39319 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39323 setRegion : function(region){
39324 this.region = region;
39325 this.setActiveClass(region && !this.background);
39329 setActiveClass: function(state)
39332 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39333 this.el.setStyle('position','relative');
39335 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39336 this.el.setStyle('position', 'absolute');
39341 * Returns the toolbar for this Panel if one was configured.
39342 * @return {Roo.Toolbar}
39344 getToolbar : function(){
39345 return this.toolbar;
39348 setActiveState : function(active)
39350 this.active = active;
39351 this.setActiveClass(active);
39353 if(this.fireEvent("deactivate", this) === false){
39358 this.fireEvent("activate", this);
39362 * Updates this panel's element
39363 * @param {String} content The new content
39364 * @param {Boolean} loadScripts (optional) true to look for and process scripts
39366 setContent : function(content, loadScripts){
39367 this.el.update(content, loadScripts);
39370 ignoreResize : function(w, h){
39371 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39374 this.lastSize = {width: w, height: h};
39379 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39380 * @return {Roo.UpdateManager} The UpdateManager
39382 getUpdateManager : function(){
39383 return this.el.getUpdateManager();
39386 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39387 * @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:
39390 url: "your-url.php",
39391 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39392 callback: yourFunction,
39393 scope: yourObject, //(optional scope)
39396 text: "Loading...",
39401 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39402 * 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.
39403 * @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}
39404 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39405 * @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.
39406 * @return {Roo.ContentPanel} this
39409 var um = this.el.getUpdateManager();
39410 um.update.apply(um, arguments);
39416 * 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.
39417 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39418 * @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)
39419 * @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)
39420 * @return {Roo.UpdateManager} The UpdateManager
39422 setUrl : function(url, params, loadOnce){
39423 if(this.refreshDelegate){
39424 this.removeListener("activate", this.refreshDelegate);
39426 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39427 this.on("activate", this.refreshDelegate);
39428 return this.el.getUpdateManager();
39431 _handleRefresh : function(url, params, loadOnce){
39432 if(!loadOnce || !this.loaded){
39433 var updater = this.el.getUpdateManager();
39434 updater.update(url, params, this._setLoaded.createDelegate(this));
39438 _setLoaded : function(){
39439 this.loaded = true;
39443 * Returns this panel's id
39446 getId : function(){
39451 * Returns this panel's element - used by regiosn to add.
39452 * @return {Roo.Element}
39454 getEl : function(){
39455 return this.wrapEl || this.el;
39460 adjustForComponents : function(width, height)
39462 //Roo.log('adjustForComponents ');
39463 if(this.resizeEl != this.el){
39464 width -= this.el.getFrameWidth('lr');
39465 height -= this.el.getFrameWidth('tb');
39468 var te = this.toolbar.getEl();
39469 te.setWidth(width);
39470 height -= te.getHeight();
39473 var te = this.footer.getEl();
39474 te.setWidth(width);
39475 height -= te.getHeight();
39479 if(this.adjustments){
39480 width += this.adjustments[0];
39481 height += this.adjustments[1];
39483 return {"width": width, "height": height};
39486 setSize : function(width, height){
39487 if(this.fitToFrame && !this.ignoreResize(width, height)){
39488 if(this.fitContainer && this.resizeEl != this.el){
39489 this.el.setSize(width, height);
39491 var size = this.adjustForComponents(width, height);
39492 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39493 this.fireEvent('resize', this, size.width, size.height);
39498 * Returns this panel's title
39501 getTitle : function(){
39503 if (typeof(this.title) != 'object') {
39508 for (var k in this.title) {
39509 if (!this.title.hasOwnProperty(k)) {
39513 if (k.indexOf('-') >= 0) {
39514 var s = k.split('-');
39515 for (var i = 0; i<s.length; i++) {
39516 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
39519 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
39526 * Set this panel's title
39527 * @param {String} title
39529 setTitle : function(title){
39530 this.title = title;
39532 this.region.updatePanelTitle(this, title);
39537 * Returns true is this panel was configured to be closable
39538 * @return {Boolean}
39540 isClosable : function(){
39541 return this.closable;
39544 beforeSlide : function(){
39546 this.resizeEl.clip();
39549 afterSlide : function(){
39551 this.resizeEl.unclip();
39555 * Force a content refresh from the URL specified in the {@link #setUrl} method.
39556 * Will fail silently if the {@link #setUrl} method has not been called.
39557 * This does not activate the panel, just updates its content.
39559 refresh : function(){
39560 if(this.refreshDelegate){
39561 this.loaded = false;
39562 this.refreshDelegate();
39567 * Destroys this panel
39569 destroy : function(){
39570 this.el.removeAllListeners();
39571 var tempEl = document.createElement("span");
39572 tempEl.appendChild(this.el.dom);
39573 tempEl.innerHTML = "";
39579 * form - if the content panel contains a form - this is a reference to it.
39580 * @type {Roo.form.Form}
39584 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
39585 * This contains a reference to it.
39591 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
39601 * @param {Object} cfg Xtype definition of item to add.
39605 getChildContainer: function () {
39606 return this.getEl();
39611 var ret = new Roo.factory(cfg);
39616 if (cfg.xtype.match(/^Form$/)) {
39619 //if (this.footer) {
39620 // el = this.footer.container.insertSibling(false, 'before');
39622 el = this.el.createChild();
39625 this.form = new Roo.form.Form(cfg);
39628 if ( this.form.allItems.length) {
39629 this.form.render(el.dom);
39633 // should only have one of theses..
39634 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
39635 // views.. should not be just added - used named prop 'view''
39637 cfg.el = this.el.appendChild(document.createElement("div"));
39640 var ret = new Roo.factory(cfg);
39642 ret.render && ret.render(false, ''); // render blank..
39652 * @class Roo.bootstrap.panel.Grid
39653 * @extends Roo.bootstrap.panel.Content
39655 * Create a new GridPanel.
39656 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
39657 * @param {Object} config A the config object
39663 Roo.bootstrap.panel.Grid = function(config)
39667 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
39668 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
39670 config.el = this.wrapper;
39671 //this.el = this.wrapper;
39673 if (config.container) {
39674 // ctor'ed from a Border/panel.grid
39677 this.wrapper.setStyle("overflow", "hidden");
39678 this.wrapper.addClass('roo-grid-container');
39683 if(config.toolbar){
39684 var tool_el = this.wrapper.createChild();
39685 this.toolbar = Roo.factory(config.toolbar);
39687 if (config.toolbar.items) {
39688 ti = config.toolbar.items ;
39689 delete config.toolbar.items ;
39693 this.toolbar.render(tool_el);
39694 for(var i =0;i < ti.length;i++) {
39695 // Roo.log(['add child', items[i]]);
39696 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39698 this.toolbar.items = nitems;
39700 delete config.toolbar;
39703 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
39704 config.grid.scrollBody = true;;
39705 config.grid.monitorWindowResize = false; // turn off autosizing
39706 config.grid.autoHeight = false;
39707 config.grid.autoWidth = false;
39709 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
39711 if (config.background) {
39712 // render grid on panel activation (if panel background)
39713 this.on('activate', function(gp) {
39714 if (!gp.grid.rendered) {
39715 gp.grid.render(this.wrapper);
39716 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
39721 this.grid.render(this.wrapper);
39722 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
39725 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
39726 // ??? needed ??? config.el = this.wrapper;
39731 // xtype created footer. - not sure if will work as we normally have to render first..
39732 if (this.footer && !this.footer.el && this.footer.xtype) {
39734 var ctr = this.grid.getView().getFooterPanel(true);
39735 this.footer.dataSource = this.grid.dataSource;
39736 this.footer = Roo.factory(this.footer, Roo);
39737 this.footer.render(ctr);
39747 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
39748 getId : function(){
39749 return this.grid.id;
39753 * Returns the grid for this panel
39754 * @return {Roo.bootstrap.Table}
39756 getGrid : function(){
39760 setSize : function(width, height){
39761 if(!this.ignoreResize(width, height)){
39762 var grid = this.grid;
39763 var size = this.adjustForComponents(width, height);
39764 // tfoot is not a footer?
39767 var gridel = grid.getGridEl();
39768 gridel.setSize(size.width, size.height);
39770 var tbd = grid.getGridEl().select('tbody', true).first();
39771 var thd = grid.getGridEl().select('thead',true).first();
39772 var tbf= grid.getGridEl().select('tfoot', true).first();
39775 size.height -= thd.getHeight();
39778 size.height -= thd.getHeight();
39781 tbd.setSize(size.width, size.height );
39782 // this is for the account management tab -seems to work there.
39783 var thd = grid.getGridEl().select('thead',true).first();
39785 // tbd.setSize(size.width, size.height - thd.getHeight());
39794 beforeSlide : function(){
39795 this.grid.getView().scroller.clip();
39798 afterSlide : function(){
39799 this.grid.getView().scroller.unclip();
39802 destroy : function(){
39803 this.grid.destroy();
39805 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
39810 * @class Roo.bootstrap.panel.Nest
39811 * @extends Roo.bootstrap.panel.Content
39813 * Create a new Panel, that can contain a layout.Border.
39816 * @param {Roo.BorderLayout} layout The layout for this panel
39817 * @param {String/Object} config A string to set only the title or a config object
39819 Roo.bootstrap.panel.Nest = function(config)
39821 // construct with only one argument..
39822 /* FIXME - implement nicer consturctors
39823 if (layout.layout) {
39825 layout = config.layout;
39826 delete config.layout;
39828 if (layout.xtype && !layout.getEl) {
39829 // then layout needs constructing..
39830 layout = Roo.factory(layout, Roo);
39834 config.el = config.layout.getEl();
39836 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
39838 config.layout.monitorWindowResize = false; // turn off autosizing
39839 this.layout = config.layout;
39840 this.layout.getEl().addClass("roo-layout-nested-layout");
39841 this.layout.parent = this;
39848 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
39850 setSize : function(width, height){
39851 if(!this.ignoreResize(width, height)){
39852 var size = this.adjustForComponents(width, height);
39853 var el = this.layout.getEl();
39854 if (size.height < 1) {
39855 el.setWidth(size.width);
39857 el.setSize(size.width, size.height);
39859 var touch = el.dom.offsetWidth;
39860 this.layout.layout();
39861 // ie requires a double layout on the first pass
39862 if(Roo.isIE && !this.initialized){
39863 this.initialized = true;
39864 this.layout.layout();
39869 // activate all subpanels if not currently active..
39871 setActiveState : function(active){
39872 this.active = active;
39873 this.setActiveClass(active);
39876 this.fireEvent("deactivate", this);
39880 this.fireEvent("activate", this);
39881 // not sure if this should happen before or after..
39882 if (!this.layout) {
39883 return; // should not happen..
39886 for (var r in this.layout.regions) {
39887 reg = this.layout.getRegion(r);
39888 if (reg.getActivePanel()) {
39889 //reg.showPanel(reg.getActivePanel()); // force it to activate..
39890 reg.setActivePanel(reg.getActivePanel());
39893 if (!reg.panels.length) {
39896 reg.showPanel(reg.getPanel(0));
39905 * Returns the nested BorderLayout for this panel
39906 * @return {Roo.BorderLayout}
39908 getLayout : function(){
39909 return this.layout;
39913 * Adds a xtype elements to the layout of the nested panel
39917 xtype : 'ContentPanel',
39924 xtype : 'NestedLayoutPanel',
39930 items : [ ... list of content panels or nested layout panels.. ]
39934 * @param {Object} cfg Xtype definition of item to add.
39936 addxtype : function(cfg) {
39937 return this.layout.addxtype(cfg);
39942 * Ext JS Library 1.1.1
39943 * Copyright(c) 2006-2007, Ext JS, LLC.
39945 * Originally Released Under LGPL - original licence link has changed is not relivant.
39948 * <script type="text/javascript">
39951 * @class Roo.TabPanel
39952 * @extends Roo.util.Observable
39953 * A lightweight tab container.
39957 // basic tabs 1, built from existing content
39958 var tabs = new Roo.TabPanel("tabs1");
39959 tabs.addTab("script", "View Script");
39960 tabs.addTab("markup", "View Markup");
39961 tabs.activate("script");
39963 // more advanced tabs, built from javascript
39964 var jtabs = new Roo.TabPanel("jtabs");
39965 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
39967 // set up the UpdateManager
39968 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
39969 var updater = tab2.getUpdateManager();
39970 updater.setDefaultUrl("ajax1.htm");
39971 tab2.on('activate', updater.refresh, updater, true);
39973 // Use setUrl for Ajax loading
39974 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
39975 tab3.setUrl("ajax2.htm", null, true);
39978 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
39981 jtabs.activate("jtabs-1");
39984 * Create a new TabPanel.
39985 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
39986 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
39988 Roo.bootstrap.panel.Tabs = function(config){
39990 * The container element for this TabPanel.
39991 * @type Roo.Element
39993 this.el = Roo.get(config.el);
39996 if(typeof config == "boolean"){
39997 this.tabPosition = config ? "bottom" : "top";
39999 Roo.apply(this, config);
40003 if(this.tabPosition == "bottom"){
40004 // if tabs are at the bottom = create the body first.
40005 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40006 this.el.addClass("roo-tabs-bottom");
40008 // next create the tabs holders
40010 if (this.tabPosition == "west"){
40012 var reg = this.region; // fake it..
40014 if (!reg.mgr.parent) {
40017 reg = reg.mgr.parent.region;
40019 Roo.log("got nest?");
40021 if (reg.mgr.getRegion('west')) {
40022 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40023 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40024 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40025 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40026 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40034 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40035 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40036 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40037 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40042 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40045 // finally - if tabs are at the top, then create the body last..
40046 if(this.tabPosition != "bottom"){
40047 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40048 * @type Roo.Element
40050 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40051 this.el.addClass("roo-tabs-top");
40055 this.bodyEl.setStyle("position", "relative");
40057 this.active = null;
40058 this.activateDelegate = this.activate.createDelegate(this);
40063 * Fires when the active tab changes
40064 * @param {Roo.TabPanel} this
40065 * @param {Roo.TabPanelItem} activePanel The new active tab
40069 * @event beforetabchange
40070 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40071 * @param {Roo.TabPanel} this
40072 * @param {Object} e Set cancel to true on this object to cancel the tab change
40073 * @param {Roo.TabPanelItem} tab The tab being changed to
40075 "beforetabchange" : true
40078 Roo.EventManager.onWindowResize(this.onResize, this);
40079 this.cpad = this.el.getPadding("lr");
40080 this.hiddenCount = 0;
40083 // toolbar on the tabbar support...
40084 if (this.toolbar) {
40085 alert("no toolbar support yet");
40086 this.toolbar = false;
40088 var tcfg = this.toolbar;
40089 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
40090 this.toolbar = new Roo.Toolbar(tcfg);
40091 if (Roo.isSafari) {
40092 var tbl = tcfg.container.child('table', true);
40093 tbl.setAttribute('width', '100%');
40101 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40104 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40106 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40108 tabPosition : "top",
40110 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40112 currentTabWidth : 0,
40114 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40118 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40122 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40124 preferredTabWidth : 175,
40126 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40128 resizeTabs : false,
40130 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40132 monitorResize : true,
40134 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
40136 toolbar : false, // set by caller..
40138 region : false, /// set by caller
40140 disableTooltips : true, // not used yet...
40143 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40144 * @param {String} id The id of the div to use <b>or create</b>
40145 * @param {String} text The text for the tab
40146 * @param {String} content (optional) Content to put in the TabPanelItem body
40147 * @param {Boolean} closable (optional) True to create a close icon on the tab
40148 * @return {Roo.TabPanelItem} The created TabPanelItem
40150 addTab : function(id, text, content, closable, tpl)
40152 var item = new Roo.bootstrap.panel.TabItem({
40156 closable : closable,
40159 this.addTabItem(item);
40161 item.setContent(content);
40167 * Returns the {@link Roo.TabPanelItem} with the specified id/index
40168 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40169 * @return {Roo.TabPanelItem}
40171 getTab : function(id){
40172 return this.items[id];
40176 * Hides the {@link Roo.TabPanelItem} with the specified id/index
40177 * @param {String/Number} id The id or index of the TabPanelItem to hide.
40179 hideTab : function(id){
40180 var t = this.items[id];
40183 this.hiddenCount++;
40184 this.autoSizeTabs();
40189 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40190 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40192 unhideTab : function(id){
40193 var t = this.items[id];
40195 t.setHidden(false);
40196 this.hiddenCount--;
40197 this.autoSizeTabs();
40202 * Adds an existing {@link Roo.TabPanelItem}.
40203 * @param {Roo.TabPanelItem} item The TabPanelItem to add
40205 addTabItem : function(item)
40207 this.items[item.id] = item;
40208 this.items.push(item);
40209 this.autoSizeTabs();
40210 // if(this.resizeTabs){
40211 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40212 // this.autoSizeTabs();
40214 // item.autoSize();
40219 * Removes a {@link Roo.TabPanelItem}.
40220 * @param {String/Number} id The id or index of the TabPanelItem to remove.
40222 removeTab : function(id){
40223 var items = this.items;
40224 var tab = items[id];
40225 if(!tab) { return; }
40226 var index = items.indexOf(tab);
40227 if(this.active == tab && items.length > 1){
40228 var newTab = this.getNextAvailable(index);
40233 this.stripEl.dom.removeChild(tab.pnode.dom);
40234 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40235 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40237 items.splice(index, 1);
40238 delete this.items[tab.id];
40239 tab.fireEvent("close", tab);
40240 tab.purgeListeners();
40241 this.autoSizeTabs();
40244 getNextAvailable : function(start){
40245 var items = this.items;
40247 // look for a next tab that will slide over to
40248 // replace the one being removed
40249 while(index < items.length){
40250 var item = items[++index];
40251 if(item && !item.isHidden()){
40255 // if one isn't found select the previous tab (on the left)
40258 var item = items[--index];
40259 if(item && !item.isHidden()){
40267 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40268 * @param {String/Number} id The id or index of the TabPanelItem to disable.
40270 disableTab : function(id){
40271 var tab = this.items[id];
40272 if(tab && this.active != tab){
40278 * Enables a {@link Roo.TabPanelItem} that is disabled.
40279 * @param {String/Number} id The id or index of the TabPanelItem to enable.
40281 enableTab : function(id){
40282 var tab = this.items[id];
40287 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40288 * @param {String/Number} id The id or index of the TabPanelItem to activate.
40289 * @return {Roo.TabPanelItem} The TabPanelItem.
40291 activate : function(id)
40293 //Roo.log('activite:' + id);
40295 var tab = this.items[id];
40299 if(tab == this.active || tab.disabled){
40303 this.fireEvent("beforetabchange", this, e, tab);
40304 if(e.cancel !== true && !tab.disabled){
40306 this.active.hide();
40308 this.active = this.items[id];
40309 this.active.show();
40310 this.fireEvent("tabchange", this, this.active);
40316 * Gets the active {@link Roo.TabPanelItem}.
40317 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40319 getActiveTab : function(){
40320 return this.active;
40324 * Updates the tab body element to fit the height of the container element
40325 * for overflow scrolling
40326 * @param {Number} targetHeight (optional) Override the starting height from the elements height
40328 syncHeight : function(targetHeight){
40329 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40330 var bm = this.bodyEl.getMargins();
40331 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40332 this.bodyEl.setHeight(newHeight);
40336 onResize : function(){
40337 if(this.monitorResize){
40338 this.autoSizeTabs();
40343 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40345 beginUpdate : function(){
40346 this.updating = true;
40350 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40352 endUpdate : function(){
40353 this.updating = false;
40354 this.autoSizeTabs();
40358 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40360 autoSizeTabs : function()
40362 var count = this.items.length;
40363 var vcount = count - this.hiddenCount;
40366 this.stripEl.hide();
40368 this.stripEl.show();
40371 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40376 var w = Math.max(this.el.getWidth() - this.cpad, 10);
40377 var availWidth = Math.floor(w / vcount);
40378 var b = this.stripBody;
40379 if(b.getWidth() > w){
40380 var tabs = this.items;
40381 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40382 if(availWidth < this.minTabWidth){
40383 /*if(!this.sleft){ // incomplete scrolling code
40384 this.createScrollButtons();
40387 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40390 if(this.currentTabWidth < this.preferredTabWidth){
40391 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40397 * Returns the number of tabs in this TabPanel.
40400 getCount : function(){
40401 return this.items.length;
40405 * Resizes all the tabs to the passed width
40406 * @param {Number} The new width
40408 setTabWidth : function(width){
40409 this.currentTabWidth = width;
40410 for(var i = 0, len = this.items.length; i < len; i++) {
40411 if(!this.items[i].isHidden()) {
40412 this.items[i].setWidth(width);
40418 * Destroys this TabPanel
40419 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40421 destroy : function(removeEl){
40422 Roo.EventManager.removeResizeListener(this.onResize, this);
40423 for(var i = 0, len = this.items.length; i < len; i++){
40424 this.items[i].purgeListeners();
40426 if(removeEl === true){
40427 this.el.update("");
40432 createStrip : function(container)
40434 var strip = document.createElement("nav");
40435 strip.className = Roo.bootstrap.version == 4 ?
40436 "navbar-light bg-light" :
40437 "navbar navbar-default"; //"x-tabs-wrap";
40438 container.appendChild(strip);
40442 createStripList : function(strip)
40444 // div wrapper for retard IE
40445 // returns the "tr" element.
40446 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40447 //'<div class="x-tabs-strip-wrap">'+
40448 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40449 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40450 return strip.firstChild; //.firstChild.firstChild.firstChild;
40452 createBody : function(container)
40454 var body = document.createElement("div");
40455 Roo.id(body, "tab-body");
40456 //Roo.fly(body).addClass("x-tabs-body");
40457 Roo.fly(body).addClass("tab-content");
40458 container.appendChild(body);
40461 createItemBody :function(bodyEl, id){
40462 var body = Roo.getDom(id);
40464 body = document.createElement("div");
40467 //Roo.fly(body).addClass("x-tabs-item-body");
40468 Roo.fly(body).addClass("tab-pane");
40469 bodyEl.insertBefore(body, bodyEl.firstChild);
40473 createStripElements : function(stripEl, text, closable, tpl)
40475 var td = document.createElement("li"); // was td..
40476 td.className = 'nav-item';
40478 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40481 stripEl.appendChild(td);
40483 td.className = "x-tabs-closable";
40484 if(!this.closeTpl){
40485 this.closeTpl = new Roo.Template(
40486 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40487 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40488 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
40491 var el = this.closeTpl.overwrite(td, {"text": text});
40492 var close = el.getElementsByTagName("div")[0];
40493 var inner = el.getElementsByTagName("em")[0];
40494 return {"el": el, "close": close, "inner": inner};
40497 // not sure what this is..
40498 // if(!this.tabTpl){
40499 //this.tabTpl = new Roo.Template(
40500 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40501 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40503 // this.tabTpl = new Roo.Template(
40504 // '<a href="#">' +
40505 // '<span unselectable="on"' +
40506 // (this.disableTooltips ? '' : ' title="{text}"') +
40507 // ' >{text}</span></a>'
40513 var template = tpl || this.tabTpl || false;
40516 template = new Roo.Template(
40517 Roo.bootstrap.version == 4 ?
40519 '<a class="nav-link" href="#" unselectable="on"' +
40520 (this.disableTooltips ? '' : ' title="{text}"') +
40523 '<a class="nav-link" href="#">' +
40524 '<span unselectable="on"' +
40525 (this.disableTooltips ? '' : ' title="{text}"') +
40526 ' >{text}</span></a>'
40531 switch (typeof(template)) {
40535 template = new Roo.Template(template);
40541 var el = template.overwrite(td, {"text": text});
40543 var inner = el.getElementsByTagName("span")[0];
40545 return {"el": el, "inner": inner};
40553 * @class Roo.TabPanelItem
40554 * @extends Roo.util.Observable
40555 * Represents an individual item (tab plus body) in a TabPanel.
40556 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
40557 * @param {String} id The id of this TabPanelItem
40558 * @param {String} text The text for the tab of this TabPanelItem
40559 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
40561 Roo.bootstrap.panel.TabItem = function(config){
40563 * The {@link Roo.TabPanel} this TabPanelItem belongs to
40564 * @type Roo.TabPanel
40566 this.tabPanel = config.panel;
40568 * The id for this TabPanelItem
40571 this.id = config.id;
40573 this.disabled = false;
40575 this.text = config.text;
40577 this.loaded = false;
40578 this.closable = config.closable;
40581 * The body element for this TabPanelItem.
40582 * @type Roo.Element
40584 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
40585 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
40586 this.bodyEl.setStyle("display", "block");
40587 this.bodyEl.setStyle("zoom", "1");
40588 //this.hideAction();
40590 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
40592 this.el = Roo.get(els.el);
40593 this.inner = Roo.get(els.inner, true);
40594 this.textEl = Roo.bootstrap.version == 4 ?
40595 this.el : Roo.get(this.el.dom.firstChild, true);
40597 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
40598 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
40601 // this.el.on("mousedown", this.onTabMouseDown, this);
40602 this.el.on("click", this.onTabClick, this);
40604 if(config.closable){
40605 var c = Roo.get(els.close, true);
40606 c.dom.title = this.closeText;
40607 c.addClassOnOver("close-over");
40608 c.on("click", this.closeClick, this);
40614 * Fires when this tab becomes the active tab.
40615 * @param {Roo.TabPanel} tabPanel The parent TabPanel
40616 * @param {Roo.TabPanelItem} this
40620 * @event beforeclose
40621 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
40622 * @param {Roo.TabPanelItem} this
40623 * @param {Object} e Set cancel to true on this object to cancel the close.
40625 "beforeclose": true,
40628 * Fires when this tab is closed.
40629 * @param {Roo.TabPanelItem} this
40633 * @event deactivate
40634 * Fires when this tab is no longer the active tab.
40635 * @param {Roo.TabPanel} tabPanel The parent TabPanel
40636 * @param {Roo.TabPanelItem} this
40638 "deactivate" : true
40640 this.hidden = false;
40642 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
40645 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
40647 purgeListeners : function(){
40648 Roo.util.Observable.prototype.purgeListeners.call(this);
40649 this.el.removeAllListeners();
40652 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
40655 this.status_node.addClass("active");
40658 this.tabPanel.stripWrap.repaint();
40660 this.fireEvent("activate", this.tabPanel, this);
40664 * Returns true if this tab is the active tab.
40665 * @return {Boolean}
40667 isActive : function(){
40668 return this.tabPanel.getActiveTab() == this;
40672 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
40675 this.status_node.removeClass("active");
40677 this.fireEvent("deactivate", this.tabPanel, this);
40680 hideAction : function(){
40681 this.bodyEl.hide();
40682 this.bodyEl.setStyle("position", "absolute");
40683 this.bodyEl.setLeft("-20000px");
40684 this.bodyEl.setTop("-20000px");
40687 showAction : function(){
40688 this.bodyEl.setStyle("position", "relative");
40689 this.bodyEl.setTop("");
40690 this.bodyEl.setLeft("");
40691 this.bodyEl.show();
40695 * Set the tooltip for the tab.
40696 * @param {String} tooltip The tab's tooltip
40698 setTooltip : function(text){
40699 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
40700 this.textEl.dom.qtip = text;
40701 this.textEl.dom.removeAttribute('title');
40703 this.textEl.dom.title = text;
40707 onTabClick : function(e){
40708 e.preventDefault();
40709 this.tabPanel.activate(this.id);
40712 onTabMouseDown : function(e){
40713 e.preventDefault();
40714 this.tabPanel.activate(this.id);
40717 getWidth : function(){
40718 return this.inner.getWidth();
40721 setWidth : function(width){
40722 var iwidth = width - this.linode.getPadding("lr");
40723 this.inner.setWidth(iwidth);
40724 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
40725 this.linode.setWidth(width);
40729 * Show or hide the tab
40730 * @param {Boolean} hidden True to hide or false to show.
40732 setHidden : function(hidden){
40733 this.hidden = hidden;
40734 this.linode.setStyle("display", hidden ? "none" : "");
40738 * Returns true if this tab is "hidden"
40739 * @return {Boolean}
40741 isHidden : function(){
40742 return this.hidden;
40746 * Returns the text for this tab
40749 getText : function(){
40753 autoSize : function(){
40754 //this.el.beginMeasure();
40755 this.textEl.setWidth(1);
40757 * #2804 [new] Tabs in Roojs
40758 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
40760 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
40761 //this.el.endMeasure();
40765 * Sets the text for the tab (Note: this also sets the tooltip text)
40766 * @param {String} text The tab's text and tooltip
40768 setText : function(text){
40770 this.textEl.update(text);
40771 this.setTooltip(text);
40772 //if(!this.tabPanel.resizeTabs){
40773 // this.autoSize();
40777 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
40779 activate : function(){
40780 this.tabPanel.activate(this.id);
40784 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
40786 disable : function(){
40787 if(this.tabPanel.active != this){
40788 this.disabled = true;
40789 this.status_node.addClass("disabled");
40794 * Enables this TabPanelItem if it was previously disabled.
40796 enable : function(){
40797 this.disabled = false;
40798 this.status_node.removeClass("disabled");
40802 * Sets the content for this TabPanelItem.
40803 * @param {String} content The content
40804 * @param {Boolean} loadScripts true to look for and load scripts
40806 setContent : function(content, loadScripts){
40807 this.bodyEl.update(content, loadScripts);
40811 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
40812 * @return {Roo.UpdateManager} The UpdateManager
40814 getUpdateManager : function(){
40815 return this.bodyEl.getUpdateManager();
40819 * Set a URL to be used to load the content for this TabPanelItem.
40820 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
40821 * @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)
40822 * @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)
40823 * @return {Roo.UpdateManager} The UpdateManager
40825 setUrl : function(url, params, loadOnce){
40826 if(this.refreshDelegate){
40827 this.un('activate', this.refreshDelegate);
40829 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40830 this.on("activate", this.refreshDelegate);
40831 return this.bodyEl.getUpdateManager();
40835 _handleRefresh : function(url, params, loadOnce){
40836 if(!loadOnce || !this.loaded){
40837 var updater = this.bodyEl.getUpdateManager();
40838 updater.update(url, params, this._setLoaded.createDelegate(this));
40843 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
40844 * Will fail silently if the setUrl method has not been called.
40845 * This does not activate the panel, just updates its content.
40847 refresh : function(){
40848 if(this.refreshDelegate){
40849 this.loaded = false;
40850 this.refreshDelegate();
40855 _setLoaded : function(){
40856 this.loaded = true;
40860 closeClick : function(e){
40863 this.fireEvent("beforeclose", this, o);
40864 if(o.cancel !== true){
40865 this.tabPanel.removeTab(this.id);
40869 * The text displayed in the tooltip for the close icon.
40872 closeText : "Close this tab"
40875 * This script refer to:
40876 * Title: International Telephone Input
40877 * Author: Jack O'Connor
40878 * Code version: v12.1.12
40879 * Availability: https://github.com/jackocnr/intl-tel-input.git
40882 Roo.bootstrap.PhoneInputData = function() {
40885 "Afghanistan (افغانستان)",
40890 "Albania (Shqipëri)",
40895 "Algeria (الجزائر)",
40920 "Antigua and Barbuda",
40930 "Armenia (Հայաստան)",
40946 "Austria (Österreich)",
40951 "Azerbaijan (Azərbaycan)",
40961 "Bahrain (البحرين)",
40966 "Bangladesh (বাংলাদেশ)",
40976 "Belarus (Беларусь)",
40981 "Belgium (België)",
41011 "Bosnia and Herzegovina (Босна и Херцеговина)",
41026 "British Indian Ocean Territory",
41031 "British Virgin Islands",
41041 "Bulgaria (България)",
41051 "Burundi (Uburundi)",
41056 "Cambodia (កម្ពុជា)",
41061 "Cameroon (Cameroun)",
41070 ["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"]
41073 "Cape Verde (Kabu Verdi)",
41078 "Caribbean Netherlands",
41089 "Central African Republic (République centrafricaine)",
41109 "Christmas Island",
41115 "Cocos (Keeling) Islands",
41126 "Comoros (جزر القمر)",
41131 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41136 "Congo (Republic) (Congo-Brazzaville)",
41156 "Croatia (Hrvatska)",
41177 "Czech Republic (Česká republika)",
41182 "Denmark (Danmark)",
41197 "Dominican Republic (República Dominicana)",
41201 ["809", "829", "849"]
41219 "Equatorial Guinea (Guinea Ecuatorial)",
41239 "Falkland Islands (Islas Malvinas)",
41244 "Faroe Islands (Føroyar)",
41265 "French Guiana (Guyane française)",
41270 "French Polynesia (Polynésie française)",
41285 "Georgia (საქართველო)",
41290 "Germany (Deutschland)",
41310 "Greenland (Kalaallit Nunaat)",
41347 "Guinea-Bissau (Guiné Bissau)",
41372 "Hungary (Magyarország)",
41377 "Iceland (Ísland)",
41397 "Iraq (العراق)",
41413 "Israel (ישראל)",
41440 "Jordan (الأردن)",
41445 "Kazakhstan (Казахстан)",
41466 "Kuwait (الكويت)",
41471 "Kyrgyzstan (Кыргызстан)",
41481 "Latvia (Latvija)",
41486 "Lebanon (لبنان)",
41501 "Libya (ليبيا)",
41511 "Lithuania (Lietuva)",
41526 "Macedonia (FYROM) (Македонија)",
41531 "Madagascar (Madagasikara)",
41561 "Marshall Islands",
41571 "Mauritania (موريتانيا)",
41576 "Mauritius (Moris)",
41597 "Moldova (Republica Moldova)",
41607 "Mongolia (Монгол)",
41612 "Montenegro (Crna Gora)",
41622 "Morocco (المغرب)",
41628 "Mozambique (Moçambique)",
41633 "Myanmar (Burma) (မြန်မာ)",
41638 "Namibia (Namibië)",
41653 "Netherlands (Nederland)",
41658 "New Caledonia (Nouvelle-Calédonie)",
41693 "North Korea (조선 민주주의 인민 공화국)",
41698 "Northern Mariana Islands",
41714 "Pakistan (پاکستان)",
41724 "Palestine (فلسطين)",
41734 "Papua New Guinea",
41776 "Réunion (La Réunion)",
41782 "Romania (România)",
41798 "Saint Barthélemy",
41809 "Saint Kitts and Nevis",
41819 "Saint Martin (Saint-Martin (partie française))",
41825 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
41830 "Saint Vincent and the Grenadines",
41845 "São Tomé and Príncipe (São Tomé e Príncipe)",
41850 "Saudi Arabia (المملكة العربية السعودية)",
41855 "Senegal (Sénégal)",
41885 "Slovakia (Slovensko)",
41890 "Slovenia (Slovenija)",
41900 "Somalia (Soomaaliya)",
41910 "South Korea (대한민국)",
41915 "South Sudan (جنوب السودان)",
41925 "Sri Lanka (ශ්රී ලංකාව)",
41930 "Sudan (السودان)",
41940 "Svalbard and Jan Mayen",
41951 "Sweden (Sverige)",
41956 "Switzerland (Schweiz)",
41961 "Syria (سوريا)",
42006 "Trinidad and Tobago",
42011 "Tunisia (تونس)",
42016 "Turkey (Türkiye)",
42026 "Turks and Caicos Islands",
42036 "U.S. Virgin Islands",
42046 "Ukraine (Україна)",
42051 "United Arab Emirates (الإمارات العربية المتحدة)",
42073 "Uzbekistan (Oʻzbekiston)",
42083 "Vatican City (Città del Vaticano)",
42094 "Vietnam (Việt Nam)",
42099 "Wallis and Futuna (Wallis-et-Futuna)",
42104 "Western Sahara (الصحراء الغربية)",
42110 "Yemen (اليمن)",
42134 * This script refer to:
42135 * Title: International Telephone Input
42136 * Author: Jack O'Connor
42137 * Code version: v12.1.12
42138 * Availability: https://github.com/jackocnr/intl-tel-input.git
42142 * @class Roo.bootstrap.PhoneInput
42143 * @extends Roo.bootstrap.TriggerField
42144 * An input with International dial-code selection
42146 * @cfg {String} defaultDialCode default '+852'
42147 * @cfg {Array} preferedCountries default []
42150 * Create a new PhoneInput.
42151 * @param {Object} config Configuration options
42154 Roo.bootstrap.PhoneInput = function(config) {
42155 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42158 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42160 listWidth: undefined,
42162 selectedClass: 'active',
42164 invalidClass : "has-warning",
42166 validClass: 'has-success',
42168 allowed: '0123456789',
42173 * @cfg {String} defaultDialCode The default dial code when initializing the input
42175 defaultDialCode: '+852',
42178 * @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
42180 preferedCountries: false,
42182 getAutoCreate : function()
42184 var data = Roo.bootstrap.PhoneInputData();
42185 var align = this.labelAlign || this.parentLabelAlign();
42188 this.allCountries = [];
42189 this.dialCodeMapping = [];
42191 for (var i = 0; i < data.length; i++) {
42193 this.allCountries[i] = {
42197 priority: c[3] || 0,
42198 areaCodes: c[4] || null
42200 this.dialCodeMapping[c[2]] = {
42203 priority: c[3] || 0,
42204 areaCodes: c[4] || null
42216 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42217 maxlength: this.max_length,
42218 cls : 'form-control tel-input',
42219 autocomplete: 'new-password'
42222 var hiddenInput = {
42225 cls: 'hidden-tel-input'
42229 hiddenInput.name = this.name;
42232 if (this.disabled) {
42233 input.disabled = true;
42236 var flag_container = {
42253 cls: this.hasFeedback ? 'has-feedback' : '',
42259 cls: 'dial-code-holder',
42266 cls: 'roo-select2-container input-group',
42273 if (this.fieldLabel.length) {
42276 tooltip: 'This field is required'
42282 cls: 'control-label',
42288 html: this.fieldLabel
42291 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42297 if(this.indicatorpos == 'right') {
42298 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42305 if(align == 'left') {
42313 if(this.labelWidth > 12){
42314 label.style = "width: " + this.labelWidth + 'px';
42316 if(this.labelWidth < 13 && this.labelmd == 0){
42317 this.labelmd = this.labelWidth;
42319 if(this.labellg > 0){
42320 label.cls += ' col-lg-' + this.labellg;
42321 input.cls += ' col-lg-' + (12 - this.labellg);
42323 if(this.labelmd > 0){
42324 label.cls += ' col-md-' + this.labelmd;
42325 container.cls += ' col-md-' + (12 - this.labelmd);
42327 if(this.labelsm > 0){
42328 label.cls += ' col-sm-' + this.labelsm;
42329 container.cls += ' col-sm-' + (12 - this.labelsm);
42331 if(this.labelxs > 0){
42332 label.cls += ' col-xs-' + this.labelxs;
42333 container.cls += ' col-xs-' + (12 - this.labelxs);
42343 var settings = this;
42345 ['xs','sm','md','lg'].map(function(size){
42346 if (settings[size]) {
42347 cfg.cls += ' col-' + size + '-' + settings[size];
42351 this.store = new Roo.data.Store({
42352 proxy : new Roo.data.MemoryProxy({}),
42353 reader : new Roo.data.JsonReader({
42364 'name' : 'dialCode',
42368 'name' : 'priority',
42372 'name' : 'areaCodes',
42379 if(!this.preferedCountries) {
42380 this.preferedCountries = [
42387 var p = this.preferedCountries.reverse();
42390 for (var i = 0; i < p.length; i++) {
42391 for (var j = 0; j < this.allCountries.length; j++) {
42392 if(this.allCountries[j].iso2 == p[i]) {
42393 var t = this.allCountries[j];
42394 this.allCountries.splice(j,1);
42395 this.allCountries.unshift(t);
42401 this.store.proxy.data = {
42403 data: this.allCountries
42409 initEvents : function()
42412 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42414 this.indicator = this.indicatorEl();
42415 this.flag = this.flagEl();
42416 this.dialCodeHolder = this.dialCodeHolderEl();
42418 this.trigger = this.el.select('div.flag-box',true).first();
42419 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42424 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42425 _this.list.setWidth(lw);
42428 this.list.on('mouseover', this.onViewOver, this);
42429 this.list.on('mousemove', this.onViewMove, this);
42430 this.inputEl().on("keyup", this.onKeyUp, this);
42431 this.inputEl().on("keypress", this.onKeyPress, this);
42433 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42435 this.view = new Roo.View(this.list, this.tpl, {
42436 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42439 this.view.on('click', this.onViewClick, this);
42440 this.setValue(this.defaultDialCode);
42443 onTriggerClick : function(e)
42445 Roo.log('trigger click');
42450 if(this.isExpanded()){
42452 this.hasFocus = false;
42454 this.store.load({});
42455 this.hasFocus = true;
42460 isExpanded : function()
42462 return this.list.isVisible();
42465 collapse : function()
42467 if(!this.isExpanded()){
42471 Roo.get(document).un('mousedown', this.collapseIf, this);
42472 Roo.get(document).un('mousewheel', this.collapseIf, this);
42473 this.fireEvent('collapse', this);
42477 expand : function()
42481 if(this.isExpanded() || !this.hasFocus){
42485 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42486 this.list.setWidth(lw);
42489 this.restrictHeight();
42491 Roo.get(document).on('mousedown', this.collapseIf, this);
42492 Roo.get(document).on('mousewheel', this.collapseIf, this);
42494 this.fireEvent('expand', this);
42497 restrictHeight : function()
42499 this.list.alignTo(this.inputEl(), this.listAlign);
42500 this.list.alignTo(this.inputEl(), this.listAlign);
42503 onViewOver : function(e, t)
42505 if(this.inKeyMode){
42508 var item = this.view.findItemFromChild(t);
42511 var index = this.view.indexOf(item);
42512 this.select(index, false);
42517 onViewClick : function(view, doFocus, el, e)
42519 var index = this.view.getSelectedIndexes()[0];
42521 var r = this.store.getAt(index);
42524 this.onSelect(r, index);
42526 if(doFocus !== false && !this.blockFocus){
42527 this.inputEl().focus();
42531 onViewMove : function(e, t)
42533 this.inKeyMode = false;
42536 select : function(index, scrollIntoView)
42538 this.selectedIndex = index;
42539 this.view.select(index);
42540 if(scrollIntoView !== false){
42541 var el = this.view.getNode(index);
42543 this.list.scrollChildIntoView(el, false);
42548 createList : function()
42550 this.list = Roo.get(document.body).createChild({
42552 cls: 'typeahead typeahead-long dropdown-menu tel-list',
42553 style: 'display:none'
42556 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
42559 collapseIf : function(e)
42561 var in_combo = e.within(this.el);
42562 var in_list = e.within(this.list);
42563 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
42565 if (in_combo || in_list || is_list) {
42571 onSelect : function(record, index)
42573 if(this.fireEvent('beforeselect', this, record, index) !== false){
42575 this.setFlagClass(record.data.iso2);
42576 this.setDialCode(record.data.dialCode);
42577 this.hasFocus = false;
42579 this.fireEvent('select', this, record, index);
42583 flagEl : function()
42585 var flag = this.el.select('div.flag',true).first();
42592 dialCodeHolderEl : function()
42594 var d = this.el.select('input.dial-code-holder',true).first();
42601 setDialCode : function(v)
42603 this.dialCodeHolder.dom.value = '+'+v;
42606 setFlagClass : function(n)
42608 this.flag.dom.className = 'flag '+n;
42611 getValue : function()
42613 var v = this.inputEl().getValue();
42614 if(this.dialCodeHolder) {
42615 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
42620 setValue : function(v)
42622 var d = this.getDialCode(v);
42624 //invalid dial code
42625 if(v.length == 0 || !d || d.length == 0) {
42627 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
42628 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
42634 this.setFlagClass(this.dialCodeMapping[d].iso2);
42635 this.setDialCode(d);
42636 this.inputEl().dom.value = v.replace('+'+d,'');
42637 this.hiddenEl().dom.value = this.getValue();
42642 getDialCode : function(v)
42646 if (v.length == 0) {
42647 return this.dialCodeHolder.dom.value;
42651 if (v.charAt(0) != "+") {
42654 var numericChars = "";
42655 for (var i = 1; i < v.length; i++) {
42656 var c = v.charAt(i);
42659 if (this.dialCodeMapping[numericChars]) {
42660 dialCode = v.substr(1, i);
42662 if (numericChars.length == 4) {
42672 this.setValue(this.defaultDialCode);
42676 hiddenEl : function()
42678 return this.el.select('input.hidden-tel-input',true).first();
42681 // after setting val
42682 onKeyUp : function(e){
42683 this.setValue(this.getValue());
42686 onKeyPress : function(e){
42687 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
42694 * @class Roo.bootstrap.MoneyField
42695 * @extends Roo.bootstrap.ComboBox
42696 * Bootstrap MoneyField class
42699 * Create a new MoneyField.
42700 * @param {Object} config Configuration options
42703 Roo.bootstrap.MoneyField = function(config) {
42705 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
42709 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
42712 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
42714 allowDecimals : true,
42716 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
42718 decimalSeparator : ".",
42720 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
42722 decimalPrecision : 0,
42724 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
42726 allowNegative : true,
42728 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
42732 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
42734 minValue : Number.NEGATIVE_INFINITY,
42736 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
42738 maxValue : Number.MAX_VALUE,
42740 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
42742 minText : "The minimum value for this field is {0}",
42744 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
42746 maxText : "The maximum value for this field is {0}",
42748 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
42749 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
42751 nanText : "{0} is not a valid number",
42753 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
42757 * @cfg {String} defaults currency of the MoneyField
42758 * value should be in lkey
42760 defaultCurrency : false,
42762 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
42764 thousandsDelimiter : false,
42766 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
42777 getAutoCreate : function()
42779 var align = this.labelAlign || this.parentLabelAlign();
42791 cls : 'form-control roo-money-amount-input',
42792 autocomplete: 'new-password'
42795 var hiddenInput = {
42799 cls: 'hidden-number-input'
42802 if(this.max_length) {
42803 input.maxlength = this.max_length;
42807 hiddenInput.name = this.name;
42810 if (this.disabled) {
42811 input.disabled = true;
42814 var clg = 12 - this.inputlg;
42815 var cmd = 12 - this.inputmd;
42816 var csm = 12 - this.inputsm;
42817 var cxs = 12 - this.inputxs;
42821 cls : 'row roo-money-field',
42825 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
42829 cls: 'roo-select2-container input-group',
42833 cls : 'form-control roo-money-currency-input',
42834 autocomplete: 'new-password',
42836 name : this.currencyName
42840 cls : 'input-group-addon',
42854 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
42858 cls: this.hasFeedback ? 'has-feedback' : '',
42869 if (this.fieldLabel.length) {
42872 tooltip: 'This field is required'
42878 cls: 'control-label',
42884 html: this.fieldLabel
42887 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42893 if(this.indicatorpos == 'right') {
42894 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42901 if(align == 'left') {
42909 if(this.labelWidth > 12){
42910 label.style = "width: " + this.labelWidth + 'px';
42912 if(this.labelWidth < 13 && this.labelmd == 0){
42913 this.labelmd = this.labelWidth;
42915 if(this.labellg > 0){
42916 label.cls += ' col-lg-' + this.labellg;
42917 input.cls += ' col-lg-' + (12 - this.labellg);
42919 if(this.labelmd > 0){
42920 label.cls += ' col-md-' + this.labelmd;
42921 container.cls += ' col-md-' + (12 - this.labelmd);
42923 if(this.labelsm > 0){
42924 label.cls += ' col-sm-' + this.labelsm;
42925 container.cls += ' col-sm-' + (12 - this.labelsm);
42927 if(this.labelxs > 0){
42928 label.cls += ' col-xs-' + this.labelxs;
42929 container.cls += ' col-xs-' + (12 - this.labelxs);
42940 var settings = this;
42942 ['xs','sm','md','lg'].map(function(size){
42943 if (settings[size]) {
42944 cfg.cls += ' col-' + size + '-' + settings[size];
42951 initEvents : function()
42953 this.indicator = this.indicatorEl();
42955 this.initCurrencyEvent();
42957 this.initNumberEvent();
42960 initCurrencyEvent : function()
42963 throw "can not find store for combo";
42966 this.store = Roo.factory(this.store, Roo.data);
42967 this.store.parent = this;
42971 this.triggerEl = this.el.select('.input-group-addon', true).first();
42973 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
42978 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42979 _this.list.setWidth(lw);
42982 this.list.on('mouseover', this.onViewOver, this);
42983 this.list.on('mousemove', this.onViewMove, this);
42984 this.list.on('scroll', this.onViewScroll, this);
42987 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
42990 this.view = new Roo.View(this.list, this.tpl, {
42991 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42994 this.view.on('click', this.onViewClick, this);
42996 this.store.on('beforeload', this.onBeforeLoad, this);
42997 this.store.on('load', this.onLoad, this);
42998 this.store.on('loadexception', this.onLoadException, this);
43000 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43001 "up" : function(e){
43002 this.inKeyMode = true;
43006 "down" : function(e){
43007 if(!this.isExpanded()){
43008 this.onTriggerClick();
43010 this.inKeyMode = true;
43015 "enter" : function(e){
43018 if(this.fireEvent("specialkey", this, e)){
43019 this.onViewClick(false);
43025 "esc" : function(e){
43029 "tab" : function(e){
43032 if(this.fireEvent("specialkey", this, e)){
43033 this.onViewClick(false);
43041 doRelay : function(foo, bar, hname){
43042 if(hname == 'down' || this.scope.isExpanded()){
43043 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43051 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43055 initNumberEvent : function(e)
43057 this.inputEl().on("keydown" , this.fireKey, this);
43058 this.inputEl().on("focus", this.onFocus, this);
43059 this.inputEl().on("blur", this.onBlur, this);
43061 this.inputEl().relayEvent('keyup', this);
43063 if(this.indicator){
43064 this.indicator.addClass('invisible');
43067 this.originalValue = this.getValue();
43069 if(this.validationEvent == 'keyup'){
43070 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43071 this.inputEl().on('keyup', this.filterValidation, this);
43073 else if(this.validationEvent !== false){
43074 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43077 if(this.selectOnFocus){
43078 this.on("focus", this.preFocus, this);
43081 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43082 this.inputEl().on("keypress", this.filterKeys, this);
43084 this.inputEl().relayEvent('keypress', this);
43087 var allowed = "0123456789";
43089 if(this.allowDecimals){
43090 allowed += this.decimalSeparator;
43093 if(this.allowNegative){
43097 if(this.thousandsDelimiter) {
43101 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43103 var keyPress = function(e){
43105 var k = e.getKey();
43107 var c = e.getCharCode();
43110 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43111 allowed.indexOf(String.fromCharCode(c)) === -1
43117 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43121 if(allowed.indexOf(String.fromCharCode(c)) === -1){
43126 this.inputEl().on("keypress", keyPress, this);
43130 onTriggerClick : function(e)
43137 this.loadNext = false;
43139 if(this.isExpanded()){
43144 this.hasFocus = true;
43146 if(this.triggerAction == 'all') {
43147 this.doQuery(this.allQuery, true);
43151 this.doQuery(this.getRawValue());
43154 getCurrency : function()
43156 var v = this.currencyEl().getValue();
43161 restrictHeight : function()
43163 this.list.alignTo(this.currencyEl(), this.listAlign);
43164 this.list.alignTo(this.currencyEl(), this.listAlign);
43167 onViewClick : function(view, doFocus, el, e)
43169 var index = this.view.getSelectedIndexes()[0];
43171 var r = this.store.getAt(index);
43174 this.onSelect(r, index);
43178 onSelect : function(record, index){
43180 if(this.fireEvent('beforeselect', this, record, index) !== false){
43182 this.setFromCurrencyData(index > -1 ? record.data : false);
43186 this.fireEvent('select', this, record, index);
43190 setFromCurrencyData : function(o)
43194 this.lastCurrency = o;
43196 if (this.currencyField) {
43197 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43199 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
43202 this.lastSelectionText = currency;
43204 //setting default currency
43205 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43206 this.setCurrency(this.defaultCurrency);
43210 this.setCurrency(currency);
43213 setFromData : function(o)
43217 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43219 this.setFromCurrencyData(c);
43224 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43226 Roo.log('no value set for '+ (this.name ? this.name : this.id));
43229 this.setValue(value);
43233 setCurrency : function(v)
43235 this.currencyValue = v;
43238 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43243 setValue : function(v)
43245 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43251 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43253 this.inputEl().dom.value = (v == '') ? '' :
43254 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43256 if(!this.allowZero && v === '0') {
43257 this.hiddenEl().dom.value = '';
43258 this.inputEl().dom.value = '';
43265 getRawValue : function()
43267 var v = this.inputEl().getValue();
43272 getValue : function()
43274 return this.fixPrecision(this.parseValue(this.getRawValue()));
43277 parseValue : function(value)
43279 if(this.thousandsDelimiter) {
43281 r = new RegExp(",", "g");
43282 value = value.replace(r, "");
43285 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43286 return isNaN(value) ? '' : value;
43290 fixPrecision : function(value)
43292 if(this.thousandsDelimiter) {
43294 r = new RegExp(",", "g");
43295 value = value.replace(r, "");
43298 var nan = isNaN(value);
43300 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43301 return nan ? '' : value;
43303 return parseFloat(value).toFixed(this.decimalPrecision);
43306 decimalPrecisionFcn : function(v)
43308 return Math.floor(v);
43311 validateValue : function(value)
43313 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43317 var num = this.parseValue(value);
43320 this.markInvalid(String.format(this.nanText, value));
43324 if(num < this.minValue){
43325 this.markInvalid(String.format(this.minText, this.minValue));
43329 if(num > this.maxValue){
43330 this.markInvalid(String.format(this.maxText, this.maxValue));
43337 validate : function()
43339 if(this.disabled || this.allowBlank){
43344 var currency = this.getCurrency();
43346 if(this.validateValue(this.getRawValue()) && currency.length){
43351 this.markInvalid();
43355 getName: function()
43360 beforeBlur : function()
43366 var v = this.parseValue(this.getRawValue());
43373 onBlur : function()
43377 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43378 //this.el.removeClass(this.focusClass);
43381 this.hasFocus = false;
43383 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43387 var v = this.getValue();
43389 if(String(v) !== String(this.startValue)){
43390 this.fireEvent('change', this, v, this.startValue);
43393 this.fireEvent("blur", this);
43396 inputEl : function()
43398 return this.el.select('.roo-money-amount-input', true).first();
43401 currencyEl : function()
43403 return this.el.select('.roo-money-currency-input', true).first();
43406 hiddenEl : function()
43408 return this.el.select('input.hidden-number-input',true).first();
43412 * @class Roo.bootstrap.BezierSignature
43413 * @extends Roo.bootstrap.Component
43414 * Bootstrap BezierSignature class
43415 * This script refer to:
43416 * Title: Signature Pad
43418 * Availability: https://github.com/szimek/signature_pad
43421 * Create a new BezierSignature
43422 * @param {Object} config The config object
43425 Roo.bootstrap.BezierSignature = function(config){
43426 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43432 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43439 mouse_btn_down: true,
43442 * @cfg {int} canvas height
43444 canvas_height: '200px',
43447 * @cfg {float|function} Radius of a single dot.
43452 * @cfg {float} Minimum width of a line. Defaults to 0.5.
43457 * @cfg {float} Maximum width of a line. Defaults to 2.5.
43462 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43467 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43472 * @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.
43474 bg_color: 'rgba(0, 0, 0, 0)',
43477 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43479 dot_color: 'black',
43482 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43484 velocity_filter_weight: 0.7,
43487 * @cfg {function} Callback when stroke begin.
43492 * @cfg {function} Callback when stroke end.
43496 getAutoCreate : function()
43498 var cls = 'roo-signature column';
43501 cls += ' ' + this.cls;
43511 for(var i = 0; i < col_sizes.length; i++) {
43512 if(this[col_sizes[i]]) {
43513 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
43523 cls: 'roo-signature-body',
43527 cls: 'roo-signature-body-canvas',
43528 height: this.canvas_height,
43529 width: this.canvas_width
43536 style: 'display: none'
43544 initEvents: function()
43546 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
43548 var canvas = this.canvasEl();
43550 // mouse && touch event swapping...
43551 canvas.dom.style.touchAction = 'none';
43552 canvas.dom.style.msTouchAction = 'none';
43554 this.mouse_btn_down = false;
43555 canvas.on('mousedown', this._handleMouseDown, this);
43556 canvas.on('mousemove', this._handleMouseMove, this);
43557 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
43559 if (window.PointerEvent) {
43560 canvas.on('pointerdown', this._handleMouseDown, this);
43561 canvas.on('pointermove', this._handleMouseMove, this);
43562 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
43565 if ('ontouchstart' in window) {
43566 canvas.on('touchstart', this._handleTouchStart, this);
43567 canvas.on('touchmove', this._handleTouchMove, this);
43568 canvas.on('touchend', this._handleTouchEnd, this);
43571 Roo.EventManager.onWindowResize(this.resize, this, true);
43573 // file input event
43574 this.fileEl().on('change', this.uploadImage, this);
43581 resize: function(){
43583 var canvas = this.canvasEl().dom;
43584 var ctx = this.canvasElCtx();
43585 var img_data = false;
43587 if(canvas.width > 0) {
43588 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
43590 // setting canvas width will clean img data
43593 var style = window.getComputedStyle ?
43594 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
43596 var padding_left = parseInt(style.paddingLeft) || 0;
43597 var padding_right = parseInt(style.paddingRight) || 0;
43599 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
43602 ctx.putImageData(img_data, 0, 0);
43606 _handleMouseDown: function(e)
43608 if (e.browserEvent.which === 1) {
43609 this.mouse_btn_down = true;
43610 this.strokeBegin(e);
43614 _handleMouseMove: function (e)
43616 if (this.mouse_btn_down) {
43617 this.strokeMoveUpdate(e);
43621 _handleMouseUp: function (e)
43623 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
43624 this.mouse_btn_down = false;
43629 _handleTouchStart: function (e) {
43631 e.preventDefault();
43632 if (e.browserEvent.targetTouches.length === 1) {
43633 // var touch = e.browserEvent.changedTouches[0];
43634 // this.strokeBegin(touch);
43636 this.strokeBegin(e); // assume e catching the correct xy...
43640 _handleTouchMove: function (e) {
43641 e.preventDefault();
43642 // var touch = event.targetTouches[0];
43643 // _this._strokeMoveUpdate(touch);
43644 this.strokeMoveUpdate(e);
43647 _handleTouchEnd: function (e) {
43648 var wasCanvasTouched = e.target === this.canvasEl().dom;
43649 if (wasCanvasTouched) {
43650 e.preventDefault();
43651 // var touch = event.changedTouches[0];
43652 // _this._strokeEnd(touch);
43657 reset: function () {
43658 this._lastPoints = [];
43659 this._lastVelocity = 0;
43660 this._lastWidth = (this.min_width + this.max_width) / 2;
43661 this.canvasElCtx().fillStyle = this.dot_color;
43664 strokeMoveUpdate: function(e)
43666 this.strokeUpdate(e);
43668 if (this.throttle) {
43669 this.throttleStroke(this.strokeUpdate, this.throttle);
43672 this.strokeUpdate(e);
43676 strokeBegin: function(e)
43678 var newPointGroup = {
43679 color: this.dot_color,
43683 if (typeof this.onBegin === 'function') {
43687 this.curve_data.push(newPointGroup);
43689 this.strokeUpdate(e);
43692 strokeUpdate: function(e)
43694 var rect = this.canvasEl().dom.getBoundingClientRect();
43695 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
43696 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
43697 var lastPoints = lastPointGroup.points;
43698 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
43699 var isLastPointTooClose = lastPoint
43700 ? point.distanceTo(lastPoint) <= this.min_distance
43702 var color = lastPointGroup.color;
43703 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
43704 var curve = this.addPoint(point);
43706 this.drawDot({color: color, point: point});
43709 this.drawCurve({color: color, curve: curve});
43719 strokeEnd: function(e)
43721 this.strokeUpdate(e);
43722 if (typeof this.onEnd === 'function') {
43727 addPoint: function (point) {
43728 var _lastPoints = this._lastPoints;
43729 _lastPoints.push(point);
43730 if (_lastPoints.length > 2) {
43731 if (_lastPoints.length === 3) {
43732 _lastPoints.unshift(_lastPoints[0]);
43734 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
43735 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
43736 _lastPoints.shift();
43742 calculateCurveWidths: function (startPoint, endPoint) {
43743 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
43744 (1 - this.velocity_filter_weight) * this._lastVelocity;
43746 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
43749 start: this._lastWidth
43752 this._lastVelocity = velocity;
43753 this._lastWidth = newWidth;
43757 drawDot: function (_a) {
43758 var color = _a.color, point = _a.point;
43759 var ctx = this.canvasElCtx();
43760 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
43762 this.drawCurveSegment(point.x, point.y, width);
43764 ctx.fillStyle = color;
43768 drawCurve: function (_a) {
43769 var color = _a.color, curve = _a.curve;
43770 var ctx = this.canvasElCtx();
43771 var widthDelta = curve.endWidth - curve.startWidth;
43772 var drawSteps = Math.floor(curve.length()) * 2;
43774 ctx.fillStyle = color;
43775 for (var i = 0; i < drawSteps; i += 1) {
43776 var t = i / drawSteps;
43782 var x = uuu * curve.startPoint.x;
43783 x += 3 * uu * t * curve.control1.x;
43784 x += 3 * u * tt * curve.control2.x;
43785 x += ttt * curve.endPoint.x;
43786 var y = uuu * curve.startPoint.y;
43787 y += 3 * uu * t * curve.control1.y;
43788 y += 3 * u * tt * curve.control2.y;
43789 y += ttt * curve.endPoint.y;
43790 var width = curve.startWidth + ttt * widthDelta;
43791 this.drawCurveSegment(x, y, width);
43797 drawCurveSegment: function (x, y, width) {
43798 var ctx = this.canvasElCtx();
43800 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
43801 this.is_empty = false;
43806 var ctx = this.canvasElCtx();
43807 var canvas = this.canvasEl().dom;
43808 ctx.fillStyle = this.bg_color;
43809 ctx.clearRect(0, 0, canvas.width, canvas.height);
43810 ctx.fillRect(0, 0, canvas.width, canvas.height);
43811 this.curve_data = [];
43813 this.is_empty = true;
43818 return this.el.select('input',true).first();
43821 canvasEl: function()
43823 return this.el.select('canvas',true).first();
43826 canvasElCtx: function()
43828 return this.el.select('canvas',true).first().dom.getContext('2d');
43831 getImage: function(type)
43833 if(this.is_empty) {
43838 return this.canvasEl().dom.toDataURL('image/'+type, 1);
43841 drawFromImage: function(img_src)
43843 var img = new Image();
43845 img.onload = function(){
43846 this.canvasElCtx().drawImage(img, 0, 0);
43851 this.is_empty = false;
43854 selectImage: function()
43856 this.fileEl().dom.click();
43859 uploadImage: function(e)
43861 var reader = new FileReader();
43863 reader.onload = function(e){
43864 var img = new Image();
43865 img.onload = function(){
43867 this.canvasElCtx().drawImage(img, 0, 0);
43869 img.src = e.target.result;
43872 reader.readAsDataURL(e.target.files[0]);
43875 // Bezier Point Constructor
43876 Point: (function () {
43877 function Point(x, y, time) {
43880 this.time = time || Date.now();
43882 Point.prototype.distanceTo = function (start) {
43883 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
43885 Point.prototype.equals = function (other) {
43886 return this.x === other.x && this.y === other.y && this.time === other.time;
43888 Point.prototype.velocityFrom = function (start) {
43889 return this.time !== start.time
43890 ? this.distanceTo(start) / (this.time - start.time)
43897 // Bezier Constructor
43898 Bezier: (function () {
43899 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
43900 this.startPoint = startPoint;
43901 this.control2 = control2;
43902 this.control1 = control1;
43903 this.endPoint = endPoint;
43904 this.startWidth = startWidth;
43905 this.endWidth = endWidth;
43907 Bezier.fromPoints = function (points, widths, scope) {
43908 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
43909 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
43910 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
43912 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
43913 var dx1 = s1.x - s2.x;
43914 var dy1 = s1.y - s2.y;
43915 var dx2 = s2.x - s3.x;
43916 var dy2 = s2.y - s3.y;
43917 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
43918 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
43919 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
43920 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
43921 var dxm = m1.x - m2.x;
43922 var dym = m1.y - m2.y;
43923 var k = l2 / (l1 + l2);
43924 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
43925 var tx = s2.x - cm.x;
43926 var ty = s2.y - cm.y;
43928 c1: new scope.Point(m1.x + tx, m1.y + ty),
43929 c2: new scope.Point(m2.x + tx, m2.y + ty)
43932 Bezier.prototype.length = function () {
43937 for (var i = 0; i <= steps; i += 1) {
43939 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
43940 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
43942 var xdiff = cx - px;
43943 var ydiff = cy - py;
43944 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
43951 Bezier.prototype.point = function (t, start, c1, c2, end) {
43952 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
43953 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
43954 + (3.0 * c2 * (1.0 - t) * t * t)
43955 + (end * t * t * t);
43960 throttleStroke: function(fn, wait) {
43961 if (wait === void 0) { wait = 250; }
43963 var timeout = null;
43967 var later = function () {
43968 previous = Date.now();
43970 result = fn.apply(storedContext, storedArgs);
43972 storedContext = null;
43976 return function wrapper() {
43978 for (var _i = 0; _i < arguments.length; _i++) {
43979 args[_i] = arguments[_i];
43981 var now = Date.now();
43982 var remaining = wait - (now - previous);
43983 storedContext = this;
43985 if (remaining <= 0 || remaining > wait) {
43987 clearTimeout(timeout);
43991 result = fn.apply(storedContext, storedArgs);
43993 storedContext = null;
43997 else if (!timeout) {
43998 timeout = window.setTimeout(later, remaining);